[eclipse-jgit] 01/01: Imported Upstream version 4.2.0
Markus Koschany
apo-guest at moszumanska.debian.org
Tue Feb 23 23:12:32 GMT 2016
This is an automated email from the git hooks/post-receive script.
apo-guest pushed a commit to annotated tag upstream/4.2.0
in repository eclipse-jgit.
commit 6932869f6e99f320f48c2f79ec1877327cc7c8d2
Author: Markus Koschany <apo at debian.org>
Date: Tue Feb 23 11:47:25 2016 +0100
Imported Upstream version 4.2.0
---
.buckconfig | 15 +
.buckversion | 1 +
.gitignore | 3 +
BUCK | 45 +
README.md | 22 +-
lib/BUCK | 125 ++
lib/jetty/BUCK | 56 +
org.eclipse.jgit.ant.test/.classpath | 2 +-
.../.settings/org.eclipse.jdt.core.prefs | 6 +-
org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF | 14 +-
org.eclipse.jgit.ant.test/build.properties | 2 +-
org.eclipse.jgit.ant.test/pom.xml | 2 +-
org.eclipse.jgit.ant/.classpath | 2 +-
.../.settings/org.eclipse.jdt.core.prefs | 6 +-
org.eclipse.jgit.ant/META-INF/MANIFEST.MF | 8 +-
org.eclipse.jgit.ant/pom.xml | 111 +-
.../src/org/eclipse/jgit/ant/tasks/GitAddTask.java | 8 +-
.../eclipse/jgit/ant/tasks/GitCheckoutTask.java | 8 +-
org.eclipse.jgit.archive/.classpath | 2 +-
.../.settings/org.eclipse.jdt.core.prefs | 20 +-
org.eclipse.jgit.archive/BUCK | 13 +
org.eclipse.jgit.archive/META-INF/MANIFEST.MF | 13 +-
.../META-INF/SOURCE-MANIFEST.MF | 4 +-
org.eclipse.jgit.archive/pom.xml | 89 +-
.../jgit/archive/internal/ArchiveText.properties | 1 +
.../src/org/eclipse/jgit/archive/BaseFormat.java | 42 +-
.../src/org/eclipse/jgit/archive/TarFormat.java | 20 +-
.../src/org/eclipse/jgit/archive/Tbz2Format.java | 15 +-
.../src/org/eclipse/jgit/archive/TgzFormat.java | 15 +-
.../src/org/eclipse/jgit/archive/TxzFormat.java | 15 +-
.../src/org/eclipse/jgit/archive/ZipFormat.java | 18 +-
.../eclipse/jgit/archive/internal/ArchiveText.java | 1 +
org.eclipse.jgit.console/.classpath | 8 -
org.eclipse.jgit.console/.gitignore | 2 -
org.eclipse.jgit.console/.project | 34 -
.../.settings/org.eclipse.core.resources.prefs | 3 -
.../.settings/org.eclipse.core.runtime.prefs | 3 -
.../.settings/org.eclipse.jdt.core.prefs | 398 ------
.../.settings/org.eclipse.jdt.ui.prefs | 61 -
.../.settings/org.eclipse.mylyn.tasks.ui.prefs | 4 -
.../.settings/org.eclipse.mylyn.team.ui.prefs | 3 -
.../.settings/org.eclipse.pde.api.tools.prefs | 94 --
.../.settings/org.eclipse.pde.core.prefs | 3 -
org.eclipse.jgit.console/META-INF/MANIFEST.MF | 13 -
org.eclipse.jgit.console/about.html | 59 -
org.eclipse.jgit.console/build.properties | 7 -
org.eclipse.jgit.console/plugin.properties | 2 -
org.eclipse.jgit.console/pom.xml | 132 --
.../eclipse/jgit/console/ConsoleText.properties | 5 -
org.eclipse.jgit.http.apache/.classpath | 2 +-
.../.settings/org.eclipse.jdt.core.prefs | 6 +-
org.eclipse.jgit.http.apache/BUCK | 12 +
org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF | 19 +-
org.eclipse.jgit.http.apache/pom.xml | 111 +-
.../http/apache/HttpClientConnection.java | 27 +-
org.eclipse.jgit.http.server/.classpath | 2 +-
.../.settings/org.eclipse.jdt.core.prefs | 6 +-
org.eclipse.jgit.http.server/BUCK | 10 +
org.eclipse.jgit.http.server/META-INF/MANIFEST.MF | 31 +-
org.eclipse.jgit.http.server/pom.xml | 90 +-
.../jgit/http/server/HttpServerText.properties | 5 +-
.../eclipse/jgit/http/server/AsIsFileFilter.java | 8 +-
.../eclipse/jgit/http/server/HttpServerText.java | 1 +
.../jgit/http/server/ReceivePackServlet.java | 37 +-
.../eclipse/jgit/http/server/RepositoryFilter.java | 4 +-
.../org/eclipse/jgit/http/server/ServletUtils.java | 10 +
.../jgit/http/server/SmartServiceInfoRefs.java | 10 +-
.../jgit/http/server/UploadPackServlet.java | 26 +-
org.eclipse.jgit.http.test/.classpath | 2 +-
.../.settings/org.eclipse.jdt.core.prefs | 6 +-
org.eclipse.jgit.http.test/BUCK | 40 +
org.eclipse.jgit.http.test/META-INF/MANIFEST.MF | 70 +-
org.eclipse.jgit.http.test/pom.xml | 6 +-
.../eclipse/jgit/http/test/AsIsServiceTest.java | 2 +-
.../http/test/DefaultReceivePackFactoryTest.java | 2 +-
.../http/test/DefaultUploadPackFactoryTest.java | 2 +-
.../jgit/http/test/DumbClientDumbServerTest.java | 29 +-
.../jgit/http/test/DumbClientSmartServerTest.java | 10 +-
.../jgit/http/test/GitServletResponseTests.java | 297 ++++
.../eclipse/jgit/http/test/HookMessageTest.java | 4 +-
.../eclipse/jgit/http/test/HttpClientTests.java | 125 +-
.../jgit/http/test/SmartClientSmartServerTest.java | 88 +-
org.eclipse.jgit.java7.test/.classpath | 10 -
org.eclipse.jgit.java7.test/.gitignore | 2 -
org.eclipse.jgit.java7.test/.project | 34 -
.../.settings/org.eclipse.core.resources.prefs | 3 -
.../.settings/org.eclipse.core.runtime.prefs | 3 -
.../.settings/org.eclipse.jdt.core.prefs | 398 ------
.../.settings/org.eclipse.jdt.ui.prefs | 61 -
.../.settings/org.eclipse.mylyn.tasks.ui.prefs | 4 -
.../.settings/org.eclipse.mylyn.team.ui.prefs | 3 -
.../.settings/org.eclipse.pde.api.tools.prefs | 94 --
.../.settings/org.eclipse.pde.core.prefs | 3 -
org.eclipse.jgit.java7.test/META-INF/MANIFEST.MF | 19 -
org.eclipse.jgit.java7.test/build.properties | 3 -
...git.java7 -- Java7 feature test (Java 8).launch | 21 -
...eclipse.jgit.java7 -- Java7 feature test.launch | 20 -
org.eclipse.jgit.java7.test/plugin.properties | 2 -
org.eclipse.jgit.java7.test/pom.xml | 126 --
org.eclipse.jgit.java7/.classpath | 7 -
org.eclipse.jgit.java7/.fbprefs | 125 --
org.eclipse.jgit.java7/.gitignore | 2 -
org.eclipse.jgit.java7/.project | 34 -
.../.settings/org.eclipse.core.resources.prefs | 3 -
.../.settings/org.eclipse.core.runtime.prefs | 3 -
.../.settings/org.eclipse.jdt.core.prefs | 398 ------
.../.settings/org.eclipse.jdt.ui.prefs | 61 -
.../.settings/org.eclipse.mylyn.tasks.ui.prefs | 4 -
.../.settings/org.eclipse.mylyn.team.ui.prefs | 3 -
.../.settings/org.eclipse.pde.api.tools.prefs | 94 --
.../.settings/org.eclipse.pde.core.prefs | 3 -
org.eclipse.jgit.java7/META-INF/MANIFEST.MF | 10 -
org.eclipse.jgit.java7/META-INF/SOURCE-MANIFEST.MF | 8 -
org.eclipse.jgit.java7/about.html | 59 -
org.eclipse.jgit.java7/build.properties | 7 -
org.eclipse.jgit.java7/plugin.properties | 2 -
org.eclipse.jgit.java7/pom.xml | 196 ---
.../src/org/eclipse/jgit/util/FS_POSIX_Java7.java | 363 -----
.../src/org/eclipse/jgit/util/FS_Win32_Java7.java | 162 ---
.../eclipse/jgit/util/FS_Win32_Java7Cygwin.java | 156 --
.../src/org/eclipse/jgit/util/FileUtil.java | 253 ----
.../src/org/eclipse/jgit/util/Java7FSFactory.java | 66 -
org.eclipse.jgit.junit.http/.classpath | 2 +-
.../.settings/org.eclipse.jdt.core.prefs | 6 +-
org.eclipse.jgit.junit.http/BUCK | 18 +
org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF | 56 +-
org.eclipse.jgit.junit.http/pom.xml | 2 +-
.../src/org/eclipse/jgit/junit/http/AppServer.java | 20 +-
.../eclipse/jgit/junit/http/RecordingLogger.java | 5 +
.../eclipse/jgit/junit/http/TestRequestLog.java | 2 +-
org.eclipse.jgit.junit/.classpath | 2 +-
.../.settings/org.eclipse.jdt.core.prefs | 6 +-
org.eclipse.jgit.junit/BUCK | 10 +
org.eclipse.jgit.junit/META-INF/MANIFEST.MF | 41 +-
org.eclipse.jgit.junit/pom.xml | 2 +-
.../src/org/eclipse/jgit/junit/JGitTestUtil.java | 18 +
.../jgit/junit/LocalDiskRepositoryTestCase.java | 122 +-
.../org/eclipse/jgit/junit/MockSystemReader.java | 36 +-
.../org/eclipse/jgit/junit/RepositoryTestCase.java | 183 +--
.../src/org/eclipse/jgit/junit/TestRepository.java | 508 +++++--
.../org.eclipse.jgit.feature/feature.xml | 8 +-
.../org.eclipse.jgit.feature/pom.xml | 7 +-
.../feature.xml | 2 +-
.../org.eclipse.jgit.http.apache.feature/pom.xml | 2 +-
.../org.eclipse.jgit.java7.feature/.gitignore | 1 -
.../org.eclipse.jgit.java7.feature/.project | 17 -
.../.settings/org.eclipse.core.resources.prefs | 3 -
.../.settings/org.eclipse.core.runtime.prefs | 3 -
.../.settings/org.eclipse.mylyn.tasks.ui.prefs | 4 -
.../.settings/org.eclipse.mylyn.team.ui.prefs | 3 -
.../build.properties | 4 -
.../org.eclipse.jgit.java7.feature/edl-v10.html | 59 -
.../feature.properties | 159 ---
.../org.eclipse.jgit.java7.feature/feature.xml | 33 -
.../org.eclipse.jgit.java7.feature/license.html | 106 --
.../org.eclipse.jgit.java7.feature/pom.xml | 77 -
.../org.eclipse.jgit.junit.feature/feature.xml | 2 +-
.../org.eclipse.jgit.junit.feature/pom.xml | 2 +-
.../org.eclipse.jgit.pgm.feature/feature.xml | 4 +-
.../org.eclipse.jgit.pgm.feature/pom.xml | 8 +-
.../feature.xml | 2 +-
.../org.eclipse.jgit.pgm.source.feature/pom.xml | 2 +-
.../org.eclipse.jgit.repository/category.xml | 3 -
.../org.eclipse.jgit.repository/pom.xml | 7 +-
.../org.eclipse.jgit.source.feature/feature.xml | 8 +-
.../org.eclipse.jgit.source.feature/pom.xml | 2 +-
.../org.eclipse.jgit.target/.classpath | 2 +-
.../org.eclipse.jgit.target/META-INF/MANIFEST.MF | 2 +-
.../org.eclipse.jgit.target/jgit-4.3.target | 48 +-
.../org.eclipse.jgit.target/jgit-4.3.tpd | 2 +-
.../org.eclipse.jgit.target/jgit-4.4.target | 48 +-
.../org.eclipse.jgit.target/jgit-4.4.tpd | 2 +-
.../org.eclipse.jgit.target/jgit-4.5.target | 62 +-
.../org.eclipse.jgit.target/jgit-4.5.tpd | 4 +-
.../orbit/R20150124073747-Luna-SR2.tpd | 12 +-
...203538-Mars-M5.tpd => R20150821153341-Mars.tpd} | 28 +-
.../org.eclipse.jgit.target/pom.xml | 2 +-
.../projects/jetty-7.6.14.tpd | 20 -
.../projects/jetty-9.2.13.tpd | 20 +
org.eclipse.jgit.packaging/pom.xml | 21 +-
org.eclipse.jgit.pgm.test/.classpath | 2 +-
.../.settings/org.eclipse.jdt.core.prefs | 6 +-
org.eclipse.jgit.pgm.test/BUCK | 38 +
org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF | 37 +-
.../org.eclipse.jgit.pgm--All-Tests (Java7).launch | 6 -
...clipse.jgit.pgm--All-Tests (Java8) (de).launch} | 4 +-
.../org.eclipse.jgit.pgm--All-Tests (Java8).launch | 1 -
org.eclipse.jgit.pgm.test/pom.xml | 19 +-
.../eclipse/jgit/lib/CLIRepositoryTestCase.java | 91 +-
.../src/org/eclipse/jgit/pgm/CLIGitCommand.java | 177 ++-
.../tst/org/eclipse/jgit/pgm/AddTest.java | 15 +-
.../tst/org/eclipse/jgit/pgm/ArchiveTest.java | 209 ++-
.../tst/org/eclipse/jgit/pgm/BranchTest.java | 206 ++-
.../tst/org/eclipse/jgit/pgm/CheckoutTest.java | 641 +++++----
.../jgit/pgm/{BranchTest.java => CommitTest.java} | 80 +-
.../tst/org/eclipse/jgit/pgm/ConfigTest.java | 4 +-
.../tst/org/eclipse/jgit/pgm/DescribeTest.java | 41 +-
.../tst/org/eclipse/jgit/pgm/MergeTest.java | 10 +-
.../tst/org/eclipse/jgit/pgm/RemoteTest.java | 159 +++
.../tst/org/eclipse/jgit/pgm/RepoTest.java | 28 +
.../tst/org/eclipse/jgit/pgm/ResetTest.java | 162 +++
.../tst/org/eclipse/jgit/pgm/StatusTest.java | 647 ++++++---
.../tst/org/eclipse/jgit/pgm/TagTest.java | 2 +-
org.eclipse.jgit.pgm/.classpath | 2 +-
.../.settings/org.eclipse.jdt.core.prefs | 20 +-
org.eclipse.jgit.pgm/BUCK | 70 +
org.eclipse.jgit.pgm/META-INF/MANIFEST.MF | 76 +-
org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF | 4 +-
.../services/org.eclipse.jgit.pgm.TextBuiltin | 2 +
org.eclipse.jgit.pgm/pom.xml | 66 +-
org.eclipse.jgit.pgm/resources/log4j.properties | 6 +
.../eclipse/jgit/pgm/internal/CLIText.properties | 23 +-
.../eclipse/jgit/console/ConsoleAuthenticator.java | 16 +-
.../jgit/console/ConsoleCredentialsProvider.java | 14 +-
.../org/eclipse/jgit/pgm/AbstractFetchCommand.java | 21 +-
.../src/org/eclipse/jgit/pgm/Add.java | 12 +-
.../src/org/eclipse/jgit/pgm/Archive.java | 4 +-
.../src/org/eclipse/jgit/pgm/Blame.java | 8 +-
.../src/org/eclipse/jgit/pgm/Branch.java | 187 ++-
.../src/org/eclipse/jgit/pgm/Checkout.java | 85 +-
.../src/org/eclipse/jgit/pgm/Clone.java | 21 +-
.../src/org/eclipse/jgit/pgm/Commit.java | 65 +-
.../src/org/eclipse/jgit/pgm/Config.java | 2 +-
.../src/org/eclipse/jgit/pgm/Describe.java | 32 +-
.../src/org/eclipse/jgit/pgm/Die.java | 15 +
.../src/org/eclipse/jgit/pgm/Diff.java | 7 +-
.../src/org/eclipse/jgit/pgm/DiffTree.java | 73 +-
.../src/org/eclipse/jgit/pgm/Fetch.java | 51 +-
.../src/org/eclipse/jgit/pgm/IndexPack.java | 5 +-
.../src/org/eclipse/jgit/pgm/Log.java | 5 +-
.../src/org/eclipse/jgit/pgm/LsTree.java | 37 +-
.../src/org/eclipse/jgit/pgm/Main.java | 197 ++-
.../src/org/eclipse/jgit/pgm/Merge.java | 63 +-
.../src/org/eclipse/jgit/pgm/Push.java | 64 +-
.../src/org/eclipse/jgit/pgm/Reflog.java | 16 +-
.../src/org/eclipse/jgit/pgm/Remote.java | 196 +++
.../src/org/eclipse/jgit/pgm/Repo.java | 6 +-
.../src/org/eclipse/jgit/pgm/Reset.java | 45 +-
.../src/org/eclipse/jgit/pgm/RevParse.java | 28 +-
.../src/org/eclipse/jgit/pgm/Rm.java | 11 +-
.../src/org/eclipse/jgit/pgm/Show.java | 28 +-
.../src/org/eclipse/jgit/pgm/Status.java | 51 +-
.../src/org/eclipse/jgit/pgm/Tag.java | 40 +-
.../src/org/eclipse/jgit/pgm/TextBuiltin.java | 93 +-
.../org/eclipse/jgit/pgm/debug/DiffAlgorithms.java | 34 +-
.../eclipse/jgit/pgm/debug/RebuildCommitGraph.java | 100 +-
.../org/eclipse/jgit/pgm/debug/RebuildRefTree.java | 145 ++
.../org/eclipse/jgit/pgm/debug/ShowPackDelta.java | 9 +-
.../eclipse/jgit/pgm/debug/TextHashFunctions.java | 24 +-
.../src/org/eclipse/jgit/pgm/internal/CLIText.java | 23 +
.../jgit/pgm/opt/AbstractTreeIteratorHandler.java | 17 +-
.../org/eclipse/jgit/pgm/opt/CmdLineParser.java | 163 ++-
.../org/eclipse/jgit/pgm/opt/ObjectIdHandler.java | 4 +-
.../jgit/pgm/opt/OptionWithValuesListHandler.java | 52 +
.../org/eclipse/jgit/pgm/opt/RevCommitHandler.java | 16 +-
.../org/eclipse/jgit/pgm/opt/RevTreeHandler.java | 10 +-
.../eclipse/jgit/pgm/opt/SubcommandHandler.java | 5 +-
...ctIdHandler.java => UntrackedFilesHandler.java} | 87 +-
org.eclipse.jgit.test/.classpath | 3 +-
.../.settings/org.eclipse.jdt.core.prefs | 6 +-
org.eclipse.jgit.test/BUCK | 95 ++
org.eclipse.jgit.test/META-INF/MANIFEST.MF | 85 +-
org.eclipse.jgit.test/build.properties | 1 +
.../ignore/CGitVsJGitRandomIgnorePatternTest.java | 275 ++++
...rg.eclipse.jgit.core--All-Tests (Java 7).launch | 1 -
...ipse.jgit.core--All-Tests (Java 8) (de).launch} | 4 +-
...rg.eclipse.jgit.core--All-Tests (Java 8).launch | 1 -
...lipse.jgit.test-WalkEncryptionTest-Proxy.launch | 20 +
...org.eclipse.jgit.test-WalkEncryptionTest.launch | 20 +
org.eclipse.jgit.test/pom.xml | 53 +-
.../api => src/org/eclipse/jgit/lib}/Sets.java | 6 +-
.../tst-rsrc/jgit-s3-config.disabled.properties | 48 +
.../tst-rsrc/jgit-s3-config.policy.bucket.json | 20 +
.../tst-rsrc/jgit-s3-config.policy.user.json | 24 +
.../tst-rsrc/jgit-s3-connection-v-0.properties | 11 +
.../tst-rsrc/jgit-s3-connection-v-1.properties | 14 +
.../tst-rsrc/jgit-s3-connection-v-2.properties | 48 +
org.eclipse.jgit.test/tst-rsrc/log4j.properties | 9 +
.../jgit/api/AbstractRemoteCommandTest.java | 89 +-
.../tst/org/eclipse/jgit/api/AddCommandTest.java | 743 +++++++---
.../org/eclipse/jgit/api/ArchiveCommandTest.java | 197 +--
.../tst/org/eclipse/jgit/api/BlameCommandTest.java | 608 ++++----
.../org/eclipse/jgit/api/BranchCommandTest.java | 66 +-
.../org/eclipse/jgit/api/CheckoutCommandTest.java | 165 ++-
.../eclipse/jgit/api/CherryPickCommandTest.java | 388 ++---
.../tst/org/eclipse/jgit/api/CloneCommandTest.java | 95 +-
.../eclipse/jgit/api/CommitAndLogCommandTest.java | 510 +++----
.../org/eclipse/jgit/api/CommitCommandTest.java | 58 +-
.../org/eclipse/jgit/api/DescribeCommandTest.java | 33 +-
.../tst/org/eclipse/jgit/api/DiffCommandTest.java | 5 +-
.../org/eclipse/jgit/api/GitConstructionTest.java | 3 +-
.../tst/org/eclipse/jgit/api/MergeCommandTest.java | 55 +-
.../org/eclipse/jgit/api/NameRevCommandTest.java | 4 +-
.../eclipse/jgit/api/PathCheckoutCommandTest.java | 70 +-
.../tst/org/eclipse/jgit/api/PullCommandTest.java | 47 +-
.../jgit/api/PullCommandWithRebaseTest.java | 4 +-
.../tst/org/eclipse/jgit/api/PushCommandTest.java | 45 +
.../org/eclipse/jgit/api/RebaseCommandTest.java | 41 +-
.../org/eclipse/jgit/api/RemoteAddCommandTest.java | 52 +-
.../RemoteDeleteCommandTest.java} | 42 +-
.../RemoteListCommandTest.java} | 42 +-
.../eclipse/jgit/api/RemoteSetUrlCommandTest.java | 95 +-
.../tst/org/eclipse/jgit/api/ResetCommandTest.java | 74 +-
.../eclipse/jgit/api/StashApplyCommandTest.java | 2 +-
.../eclipse/jgit/api/StashCreateCommandTest.java | 33 +-
.../org/eclipse/jgit/api/StashDropCommandTest.java | 102 +-
.../org/eclipse/jgit/api/StatusCommandTest.java | 1 +
.../tst/org/eclipse/jgit/api/TagCommandTest.java | 216 +--
.../eclipse/jgit/api/blame/BlameGeneratorTest.java | 21 +-
.../jgit/attributes/AttributesMatcherTest.java | 3 -
.../AttributesNodeDirCacheIteratorTest.java | 20 +-
...ributeNodeTest.java => AttributesNodeTest.java} | 29 +-
.../AttributesNodeWorkingTreeIteratorTest.java | 58 +-
.../jgit/attributes/TreeWalkAttributeTest.java | 866 ++++++++++++
.../org/eclipse/jgit/diff/DiffFormatterTest.java | 170 +--
.../org/eclipse/jgit/diff/RenameDetectorTest.java | 31 +
.../eclipse/jgit/dircache/DirCacheEntryTest.java | 7 +-
.../jgit/dircache/DirCachePathEditTest.java | 121 ++
.../eclipse/jgit/gitrepo/ManifestParserTest.java | 112 ++
.../org/eclipse/jgit/gitrepo/RepoCommandTest.java | 138 +-
.../eclipse/jgit/ignore/FastIgnoreRuleTest.java | 157 +--
.../jgit/ignore/IgnoreMatcherParametrizedTest.java | 55 +-
.../org/eclipse/jgit/ignore/IgnoreMatcherTest.java | 400 ------
.../org/eclipse/jgit/ignore/IgnoreNodeTest.java | 140 +-
.../jgit/ignore/IgnoreRuleSpecialCasesTest.java | 246 +++-
.../internal/storage/dfs/DeltaBaseCacheTest.java | 152 ++
.../jgit/internal/storage/dfs/DfsInserterTest.java | 64 +-
.../internal/storage/file/AbbreviationTest.java | 5 +-
.../storage/file/ConcurrentRepackTest.java | 37 +-
.../storage/file/FileRepositoryBuilderTest.java | 5 +-
.../internal/storage/file/GcBasicPackingTest.java | 52 +-
.../jgit/internal/storage/file/GcPackRefsTest.java | 38 +-
.../jgit/internal/storage/file/GcReflogTest.java | 1 +
.../jgit/internal/storage/file/GcTestCase.java | 4 +-
.../jgit/internal/storage/file/PackFileTest.java | 7 +-
.../jgit/internal/storage/file/PackWriterTest.java | 114 +-
.../internal/storage/file/RefDirectoryTest.java | 108 ++
.../jgit/internal/storage/file/RefUpdateTest.java | 33 +-
.../internal/storage/file/T0003_BasicTest.java | 101 +-
.../internal/storage/file/UnpackedObjectTest.java | 2 +-
.../storage/pack/GcCommitSelectionTest.java | 271 ++++
.../storage/pack/PackWriterBitmapPreparerTest.java | 136 ++
.../storage/reftree/RefTreeDatabaseTest.java | 685 +++++++++
.../jgit/internal/storage/reftree/RefTreeTest.java | 303 ++++
.../org/eclipse/jgit/junit/TestRepositoryTest.java | 384 +++++
.../tst/org/eclipse/jgit/lib/ConfigTest.java | 84 +-
.../org/eclipse/jgit/lib/DirCacheCheckoutTest.java | 432 +++++-
.../jgit/lib/DirCacheCheckoutTestWithSymlinks.java | 0
.../eclipse/jgit/lib/IndexDiffSubmoduleTest.java | 2 +-
.../tst/org/eclipse/jgit/lib/IndexDiffTest.java | 96 +-
.../org/eclipse/jgit/lib/ObjectCheckerTest.java | 1489 ++++++++------------
.../tst/org/eclipse/jgit/lib/ObjectIdTest.java | 17 +
.../tst/org/eclipse/jgit/lib/RefTest.java | 68 +-
.../tst/org/eclipse/jgit/lib/ReflogConfigTest.java | 5 +-
.../org/eclipse/jgit/lib/RepositoryCacheTest.java | 26 +
.../eclipse/jgit/lib/T0001_PersonIdentTest.java | 2 +
.../tst/org/eclipse/jgit/lib/T0002_TreeTest.java | 319 -----
.../org/eclipse/jgit/merge/MergeAlgorithmTest.java | 60 +-
.../jgit/merge/MergeMessageFormatterTest.java | 48 +-
.../eclipse/jgit/merge/RecursiveMergerTest.java | 76 +-
.../org/eclipse/jgit/merge/ResolveMergerTest.java | 82 +-
.../jgit/merge/SquashMessageFormatterTest.java | 2 +-
.../tst/org/eclipse/jgit/nls/RootLocaleTest.java | 6 -
.../eclipse/jgit/notes/DefaultNoteMergerTest.java | 4 +-
.../org/eclipse/jgit/notes/NoteMapMergerTest.java | 4 +-
.../tst/org/eclipse/jgit/notes/NoteMapTest.java | 4 +-
.../org/eclipse/jgit/revwalk/FooterLineTest.java | 10 +-
.../eclipse/jgit/revwalk/ObjectWalkFilterTest.java | 186 +++
.../org/eclipse/jgit/revwalk/ObjectWalkTest.java | 48 +-
.../eclipse/jgit/revwalk/RevCommitParseTest.java | 85 ++
.../org/eclipse/jgit/revwalk/RevTagParseTest.java | 39 +
.../eclipse/jgit/revwalk/RevWalkFilterTest.java | 4 +-
.../org/eclipse/jgit/revwalk/RevWalkTestCase.java | 4 +-
.../jgit/revwalk/RevWalkUtilsReachableTest.java | 5 +-
.../eclipse/jgit/submodule/SubmoduleInitTest.java | 5 +-
.../eclipse/jgit/submodule/SubmoduleWalkTest.java | 8 +
.../org/eclipse/jgit/symlinks/SymlinksTest.java | 0
.../org/eclipse/jgit/transport/AtomicPushTest.java | 191 +++
.../jgit/transport/BaseReceivePackTest.java | 73 +-
.../eclipse/jgit/transport/BundleWriterTest.java | 75 +-
.../jgit/transport/HMACSHA1NonceGeneratorTest.java | 131 ++
.../org/eclipse/jgit/transport/PackParserTest.java | 5 +-
.../jgit/transport/PushCertificateIdentTest.java | 196 +++
.../jgit/transport/PushCertificateParserTest.java | 388 +++++
.../jgit/transport/PushCertificateStoreTest.java | 380 +++++
.../eclipse/jgit/transport/PushConnectionTest.java | 143 ++
.../ReceivePackAdvertiseRefsHookTest.java | 23 +-
.../org/eclipse/jgit/transport/RefSpecTest.java | 50 +-
.../jgit/transport/SideBandOutputStreamTest.java | 25 +-
.../eclipse/jgit/transport/TestProtocolTest.java | 248 ++++
.../org/eclipse/jgit/transport/TransportTest.java | 73 +-
.../tst/org/eclipse/jgit/transport/URIishTest.java | 163 ++-
.../eclipse/jgit/transport/WalkEncryptionTest.java | 1318 +++++++++++++++++
.../jgit/treewalk/CanonicalTreeParserTest.java | 41 +
.../jgit/treewalk/FileTreeIteratorJava7Test.java | 155 +-
.../jgit/treewalk/FileTreeIteratorTest.java | 30 +-
.../tst/org/eclipse/jgit/treewalk/ForPathTest.java | 119 +-
.../jgit/treewalk/TreeWalkBasicDiffTest.java | 116 +-
.../eclipse/jgit/treewalk/TreeWalkJava7Test.java | 1 +
.../jgit/treewalk/filter/PathFilterGroupTest.java | 150 +-
.../org/eclipse/jgit/util/ChangeIdUtilTest.java | 3 +-
.../tst}/org/eclipse/jgit/util/FSJava7Test.java | 62 +-
.../tst/org/eclipse/jgit/util/FileUtilTest.java | 26 +-
.../tst}/org/eclipse/jgit/util/FileUtils7Test.java | 13 +
.../tst}/org/eclipse/jgit/util/HookTest.java | 94 +-
.../tst/org/eclipse/jgit/util/IOReadLineTest.java | 92 +-
.../tst/org/eclipse/jgit/util/PathsTest.java | 118 ++
.../eclipse/jgit/util/RunExternalScriptTest.java | 212 +++
.../org/eclipse/jgit/util/TemporaryBufferTest.java | 25 +
org.eclipse.jgit.ui/.classpath | 2 +-
.../.settings/org.eclipse.jdt.core.prefs | 6 +-
org.eclipse.jgit.ui/BUCK | 7 +
org.eclipse.jgit.ui/META-INF/MANIFEST.MF | 20 +-
org.eclipse.jgit.ui/pom.xml | 2 +-
.../eclipse/jgit/awtui/AwtCredentialsProvider.java | 7 +-
.../org/eclipse/jgit/awtui/SwingCommitList.java | 1 +
org.eclipse.jgit/.classpath | 2 +-
org.eclipse.jgit/.settings/.api_filters | 114 +-
.../.settings/org.eclipse.jdt.core.prefs | 20 +-
org.eclipse.jgit/BUCK | 20 +
org.eclipse.jgit/META-INF/MANIFEST.MF | 100 +-
org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF | 4 +-
org.eclipse.jgit/pom.xml | 88 +-
.../org/eclipse/jgit/internal/JGitText.properties | 108 +-
.../NonNull.java} | 64 +-
.../src/org/eclipse/jgit/annotations/Nullable.java | 100 ++
.../src/org/eclipse/jgit/api/AddCommand.java | 141 +-
.../src/org/eclipse/jgit/api/AddNoteCommand.java | 10 +-
.../src/org/eclipse/jgit/api/ApplyCommand.java | 9 +-
.../src/org/eclipse/jgit/api/ArchiveCommand.java | 54 +-
.../src/org/eclipse/jgit/api/BlameCommand.java | 5 +-
.../src/org/eclipse/jgit/api/CheckoutCommand.java | 78 +-
.../org/eclipse/jgit/api/CherryPickCommand.java | 7 +-
.../src/org/eclipse/jgit/api/CherryPickResult.java | 6 +-
.../src/org/eclipse/jgit/api/CloneCommand.java | 53 +-
.../src/org/eclipse/jgit/api/CommitCommand.java | 439 +++---
.../org/eclipse/jgit/api/CreateBranchCommand.java | 5 +-
.../org/eclipse/jgit/api/DeleteBranchCommand.java | 25 +-
.../src/org/eclipse/jgit/api/DescribeCommand.java | 43 +-
.../src/org/eclipse/jgit/api/DiffCommand.java | 12 +-
.../src/org/eclipse/jgit/api/FetchCommand.java | 30 +-
org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java | 105 +-
.../org/eclipse/jgit/api/ListBranchCommand.java | 5 +-
.../src/org/eclipse/jgit/api/ListNotesCommand.java | 5 +-
.../src/org/eclipse/jgit/api/ListTagCommand.java | 5 +-
.../src/org/eclipse/jgit/api/LsRemoteCommand.java | 42 +-
.../src/org/eclipse/jgit/api/MergeCommand.java | 35 +-
.../src/org/eclipse/jgit/api/MergeResult.java | 24 +-
.../src/org/eclipse/jgit/api/NameRevCommand.java | 4 +-
.../src/org/eclipse/jgit/api/PullCommand.java | 27 +-
.../src/org/eclipse/jgit/api/PushCommand.java | 34 +-
.../src/org/eclipse/jgit/api/RebaseCommand.java | 299 ++--
...{ShowNoteCommand.java => RemoteAddCommand.java} | 105 +-
...{ListTagCommand.java => RemoteListCommand.java} | 56 +-
...istTagCommand.java => RemoteRemoveCommand.java} | 75 +-
.../org/eclipse/jgit/api/RemoteSetUrlCommand.java | 155 ++
.../org/eclipse/jgit/api/RemoveNoteCommand.java | 14 +-
.../org/eclipse/jgit/api/RenameBranchCommand.java | 5 +
.../src/org/eclipse/jgit/api/ResetCommand.java | 34 +-
.../src/org/eclipse/jgit/api/RevertCommand.java | 13 +-
.../src/org/eclipse/jgit/api/RmCommand.java | 3 +-
.../src/org/eclipse/jgit/api/ShowNoteCommand.java | 5 +-
.../org/eclipse/jgit/api/StashApplyCommand.java | 29 +-
.../org/eclipse/jgit/api/StashCreateCommand.java | 24 +-
.../src/org/eclipse/jgit/api/StashDropCommand.java | 16 +-
.../src/org/eclipse/jgit/api/StashListCommand.java | 9 +-
.../org/eclipse/jgit/api/SubmoduleAddCommand.java | 2 +
.../eclipse/jgit/api/SubmoduleUpdateCommand.java | 9 +-
.../src/org/eclipse/jgit/api/TagCommand.java | 15 +-
.../src/org/eclipse/jgit/api/TransportCommand.java | 1 +
.../errors/AbortedByHookException.java} | 64 +-
...undException.java => EmtpyCommitException.java} | 20 +-
.../jgit/api/errors/FilterFailedException.java | 144 ++
...ception.java => RefNotAdvertisedException.java} | 10 +-
.../jgit/api/errors/RefNotFoundException.java | 9 +
.../api/errors/StashApplyFailureException.java | 9 +
...hsException.java => TooLargePackException.java} | 27 +-
.../jgit/api/errors/UnmergedPathsException.java | 9 +
.../src/org/eclipse/jgit/attributes/Attribute.java | 16 +-
.../org/eclipse/jgit/attributes/Attributes.java | 202 +++
.../eclipse/jgit/attributes/AttributesNode.java | 12 +-
.../AttributesNodeProvider.java} | 57 +-
.../AttributesProvider.java} | 20 +-
.../eclipse/jgit/attributes/AttributesRule.java | 19 +
.../src/org/eclipse/jgit/blame/BlameGenerator.java | 20 +-
.../src/org/eclipse/jgit/blame/BlameResult.java | 8 +-
.../src/org/eclipse/jgit/diff/ContentSource.java | 8 +-
.../src/org/eclipse/jgit/diff/DiffFormatter.java | 56 +-
.../src/org/eclipse/jgit/diff/HistogramDiff.java | 4 +-
.../src/org/eclipse/jgit/diff/MyersDiff.java | 5 +
.../src/org/eclipse/jgit/diff/RenameDetector.java | 2 +-
.../src/org/eclipse/jgit/diff/SimilarityIndex.java | 52 +-
.../eclipse/jgit/dircache/BaseDirCacheEditor.java | 76 +
.../src/org/eclipse/jgit/dircache/DirCache.java | 50 +-
.../jgit/dircache/DirCacheBuildIterator.java | 5 +
.../org/eclipse/jgit/dircache/DirCacheBuilder.java | 81 +-
.../eclipse/jgit/dircache/DirCacheCheckout.java | 207 +--
.../org/eclipse/jgit/dircache/DirCacheEditor.java | 180 ++-
.../org/eclipse/jgit/dircache/DirCacheEntry.java | 50 +-
.../eclipse/jgit/dircache/DirCacheIterator.java | 3 -
.../org/eclipse/jgit/dircache/DirCacheTree.java | 5 +-
.../jgit/errors/CorruptObjectException.java | 41 +-
.../DiffInterruptedException.java} | 36 +-
...ion.java => DirCacheNameConflictException.java} | 41 +-
.../IndexReadException.java} | 40 +-
.../jgit/errors/InvalidObjectIdException.java | 26 +-
.../eclipse/jgit/errors/LockFailedException.java | 14 +
.../eclipse/jgit/errors/PackProtocolException.java | 8 +-
.../eclipse/jgit/errors/TooLargePackException.java | 17 +-
.../src/org/eclipse/jgit/fnmatch/GroupHead.java | 4 +-
.../org/eclipse/jgit/gitrepo/ManifestParser.java | 392 ++++++
.../src/org/eclipse/jgit/gitrepo/RepoCommand.java | 520 ++-----
.../src/org/eclipse/jgit/gitrepo/RepoProject.java | 319 +++++
.../src/org/eclipse/jgit/hooks/CommitMsgHook.java | 146 ++
.../src/org/eclipse/jgit/hooks/GitHook.java | 159 +++
.../StrategyRecursive.java => hooks/Hooks.java} | 47 +-
.../PreCommitHook.java} | 39 +-
.../src/org/eclipse/jgit/hooks/PrePushHook.java | 159 +++
.../org/eclipse/jgit/ignore/FastIgnoreRule.java | 50 +-
.../src/org/eclipse/jgit/ignore/IgnoreNode.java | 9 +-
.../src/org/eclipse/jgit/ignore/IgnoreRule.java | 278 ----
.../ignore/internal/LeadingAsteriskMatcher.java | 2 +-
.../eclipse/jgit/ignore/internal/NameMatcher.java | 6 +-
.../eclipse/jgit/ignore/internal/PathMatcher.java | 30 +-
.../org/eclipse/jgit/ignore/internal/Strings.java | 92 +-
.../ignore/internal/TrailingAsteriskMatcher.java | 2 +-
.../jgit/ignore/internal/WildCardMatcher.java | 2 +-
.../src/org/eclipse/jgit/internal/JGitText.java | 100 +-
.../jgit/internal/storage/dfs/DeltaBaseCache.java | 148 +-
.../jgit/internal/storage/dfs/DfsBlock.java | 8 +-
.../jgit/internal/storage/dfs/DfsBlockCache.java | 8 +
.../internal/storage/dfs/DfsBlockCacheConfig.java | 44 +-
.../jgit/internal/storage/dfs/DfsCachedPack.java | 5 +-
.../internal/storage/dfs/DfsGarbageCollector.java | 99 +-
.../jgit/internal/storage/dfs/DfsInserter.java | 31 +-
.../jgit/internal/storage/dfs/DfsObjDatabase.java | 30 +
.../internal/storage/dfs/DfsPackCompactor.java | 37 +-
.../internal/storage/dfs/DfsPackDescription.java | 8 +-
.../jgit/internal/storage/dfs/DfsPackFile.java | 154 +-
.../jgit/internal/storage/dfs/DfsReader.java | 75 +-
.../internal/storage/dfs/DfsReaderOptions.java | 31 +-
.../jgit/internal/storage/dfs/DfsRefDatabase.java | 27 +-
.../jgit/internal/storage/dfs/DfsRepository.java | 47 +-
.../internal/storage/dfs/DfsRepositoryBuilder.java | 4 +-
.../internal/storage/dfs/InMemoryRepository.java | 253 +++-
.../storage/dfs/LargePackedWholeObject.java | 4 +-
.../jgit/internal/storage/dfs/PackInputStream.java | 2 +-
.../jgit/internal/storage/dfs/ReadableChannel.java | 29 +
.../internal/storage/file/BitmapIndexImpl.java | 211 +--
.../internal/storage/file/ByteArrayWindow.java | 5 +-
.../internal/storage/file/ByteBufferWindow.java | 5 +-
.../jgit/internal/storage/file/ByteWindow.java | 5 +-
.../jgit/internal/storage/file/DeltaBaseCache.java | 2 +-
.../jgit/internal/storage/file/FileRepository.java | 86 +-
.../org/eclipse/jgit/internal/storage/file/GC.java | 243 ++--
...kInputStream.java => GlobalAttributesNode.java} | 70 +-
...ackInputStream.java => InfoAttributesNode.java} | 58 +-
.../storage/file/LazyObjectIdSetFile.java} | 95 +-
.../internal/storage/file/LocalCachedPack.java | 4 +-
.../jgit/internal/storage/file/LockFile.java | 78 +-
.../internal/storage/file/ObjectDirectory.java | 80 +-
.../storage/file/ObjectDirectoryInserter.java | 11 +-
.../storage/file/ObjectDirectoryPackParser.java | 25 +-
.../internal/storage/file/PackBitmapIndex.java | 7 +
.../storage/file/PackBitmapIndexBuilder.java | 77 +-
.../storage/file/PackBitmapIndexRemapper.java | 10 +-
.../internal/storage/file/PackBitmapIndexV1.java | 8 +-
.../jgit/internal/storage/file/PackFile.java | 49 +-
.../jgit/internal/storage/file/PackIndex.java | 9 +-
.../jgit/internal/storage/file/PackIndexV1.java | 6 +-
.../jgit/internal/storage/file/PackIndexV2.java | 12 +-
.../internal/storage/file/PackInputStream.java | 2 +-
.../jgit/internal/storage/file/RefDirectory.java | 149 +-
.../internal/storage/file/RefDirectoryRename.java | 27 +-
.../internal/storage/file/RefDirectoryUpdate.java | 6 +-
.../internal/storage/file/ReflogReaderImpl.java | 6 +
.../jgit/internal/storage/file/UnpackedObject.java | 20 +-
.../jgit/internal/storage/file/WindowCursor.java | 55 +-
.../jgit/internal/storage/pack/BinaryDelta.java | 16 +-
.../jgit/internal/storage/pack/DeltaTask.java | 69 +-
.../internal/storage/pack/ObjectReuseAsIs.java | 9 +-
.../jgit/internal/storage/pack/ObjectToPack.java | 8 +-
.../jgit/internal/storage/pack/PackWriter.java | 496 +++----
.../storage/pack/PackWriterBitmapPreparer.java | 511 +++++--
.../storage/pack/PackWriterBitmapWalker.java | 123 +-
.../AlwaysFailUpdate.java} | 59 +-
.../jgit/internal/storage/reftree/Command.java | 316 +++++
.../jgit/internal/storage/reftree/RefTree.java | 411 ++++++
.../internal/storage/reftree/RefTreeBatch.java | 222 +++
.../internal/storage/reftree/RefTreeDatabase.java | 337 +++++
.../internal/storage/reftree/RefTreeNames.java | 124 ++
.../storage/reftree/RefTreeRename.java} | 123 +-
.../internal/storage/reftree/RefTreeUpdate.java | 176 +++
.../jgit/internal/storage/reftree/Scanner.java | 286 ++++
.../eclipse/jgit/lib/BaseRepositoryBuilder.java | 9 +-
.../src/org/eclipse/jgit/lib/BatchRefUpdate.java | 48 +
.../src/org/eclipse/jgit/lib/BitmapIndex.java | 23 +
.../src/org/eclipse/jgit/lib/BlobBasedConfig.java | 24 +-
.../src/org/eclipse/jgit/lib/Config.java | 2 -
.../src/org/eclipse/jgit/lib/ConfigConstants.java | 12 +
.../src/org/eclipse/jgit/lib/Constants.java | 36 +
.../src/org/eclipse/jgit/lib/FileTreeEntry.java | 115 --
.../src/org/eclipse/jgit/lib/GitlinkTreeEntry.java | 87 --
.../src/org/eclipse/jgit/lib/IndexDiff.java | 207 +--
.../src/org/eclipse/jgit/lib/ObjectChecker.java | 752 +++++++---
.../src/org/eclipse/jgit/lib/ObjectDatabase.java | 10 +-
.../src/org/eclipse/jgit/lib/ObjectId.java | 13 +-
.../src/org/eclipse/jgit/lib/ObjectIdOwnerMap.java | 10 +-
.../src/org/eclipse/jgit/lib/ObjectIdRef.java | 36 +-
.../src/org/eclipse/jgit/lib/ObjectIdSet.java | 35 +-
.../org/eclipse/jgit/lib/ObjectIdSubclassMap.java | 7 +-
.../src/org/eclipse/jgit/lib/ObjectInserter.java | 18 +-
.../src/org/eclipse/jgit/lib/ObjectReader.java | 50 +-
.../src/org/eclipse/jgit/lib/PersonIdent.java | 87 +-
org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java | 17 +-
.../src/org/eclipse/jgit/lib/RefDatabase.java | 98 +-
.../src/org/eclipse/jgit/lib/RefRename.java | 2 +-
.../src/org/eclipse/jgit/lib/RefUpdate.java | 39 +-
.../src/org/eclipse/jgit/lib/RefWriter.java | 26 +-
.../src/org/eclipse/jgit/lib/Repository.java | 239 +++-
.../src/org/eclipse/jgit/lib/RepositoryCache.java | 29 +
.../src/org/eclipse/jgit/lib/RepositoryState.java | 4 +-
.../src/org/eclipse/jgit/lib/SymbolicRef.java | 11 +-
.../src/org/eclipse/jgit/lib/SymlinkTreeEntry.java | 85 --
.../src/org/eclipse/jgit/lib/Tree.java | 601 --------
.../src/org/eclipse/jgit/lib/TreeEntry.java | 256 ----
.../EolAwareOutputStream.java} | 67 +-
.../src/org/eclipse/jgit/merge/MergeFormatter.java | 43 +-
.../org/eclipse/jgit/merge/MergeFormatterPass.java | 146 ++
.../eclipse/jgit/merge/MergeMessageFormatter.java | 37 +-
.../src/org/eclipse/jgit/merge/MergeResult.java | 4 +-
.../src/org/eclipse/jgit/merge/Merger.java | 69 +-
.../org/eclipse/jgit/merge/RecursiveMerger.java | 42 +-
.../src/org/eclipse/jgit/merge/ResolveMerger.java | 45 +-
.../eclipse/jgit/merge/SquashMessageFormatter.java | 8 +-
.../org/eclipse/jgit/merge/StrategyRecursive.java | 2 +-
.../src/org/eclipse/jgit/notes/FanoutBucket.java | 4 +-
.../src/org/eclipse/jgit/notes/LeafBucket.java | 5 +-
.../src/org/eclipse/jgit/notes/NonNoteEntry.java | 26 +-
.../src/org/eclipse/jgit/notes/NoteMapMerger.java | 4 +-
.../eclipse/jgit/revwalk/MergeBaseGenerator.java | 1 -
.../src/org/eclipse/jgit/revwalk/ObjectWalk.java | 65 +-
.../org/eclipse/jgit/revwalk/PendingGenerator.java | 2 -
.../src/org/eclipse/jgit/revwalk/RevCommit.java | 102 +-
.../src/org/eclipse/jgit/revwalk/RevTag.java | 75 +-
.../src/org/eclipse/jgit/revwalk/RevWalk.java | 46 +-
.../org/eclipse/jgit/revwalk/StartGenerator.java | 4 +-
.../filter/ObjectFilter.java} | 91 +-
.../eclipse/jgit/storage/file/FileBasedConfig.java | 3 +
.../jgit/storage/file/WindowCacheConfig.java | 1 +
.../org/eclipse/jgit/storage/pack/PackConfig.java | 278 +++-
.../eclipse/jgit/storage/pack/PackStatistics.java | 491 +++++++
.../org/eclipse/jgit/submodule/SubmoduleWalk.java | 40 +-
.../src/org/eclipse/jgit/transport/AmazonS3.java | 88 +-
.../org/eclipse/jgit/transport/BaseConnection.java | 24 +
.../eclipse/jgit/transport/BasePackConnection.java | 18 +-
.../jgit/transport/BasePackFetchConnection.java | 23 +-
.../jgit/transport/BasePackPushConnection.java | 45 +-
.../eclipse/jgit/transport/BaseReceivePack.java | 446 ++++--
.../jgit/transport/BundleFetchConnection.java | 37 +-
.../org/eclipse/jgit/transport/BundleWriter.java | 32 +-
.../transport/ChainingCredentialsProvider.java | 17 +-
.../src/org/eclipse/jgit/transport/Connection.java | 16 +-
.../jgit/transport/CredentialsProvider.java | 14 +
.../src/org/eclipse/jgit/transport/Daemon.java | 8 +-
.../org/eclipse/jgit/transport/FetchProcess.java | 26 +-
.../jgit/transport/GitProtocolConstants.java | 36 +
.../jgit/transport/HMACSHA1NonceGenerator.java | 169 +++
.../org/eclipse/jgit/transport/HttpAuthMethod.java | 3 +-
.../jgit/transport/InternalFetchConnection.java | 141 ++
.../InternalHttpServerGlue.java} | 59 +-
.../jgit/transport/InternalPushConnection.java | 131 ++
.../org/eclipse/jgit/transport/JschSession.java | 29 +-
.../jgit/transport/NetRCCredentialsProvider.java | 3 +-
...oadPackLoggerChain.java => NonceGenerator.java} | 81 +-
...oadPackLogger.java => ObjectCountCallback.java} | 42 +-
.../eclipse/jgit/transport/OperationResult.java | 13 +
.../src/org/eclipse/jgit/transport/PackParser.java | 78 +-
.../{UploadPackLogger.java => PostUploadHook.java} | 24 +-
...ckLoggerChain.java => PostUploadHookChain.java} | 51 +-
.../eclipse/jgit/transport/ProgressSpinner.java | 149 ++
.../eclipse/jgit/transport/PushCertificate.java | 276 ++++
.../jgit/transport/PushCertificateIdent.java | 269 ++++
.../jgit/transport/PushCertificateParser.java | 465 ++++++
.../jgit/transport/PushCertificateStore.java | 548 +++++++
.../org/eclipse/jgit/transport/PushProcess.java | 40 +-
.../org/eclipse/jgit/transport/ReceiveCommand.java | 55 +-
.../org/eclipse/jgit/transport/ReceivePack.java | 13 +-
.../org/eclipse/jgit/transport/RefAdvertiser.java | 18 +-
.../src/org/eclipse/jgit/transport/RefSpec.java | 4 -
.../eclipse/jgit/transport/RemoteRefUpdate.java | 6 +-
.../jgit/transport/SideBandInputStream.java | 19 +-
.../eclipse/jgit/transport/SignedPushConfig.java | 146 ++
.../org/eclipse/jgit/transport/TestProtocol.java | 194 +++
.../eclipse/jgit/transport/TrackingRefUpdate.java | 10 +-
.../org/eclipse/jgit/transport/TransferConfig.java | 153 +-
.../src/org/eclipse/jgit/transport/Transport.java | 60 +-
.../eclipse/jgit/transport/TransportAmazonS3.java | 9 +-
.../eclipse/jgit/transport/TransportGitSsh.java | 6 +-
.../org/eclipse/jgit/transport/TransportHttp.java | 46 +-
.../org/eclipse/jgit/transport/TransportLocal.java | 207 +--
.../org/eclipse/jgit/transport/TransportSftp.java | 59 +-
.../src/org/eclipse/jgit/transport/URIish.java | 30 +-
.../src/org/eclipse/jgit/transport/UploadPack.java | 193 ++-
.../eclipse/jgit/transport/UploadPackLogger.java | 3 +
.../jgit/transport/UploadPackLoggerChain.java | 5 +-
.../src/org/eclipse/jgit/transport/UserAgent.java | 147 ++
.../org/eclipse/jgit/transport/WalkEncryption.java | 500 ++++++-
.../jgit/transport/WalkFetchConnection.java | 28 +-
.../eclipse/jgit/transport/WalkPushConnection.java | 10 +-
...ueException.java => WriteAbortedException.java} | 43 +-
.../resolver/ServiceNotAuthorizedException.java | 29 +-
.../resolver/ServiceNotEnabledException.java | 17 +
.../jgit/treewalk/AbstractTreeIterator.java | 74 +-
.../eclipse/jgit/treewalk/CanonicalTreeParser.java | 66 +-
.../eclipse/jgit/treewalk/EmptyTreeIterator.java | 5 +
.../eclipse/jgit/treewalk/FileTreeIterator.java | 8 +-
.../jgit/treewalk/NameConflictTreeWalk.java | 39 +-
.../src/org/eclipse/jgit/treewalk/TreeWalk.java | 457 +++++-
.../eclipse/jgit/treewalk/WorkingTreeIterator.java | 256 +---
.../jgit/treewalk/filter/IndexDiffFilter.java | 5 +-
.../jgit/treewalk/filter/PathFilterGroup.java | 6 +-
.../src/org/eclipse/jgit/util/BlockList.java | 8 +-
.../src/org/eclipse/jgit/util/ChangeIdUtil.java | 10 +-
org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java | 544 ++++---
.../src/org/eclipse/jgit/util/FS_POSIX.java | 224 ++-
.../src/org/eclipse/jgit/util/FS_POSIX_Java6.java | 144 --
.../src/org/eclipse/jgit/util/FS_Win32.java | 78 +-
.../src/org/eclipse/jgit/util/FS_Win32_Cygwin.java | 25 +-
.../src/org/eclipse/jgit/util/FileUtil.java | 239 ++++
.../src/org/eclipse/jgit/util/FileUtils.java | 351 ++++-
.../src/org/eclipse/jgit/util/Hook.java | 149 --
.../src/org/eclipse/jgit/util/HttpSupport.java | 6 +
org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java | 70 +
.../src/org/eclipse/jgit/util/Paths.java | 188 +++
.../src/org/eclipse/jgit/util/ProcessResult.java | 9 +
.../src/org/eclipse/jgit/util/RawParseUtils.java | 125 +-
.../src/org/eclipse/jgit/util/RefList.java | 4 +-
.../src/org/eclipse/jgit/util/RefMap.java | 14 +-
.../src/org/eclipse/jgit/util/StringUtils.java | 26 +
.../src/org/eclipse/jgit/util/SystemReader.java | 63 +-
.../src/org/eclipse/jgit/util/TemporaryBuffer.java | 116 +-
.../eclipse/jgit/util/io/LimitedInputStream.java | 6 +-
.../org/eclipse/jgit/util/io/StreamCopyThread.java | 22 +-
pom.xml | 164 ++-
tools/default.defs | 42 +
tools/eclipse-JGit-Format.xml | 6 +-
tools/git.defs | 9 +
tools/maven-central/deploy.rb | 3 +-
tools/maven-central/download.rb | 3 +-
tools/version.sh | 11 -
751 files changed, 37635 insertions(+), 18542 deletions(-)
diff --git a/.buckconfig b/.buckconfig
new file mode 100644
index 0000000..b2e07ac
--- /dev/null
+++ b/.buckconfig
@@ -0,0 +1,15 @@
+[buildfile]
+ includes = //tools/default.defs
+
+[java]
+ src_roots = src, resources, tst
+
+[project]
+ ignore = .git
+
+[cache]
+ mode = dir
+
+[download]
+ maven_repo = http://repo1.maven.org/maven2
+ in_build = true
diff --git a/.buckversion b/.buckversion
new file mode 100644
index 0000000..9daac2c
--- /dev/null
+++ b/.buckversion
@@ -0,0 +1 @@
+1b03b4313b91b634bd604fc3487a05f877e59dee
diff --git a/.gitignore b/.gitignore
index ea8c4bf..6c62199 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,4 @@
/target
+/.project
+/buck-cache
+/buck-out
diff --git a/BUCK b/BUCK
new file mode 100644
index 0000000..f19b7bd
--- /dev/null
+++ b/BUCK
@@ -0,0 +1,45 @@
+java_library(
+ name = 'jgit',
+ exported_deps = ['//org.eclipse.jgit:jgit'],
+ visibility = ['PUBLIC'],
+)
+
+genrule(
+ name = 'jgit_src',
+ cmd = 'ln -s $(location //org.eclipse.jgit:jgit_src) $OUT',
+ out = 'jgit_src.zip',
+ visibility = ['PUBLIC'],
+)
+
+java_library(
+ name = 'jgit-servlet',
+ exported_deps = [
+ ':jgit',
+ '//org.eclipse.jgit.http.server:jgit-servlet'
+ ],
+ visibility = ['PUBLIC'],
+)
+
+java_library(
+ name = 'jgit-archive',
+ exported_deps = [
+ ':jgit',
+ '//org.eclipse.jgit.archive:jgit-archive'
+ ],
+ visibility = ['PUBLIC'],
+)
+
+java_library(
+ name = 'junit',
+ exported_deps = [
+ ':jgit',
+ '//org.eclipse.jgit.junit:junit'
+ ],
+ visibility = ['PUBLIC'],
+)
+
+genrule(
+ name = 'jgit_bin',
+ cmd = 'ln -s $(location //org.eclipse.jgit.pgm:jgit) $OUT',
+ out = 'jgit_bin',
+)
diff --git a/README.md b/README.md
index 72eafd2..333fa28 100644
--- a/README.md
+++ b/README.md
@@ -30,11 +30,6 @@ there, but the automated builds use Maven.
Support for exporting to various archive formats (zip etc).
-- org.eclipse.jgit.console
-
- Support for reading passwords from the console without
- echoing them. Requires Java 6.
-
- org.eclipse.jgit.http.apache
Apache httpclient support
@@ -87,12 +82,7 @@ Warnings/Caveats
- Only the timestamp of the index is used by jgit if the index is
dirty.
-- Don't try the library with a JDK other than 1.6 (Java 6) unless you
- are prepared to investigate problems yourself. JDK 1.5.0_11 and later
- Java 5 versions *may* work. Earlier versions do not. JDK 1.4 is *not*
- supported. Apple's Java 1.5.0_07 is reported to work acceptably. We
- have no information about other vendors. Please report your findings
- if you try.
+- JGit requires at least a Java 7 JDK.
- CRLF conversion is performed depending on the core.autocrlf setting,
however Git for Windows by default stores that setting during
@@ -104,9 +94,9 @@ Warnings/Caveats
Git is installed. Make sure Git can be found via the PATH
environment variable. When installing Git for Windows check the "Run
Git from the Windows Command Prompt" option. There are other options
- like the jgit.gitprefix system property or Eclipse settings that can
- be used for pointing out where C Git is installed. Modifying PATH is
- the recommended option if C Git is installed.
+ like Eclipse settings that can be used for pointing out where C Git
+ is installed. Modifying PATH is the recommended option if C Git is
+ installed.
- We try to use the same notation of $HOME as C Git does. On Windows
this is often not the same value as the user.home system property.
@@ -177,10 +167,6 @@ Package Features
* Support for Zip/Tar and other formats
-- org.eclipse.jgit.console/
-
- * Reads passwords from the console
-
- org.eclipse.http.*/
* HTTP client and server support
diff --git a/lib/BUCK b/lib/BUCK
new file mode 100644
index 0000000..524612b
--- /dev/null
+++ b/lib/BUCK
@@ -0,0 +1,125 @@
+maven_jar(
+ name = 'jsch',
+ bin_sha1 = '658b682d5c817b27ae795637dfec047c63d29935',
+ src_sha1 = '791359d94d6edcace686a56d0727ee093a2f7c33',
+ group = 'com.jcraft',
+ artifact = 'jsch',
+ version = '0.1.53',
+)
+
+maven_jar(
+ name = 'javaewah',
+ bin_sha1 = 'eceaf316a8faf0e794296ebe158ae110c7d72a5a',
+ src_sha1 = 'a50d78eb630e05439461f3130b94b3bcd1ea6f03',
+ group = 'com.googlecode.javaewah',
+ artifact = 'JavaEWAH',
+ version = '0.7.9',
+)
+
+maven_jar(
+ name = 'httpcomponents',
+ bin_sha1 = '4c47155e3e6c9a41a28db36680b828ced53b8af4',
+ src_sha1 = 'af4d76be0c46ee26b0d9d1d4a34d244a633cac84',
+ group = 'org.apache.httpcomponents',
+ artifact = 'httpclient',
+ version = '4.3.6',
+)
+
+maven_jar(
+ name = 'httpcore',
+ bin_sha1 = 'f91b7a4aadc5cf486df6e4634748d7dd7a73f06d',
+ src_sha1 = '1b0aa62a6a91e9fa00c16f0a4a2c874804ed3b1e',
+ group = 'org.apache.httpcomponents',
+ artifact = 'httpcore',
+ version = '4.3.3',
+)
+
+maven_jar(
+ name = 'commons-logging',
+ bin_sha1 = 'f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f',
+ src_sha1 = '28bb0405fddaf04f15058fbfbe01fe2780d7d3b6',
+ group = 'commons-logging',
+ artifact = 'commons-logging',
+ version = '1.1.3',
+)
+
+maven_jar(
+ name = 'slf4j-api',
+ bin_sha1 = '0081d61b7f33ebeab314e07de0cc596f8e858d97',
+ src_sha1 = '58d38f68d4a867d4552ae27960bb348d7eaa1297',
+ group = 'org.slf4j',
+ artifact = 'slf4j-api',
+ version = '1.7.2',
+)
+
+maven_jar(
+ name = 'slf4j-simple',
+ bin_sha1 = '760055906d7353ba4f7ce1b8908bc6b2e91f39fa',
+ src_sha1 = '09474919128b3a7fcf21a5f9c907f5251f234544',
+ group = 'org.slf4j',
+ artifact = 'slf4j-simple',
+ version = '1.7.2',
+)
+
+maven_jar(
+ name = 'servlet-api',
+ bin_sha1 = '3cd63d075497751784b2fa84be59432f4905bf7c',
+ src_sha1 = 'ab3976d4574c48d22dc1abf6a9e8bd0fdf928223',
+ group = 'javax.servlet',
+ artifact = 'javax.servlet-api',
+ version = '3.1.0',
+)
+
+maven_jar(
+ name = 'commons-compress',
+ bin_sha1 = 'c7d9b580aff9e9f1998361f16578e63e5c064699',
+ src_sha1 = '396b81bdfd0fb617178e1707ef64832215307c78',
+ group = 'org.apache.commons',
+ artifact = 'commons-compress',
+ version = '1.6',
+)
+
+maven_jar(
+ name = 'tukaani-xz',
+ bin_sha1 = '66db21c8484120cb6a51b5b3ea47b6f383942bec',
+ src_sha1 = '6396220725701d767c553902c41120d7bf38e9f5',
+ group = 'org.tukaani',
+ artifact = 'xz',
+ version = '1.3',
+)
+
+maven_jar(
+ name = 'args4j',
+ bin_sha1 = '139441471327b9cc6d56436cb2a31e60eb6ed2ba',
+ src_sha1 = '22631b78cc8f60a6918557e8cbdb33e90f63a77f',
+ group = 'args4j',
+ artifact = 'args4j',
+ version = '2.0.15',
+)
+
+maven_jar(
+ name = 'junit',
+ bin_sha1 = '4e031bb61df09069aeb2bffb4019e7a5034a4ee0',
+ src_sha1 = '28e0ad201304e4a4abf999ca0570b7cffc352c3c',
+ group = 'junit',
+ artifact = 'junit',
+ version = '4.11',
+)
+
+maven_jar(
+ name = 'hamcrest-library',
+ bin_sha1 = '4785a3c21320980282f9f33d0d1264a69040538f',
+ src_sha1 = '047a7ee46628ab7133129cd7cef1e92657bc275e',
+ group = 'org.hamcrest',
+ artifact = 'hamcrest-library',
+ version = '1.3',
+)
+
+maven_jar(
+ name = 'hamcrest-core',
+ bin_sha1 = '42a25dc3219429f0e5d060061f71acb49bf010a0',
+ src_sha1 = '1dc37250fbc78e23a65a67fbbaf71d2e9cbc3c0b',
+ group = 'org.hamcrest',
+ artifact = 'hamcrest-core',
+ version = '1.3',
+)
diff --git a/lib/jetty/BUCK b/lib/jetty/BUCK
new file mode 100644
index 0000000..6e7dec3
--- /dev/null
+++ b/lib/jetty/BUCK
@@ -0,0 +1,56 @@
+VERSION = '9.2.13.v20150730'
+GROUP = 'org.eclipse.jetty'
+
+maven_jar(
+ name = 'servlet',
+ bin_sha1 = '5ad6e38015a97ae9a60b6c2ad744ccfa9cf93a50',
+ src_sha1 = '78fbec19321150552d91f9e079c2f2ca33222b01',
+ group = GROUP,
+ artifact = 'jetty-servlet',
+ version = VERSION,
+)
+
+maven_jar(
+ name = 'security',
+ bin_sha1 = 'cc7c7f27ec4cc279253be1675d9e47e58b995943',
+ src_sha1 = '75632ebdf8bd651faafb97106c92496db59e165d',
+ group = GROUP,
+ artifact = 'jetty-security',
+ version = VERSION,
+)
+
+maven_jar(
+ name = 'server',
+ bin_sha1 = '5be7d1da0a7abffd142de3091d160717c120b6ab',
+ src_sha1 = '203e123f83efe2a5b8a9c74854c7897fe3563302',
+ group = GROUP,
+ artifact = 'jetty-server',
+ version = VERSION,
+)
+
+maven_jar(
+ name = 'http',
+ bin_sha1 = '23a745d9177ef67ef53cc46b9b70c5870082efc2',
+ src_sha1 = '5f87f7ff2057cd4b0995bc4fffe17b2aff64c130',
+ group = GROUP,
+ artifact = 'jetty-http',
+ version = VERSION,
+)
+
+maven_jar(
+ name = 'io',
+ bin_sha1 = '7a351e6a1b63dfd56b6632623f7ca2793ffb67ad',
+ src_sha1 = 'bbd61a84b748fc295456e1c5c3070aaf40a68f62',
+ group = GROUP,
+ artifact = 'jetty-io',
+ version = VERSION,
+)
+
+maven_jar(
+ name = 'util',
+ bin_sha1 = 'c101476360a7cdd0670462de04053507d5e70c97',
+ src_sha1 = '15ceecce141971b4e0facb861b3d10120ad6ce03',
+ group = GROUP,
+ artifact = 'jetty-util',
+ version = VERSION,
+)
diff --git a/org.eclipse.jgit.ant.test/.classpath b/org.eclipse.jgit.ant.test/.classpath
index 64c5e31..098194c 100644
--- a/org.eclipse.jgit.ant.test/.classpath
+++ b/org.eclipse.jgit.ant.test/.classpath
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
diff --git a/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs
index 5fb6234..dcc0d3a 100644
--- a/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.ant.test/.settings/org.eclipse.jdt.core.prefs
@@ -7,9 +7,9 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
@@ -111,7 +111,7 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
-org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.compiler.source=1.7
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
diff --git a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
index 66ff855..32d231f 100644
--- a/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant.test/META-INF/MANIFEST.MF
@@ -3,14 +3,14 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Bundle-SymbolicName: org.eclipse.jgit.ant.test
-Bundle-Version: 3.7.1.201504261725-r
+Bundle-Version: 4.2.0.201601211800-r
Bundle-ActivationPolicy: lazy
-Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Import-Package: org.apache.tools.ant,
- org.eclipse.jgit.ant.tasks;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.internal.storage.file;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.junit;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.lib;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.util;version="[3.7.1,3.8.0)",
+ org.eclipse.jgit.ant.tasks;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.junit;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.lib;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.util;version="[4.2.0,4.3.0)",
org.hamcrest;version="[1.1.0,2.0.0)",
org.junit;version="[4.0.0,5.0.0)"
diff --git a/org.eclipse.jgit.ant.test/build.properties b/org.eclipse.jgit.ant.test/build.properties
index 9862181..e3effea 100644
--- a/org.eclipse.jgit.ant.test/build.properties
+++ b/org.eclipse.jgit.ant.test/build.properties
@@ -3,5 +3,5 @@ output.. = bin/
bin.includes = plugin.properties,\
META-INF/,\
.
-jre.compilation.profile = J2SE-1.5
+jre.compilation.profile = JavaSE-1.7
additional.bundles = org.eclipse.jgit
diff --git a/org.eclipse.jgit.ant.test/pom.xml b/org.eclipse.jgit.ant.test/pom.xml
index 6171b61..c0205d2 100644
--- a/org.eclipse.jgit.ant.test/pom.xml
+++ b/org.eclipse.jgit.ant.test/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>3.7.1.201504261725-r</version>
+ <version>4.2.0.201601211800-r</version>
</parent>
<artifactId>org.eclipse.jgit.ant.test</artifactId>
diff --git a/org.eclipse.jgit.ant/.classpath b/org.eclipse.jgit.ant/.classpath
index b3d21cc..a14ade4 100644
--- a/org.eclipse.jgit.ant/.classpath
+++ b/org.eclipse.jgit.ant/.classpath
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="resources"/>
diff --git a/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs
index 8b5e9d7..13a031a 100644
--- a/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.ant/.settings/org.eclipse.jdt.core.prefs
@@ -7,9 +7,9 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
@@ -111,7 +111,7 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
-org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.compiler.source=1.7
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
diff --git a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
index d2ed6b4..043c389 100644
--- a/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ant/META-INF/MANIFEST.MF
@@ -2,11 +2,11 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Bundle-SymbolicName: org.eclipse.jgit.ant
-Bundle-Version: 3.7.1.201504261725-r
-Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Bundle-Version: 4.2.0.201601211800-r
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Import-Package: org.apache.tools.ant,
- org.eclipse.jgit.storage.file;version="[3.7.1,3.8.0)"
+ org.eclipse.jgit.storage.file;version="[4.2.0,4.3.0)"
Bundle-Localization: plugin
Bundle-Vendor: %Provider-Name
-Export-Package: org.eclipse.jgit.ant.tasks;version="3.7.1";
+Export-Package: org.eclipse.jgit.ant.tasks;version="4.2.0";
uses:="org.apache.tools.ant.types,org.apache.tools.ant"
diff --git a/org.eclipse.jgit.ant/pom.xml b/org.eclipse.jgit.ant/pom.xml
index 030805e..d7f4839 100644
--- a/org.eclipse.jgit.ant/pom.xml
+++ b/org.eclipse.jgit.ant/pom.xml
@@ -48,7 +48,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>3.7.1.201504261725-r</version>
+ <version>4.2.0.201601211800-r</version>
</parent>
<artifactId>org.eclipse.jgit.ant</artifactId>
@@ -102,33 +102,92 @@
</configuration>
</plugin>
- <plugin>
- <artifactId>maven-compiler-plugin</artifactId>
- <configuration>
- <source>1.5</source>
- <target>1.5</target>
- <encoding>UTF-8</encoding>
- </configuration>
- </plugin>
-
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>clirr-maven-plugin</artifactId>
- </plugin>
+ <plugin>
+ <groupId>com.github.siom79.japicmp</groupId>
+ <artifactId>japicmp-maven-plugin</artifactId>
+ <version>${japicmp-version}</version>
+ <configuration>
+ <oldVersion>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>${project.artifactId}</artifactId>
+ <version>${jgit-last-release-version}</version>
+ </dependency>
+ </oldVersion>
+ <newVersion>
+ <file>
+ <path>${project.build.directory}/${project.artifactId}-${project.version}.jar</path>
+ </file>
+ </newVersion>
+ <parameter>
+ <onlyModified>true</onlyModified>
+ <includes>
+ <include>org.eclipse.jgit.*</include>
+ </includes>
+ <accessModifier>public</accessModifier>
+ <breakBuildOnModifications>false</breakBuildOnModifications>
+ <breakBuildOnBinaryIncompatibleModifications>false</breakBuildOnBinaryIncompatibleModifications>
+ <onlyBinaryIncompatible>false</onlyBinaryIncompatible>
+ <includeSynthetic>false</includeSynthetic>
+ <ignoreMissingClasses>false</ignoreMissingClasses>
+ <skipPomModules>true</skipPomModules>
+ </parameter>
+ <skip>false</skip>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>verify</phase>
+ <goals>
+ <goal>cmp</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
<reporting>
- <plugins>
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>clirr-maven-plugin</artifactId>
- <version>${clirr-version}</version>
- <configuration>
- <comparisonVersion>${jgit-last-release-version}</comparisonVersion>
- <minSeverity>info</minSeverity>
- </configuration>
- </plugin>
- </plugins>
- </reporting>
+ <plugins>
+ <plugin>
+ <groupId>com.github.siom79.japicmp</groupId>
+ <artifactId>japicmp-maven-plugin</artifactId>
+ <version>${japicmp-version}</version>
+ <reportSets>
+ <reportSet>
+ <reports>
+ <report>cmp-report</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ <configuration>
+ <oldVersion>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>${project.artifactId}</artifactId>
+ <version>${jgit-last-release-version}</version>
+ </dependency>
+ </oldVersion>
+ <newVersion>
+ <file>
+ <path>${project.build.directory}/${project.artifactId}-${project.version}.jar</path>
+ </file>
+ </newVersion>
+ <parameter>
+ <onlyModified>true</onlyModified>
+ <includes>
+ <include>org.eclipse.jgit.*</include>
+ </includes>
+ <accessModifier>public</accessModifier>
+ <breakBuildOnModifications>false</breakBuildOnModifications>
+ <breakBuildOnBinaryIncompatibleModifications>false</breakBuildOnBinaryIncompatibleModifications>
+ <onlyBinaryIncompatible>false</onlyBinaryIncompatible>
+ <includeSynthetic>false</includeSynthetic>
+ <ignoreMissingClasses>false</ignoreMissingClasses>
+ <skipPomModules>true</skipPomModules>
+ </parameter>
+ <skip>false</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </reporting>
</project>
diff --git a/org.eclipse.jgit.ant/src/org/eclipse/jgit/ant/tasks/GitAddTask.java b/org.eclipse.jgit.ant/src/org/eclipse/jgit/ant/tasks/GitAddTask.java
index c76ae2a..b9a8688 100644
--- a/org.eclipse.jgit.ant/src/org/eclipse/jgit/ant/tasks/GitAddTask.java
+++ b/org.eclipse.jgit.ant/src/org/eclipse/jgit/ant/tasks/GitAddTask.java
@@ -117,10 +117,10 @@ public class GitAddTask extends Task {
}
AddCommand gitAdd;
- try {
- Repository repo = new FileRepositoryBuilder().readEnvironment()
- .findGitDir(src).build();
- gitAdd = new Git(repo).add();
+ try (Repository repo = new FileRepositoryBuilder().readEnvironment()
+ .findGitDir(src).build();
+ Git git = new Git(repo);) {
+ gitAdd = git.add();
} catch (IOException e) {
throw new BuildException("Could not access repository " + src, e);
}
diff --git a/org.eclipse.jgit.ant/src/org/eclipse/jgit/ant/tasks/GitCheckoutTask.java b/org.eclipse.jgit.ant/src/org/eclipse/jgit/ant/tasks/GitCheckoutTask.java
index 14c4bc5..9962472 100644
--- a/org.eclipse.jgit.ant/src/org/eclipse/jgit/ant/tasks/GitCheckoutTask.java
+++ b/org.eclipse.jgit.ant/src/org/eclipse/jgit/ant/tasks/GitCheckoutTask.java
@@ -105,10 +105,10 @@ public class GitCheckoutTask extends Task {
@Override
public void execute() throws BuildException {
CheckoutCommand checkout;
- try {
- Repository repo = new FileRepositoryBuilder().readEnvironment()
- .findGitDir(src).build();
- checkout = new Git(repo).checkout();
+ try (Repository repo = new FileRepositoryBuilder().readEnvironment()
+ .findGitDir(src).build();
+ Git git = new Git(repo)) {
+ checkout = git.checkout();
} catch (IOException e) {
throw new BuildException("Could not access repository " + src, e);
}
diff --git a/org.eclipse.jgit.archive/.classpath b/org.eclipse.jgit.archive/.classpath
index 304e861..3bc2475 100644
--- a/org.eclipse.jgit.archive/.classpath
+++ b/org.eclipse.jgit.archive/.classpath
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs
index 12a5556..45d6d2c 100644
--- a/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.archive/.settings/org.eclipse.jdt.core.prefs
@@ -1,15 +1,15 @@
eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled
+org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=enabled
org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
-org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
-org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
-org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
-org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
+org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jgit.annotations.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jgit.annotations.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jgit.annotations.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
@@ -67,11 +67,11 @@ org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
org.eclipse.jdt.core.compiler.problem.nullReference=error
org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
-org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
+org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=ignore
org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
-org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=error
org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
@@ -111,7 +111,7 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
-org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.compiler.source=1.7
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
diff --git a/org.eclipse.jgit.archive/BUCK b/org.eclipse.jgit.archive/BUCK
new file mode 100644
index 0000000..ae17032
--- /dev/null
+++ b/org.eclipse.jgit.archive/BUCK
@@ -0,0 +1,13 @@
+java_library(
+ name = 'jgit-archive',
+ srcs = glob(
+ ['src/**'],
+ excludes = ['src/org/eclipse/jgit/archive/FormatActivator.java'],
+ ),
+ resources = glob(['resources/**']),
+ provided_deps = [
+ '//org.eclipse.jgit:jgit',
+ '//lib:commons-compress',
+ ],
+ visibility = ['PUBLIC'],
+)
diff --git a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
index cbecbd1..7bd23f6 100644
--- a/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.archive/META-INF/MANIFEST.MF
@@ -2,23 +2,24 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Bundle-SymbolicName: org.eclipse.jgit.archive
-Bundle-Version: 3.7.1.201504261725-r
+Bundle-Version: 4.2.0.201601211800-r
Bundle-Vendor: %provider_name
Bundle-Localization: plugin
-Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Import-Package: org.apache.commons.compress.archivers;version="[1.4,2.0)",
org.apache.commons.compress.archivers.tar;version="[1.4,2.0)",
org.apache.commons.compress.archivers.zip;version="[1.4,2.0)",
org.apache.commons.compress.compressors.bzip2;version="[1.4,2.0)",
org.apache.commons.compress.compressors.gzip;version="[1.4,2.0)",
org.apache.commons.compress.compressors.xz;version="[1.4,2.0)",
- org.eclipse.jgit.api;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.lib;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.nls;version="[3.7.1,3.8.0)",
+ org.eclipse.jgit.api;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.lib;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.nls;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.util;version="[4.2.0,4.3.0)",
org.osgi.framework;version="[1.3.0,2.0.0)"
Bundle-ActivationPolicy: lazy
Bundle-Activator: org.eclipse.jgit.archive.FormatActivator
-Export-Package: org.eclipse.jgit.archive;version="3.7.1";
+Export-Package: org.eclipse.jgit.archive;version="4.2.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.api,
org.apache.commons.compress.archivers,
diff --git a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
index c00e35a..7e8661d 100644
--- a/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.archive/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit.archive - Sources
Bundle-SymbolicName: org.eclipse.jgit.archive.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 3.7.1.201504261725-r
-Eclipse-SourceBundle: org.eclipse.jgit.archive;version="3.7.1.201504261725-r";roots="."
+Bundle-Version: 4.2.0.201601211800-r
+Eclipse-SourceBundle: org.eclipse.jgit.archive;version="4.2.0.201601211800-r";roots="."
diff --git a/org.eclipse.jgit.archive/pom.xml b/org.eclipse.jgit.archive/pom.xml
index c2d01b5..4515845 100644
--- a/org.eclipse.jgit.archive/pom.xml
+++ b/org.eclipse.jgit.archive/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>3.7.1.201504261725-r</version>
+ <version>4.2.0.201601211800-r</version>
</parent>
<artifactId>org.eclipse.jgit.archive</artifactId>
@@ -106,6 +106,93 @@
</archive>
</configuration>
</plugin>
+
+ <plugin>
+ <groupId>com.github.siom79.japicmp</groupId>
+ <artifactId>japicmp-maven-plugin</artifactId>
+ <version>${japicmp-version}</version>
+ <configuration>
+ <oldVersion>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>${project.artifactId}</artifactId>
+ <version>${jgit-last-release-version}</version>
+ </dependency>
+ </oldVersion>
+ <newVersion>
+ <file>
+ <path>${project.build.directory}/${project.artifactId}-${project.version}.jar</path>
+ </file>
+ </newVersion>
+ <parameter>
+ <onlyModified>true</onlyModified>
+ <includes>
+ <include>org.eclipse.jgit.*</include>
+ </includes>
+ <accessModifier>public</accessModifier>
+ <breakBuildOnModifications>false</breakBuildOnModifications>
+ <breakBuildOnBinaryIncompatibleModifications>false</breakBuildOnBinaryIncompatibleModifications>
+ <onlyBinaryIncompatible>false</onlyBinaryIncompatible>
+ <includeSynthetic>false</includeSynthetic>
+ <ignoreMissingClasses>false</ignoreMissingClasses>
+ <skipPomModules>true</skipPomModules>
+ </parameter>
+ <skip>false</skip>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>verify</phase>
+ <goals>
+ <goal>cmp</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
+
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>com.github.siom79.japicmp</groupId>
+ <artifactId>japicmp-maven-plugin</artifactId>
+ <version>${japicmp-version}</version>
+ <reportSets>
+ <reportSet>
+ <reports>
+ <report>cmp-report</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ <configuration>
+ <oldVersion>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>${project.artifactId}</artifactId>
+ <version>${jgit-last-release-version}</version>
+ </dependency>
+ </oldVersion>
+ <newVersion>
+ <file>
+ <path>${project.build.directory}/${project.artifactId}-${project.version}.jar</path>
+ </file>
+ </newVersion>
+ <parameter>
+ <onlyModified>true</onlyModified>
+ <includes>
+ <include>org.eclipse.jgit.*</include>
+ </includes>
+ <accessModifier>public</accessModifier>
+ <breakBuildOnModifications>false</breakBuildOnModifications>
+ <breakBuildOnBinaryIncompatibleModifications>false</breakBuildOnBinaryIncompatibleModifications>
+ <onlyBinaryIncompatible>false</onlyBinaryIncompatible>
+ <includeSynthetic>false</includeSynthetic>
+ <ignoreMissingClasses>false</ignoreMissingClasses>
+ <skipPomModules>true</skipPomModules>
+ </parameter>
+ <skip>false</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </reporting>
</project>
diff --git a/org.eclipse.jgit.archive/resources/org/eclipse/jgit/archive/internal/ArchiveText.properties b/org.eclipse.jgit.archive/resources/org/eclipse/jgit/archive/internal/ArchiveText.properties
index fecf99e..3b50bb4 100644
--- a/org.eclipse.jgit.archive/resources/org/eclipse/jgit/archive/internal/ArchiveText.properties
+++ b/org.eclipse.jgit.archive/resources/org/eclipse/jgit/archive/internal/ArchiveText.properties
@@ -1,2 +1,3 @@
+cannotSetOption=Cannot set option: {0}
pathDoesNotMatchMode=Path {0} does not match mode {1}
unsupportedMode=Unsupported mode {0}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/TooLargePackException.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/BaseFormat.java
similarity index 65%
copy from org.eclipse.jgit/src/org/eclipse/jgit/errors/TooLargePackException.java
copy to org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/BaseFormat.java
index 5cf0f80..e80d421 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/TooLargePackException.java
+++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/BaseFormat.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014, Sasa Zivkov <sasa.zivkov at sap.com>, SAP AG
+ * Copyright (C) 2015, David Ostrovsky <david at ostrovsky.org>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -41,29 +41,45 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.errors;
+package org.eclipse.jgit.archive;
+import java.beans.Statement;
import java.io.IOException;
import java.text.MessageFormat;
+import java.util.Map;
-import org.eclipse.jgit.internal.JGitText;
+import org.apache.commons.compress.archivers.ArchiveOutputStream;
+import org.eclipse.jgit.archive.internal.ArchiveText;
+import org.eclipse.jgit.util.StringUtils;
/**
- * Thrown when a pack exceeds a given size limit
+ * Base format class
*
- * @since 3.3
+ * @since 4.0
*/
-public class TooLargePackException extends IOException {
- private static final long serialVersionUID = 1L;
+public class BaseFormat {
/**
- * Construct a too large pack exception.
+ * Apply options to archive output stream
*
- * @param packSizeLimit
- * the pack size limit (in bytes) that was exceeded
+ * @param s
+ * stream to apply options to
+ * @param o
+ * options map
+ * @return stream with option applied
+ * @throws IOException
*/
- public TooLargePackException(long packSizeLimit) {
- super(MessageFormat.format(JGitText.get().receivePackTooLarge,
- Long.valueOf(packSizeLimit)));
+ protected ArchiveOutputStream applyFormatOptions(ArchiveOutputStream s,
+ Map<String, Object> o) throws IOException {
+ for (Map.Entry<String, Object> p : o.entrySet()) {
+ try {
+ new Statement(s, "set" + StringUtils.capitalize(p.getKey()), //$NON-NLS-1$
+ new Object[] { p.getValue() }).execute();
+ } catch (Exception e) {
+ throw new IOException(MessageFormat.format(
+ ArchiveText.get().cannotSetOption, p.getKey()), e);
+ }
+ }
+ return s;
}
}
diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TarFormat.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TarFormat.java
index 9a0e304..d56cb35 100644
--- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TarFormat.java
+++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TarFormat.java
@@ -44,10 +44,11 @@ package org.eclipse.jgit.archive;
import java.io.IOException;
import java.io.OutputStream;
+import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import java.text.MessageFormat;
+import java.util.Map;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
@@ -61,15 +62,26 @@ import org.eclipse.jgit.lib.ObjectLoader;
/**
* Unix TAR format (ustar + some PAX extensions).
*/
-public final class TarFormat implements ArchiveCommand.Format<ArchiveOutputStream> {
+public final class TarFormat extends BaseFormat implements
+ ArchiveCommand.Format<ArchiveOutputStream> {
private static final List<String> SUFFIXES = Collections
.unmodifiableList(Arrays.asList(".tar")); //$NON-NLS-1$
- public ArchiveOutputStream createArchiveOutputStream(OutputStream s) {
+ public ArchiveOutputStream createArchiveOutputStream(OutputStream s)
+ throws IOException {
+ return createArchiveOutputStream(s,
+ Collections.<String, Object> emptyMap());
+ }
+
+ /**
+ * @since 4.0
+ */
+ public ArchiveOutputStream createArchiveOutputStream(OutputStream s,
+ Map<String, Object> o) throws IOException {
TarArchiveOutputStream out = new TarArchiveOutputStream(s, "UTF-8"); //$NON-NLS-1$
out.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
out.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX);
- return out;
+ return applyFormatOptions(out, o);
}
public void putEntry(ArchiveOutputStream out,
diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/Tbz2Format.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/Tbz2Format.java
index 65e1e79..f3ab4da 100644
--- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/Tbz2Format.java
+++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/Tbz2Format.java
@@ -47,6 +47,7 @@ import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream;
@@ -57,7 +58,8 @@ import org.eclipse.jgit.lib.ObjectLoader;
/**
* bzip2-compressed tarball (tar.bz2) format.
*/
-public final class Tbz2Format implements ArchiveCommand.Format<ArchiveOutputStream> {
+public final class Tbz2Format extends BaseFormat implements
+ ArchiveCommand.Format<ArchiveOutputStream> {
private static final List<String> SUFFIXES = Collections
.unmodifiableList(Arrays.asList(".tar.bz2", ".tbz", ".tbz2")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
@@ -65,8 +67,17 @@ public final class Tbz2Format implements ArchiveCommand.Format<ArchiveOutputStre
public ArchiveOutputStream createArchiveOutputStream(OutputStream s)
throws IOException {
+ return createArchiveOutputStream(s,
+ Collections.<String, Object> emptyMap());
+ }
+
+ /**
+ * @since 4.0
+ */
+ public ArchiveOutputStream createArchiveOutputStream(OutputStream s,
+ Map<String, Object> o) throws IOException {
BZip2CompressorOutputStream out = new BZip2CompressorOutputStream(s);
- return tarFormat.createArchiveOutputStream(out);
+ return tarFormat.createArchiveOutputStream(out, o);
}
public void putEntry(ArchiveOutputStream out,
diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TgzFormat.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TgzFormat.java
index e13c88a..06f09a4 100644
--- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TgzFormat.java
+++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TgzFormat.java
@@ -47,6 +47,7 @@ import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
@@ -57,7 +58,8 @@ import org.eclipse.jgit.lib.ObjectLoader;
/**
* gzip-compressed tarball (tar.gz) format.
*/
-public final class TgzFormat implements ArchiveCommand.Format<ArchiveOutputStream> {
+public final class TgzFormat extends BaseFormat implements
+ ArchiveCommand.Format<ArchiveOutputStream> {
private static final List<String> SUFFIXES = Collections
.unmodifiableList(Arrays.asList(".tar.gz", ".tgz")); //$NON-NLS-1$ //$NON-NLS-2$
@@ -65,8 +67,17 @@ public final class TgzFormat implements ArchiveCommand.Format<ArchiveOutputStrea
public ArchiveOutputStream createArchiveOutputStream(OutputStream s)
throws IOException {
+ return createArchiveOutputStream(s,
+ Collections.<String, Object> emptyMap());
+ }
+
+ /**
+ * @since 4.0
+ */
+ public ArchiveOutputStream createArchiveOutputStream(OutputStream s,
+ Map<String, Object> o) throws IOException {
GzipCompressorOutputStream out = new GzipCompressorOutputStream(s);
- return tarFormat.createArchiveOutputStream(out);
+ return tarFormat.createArchiveOutputStream(out, o);
}
public void putEntry(ArchiveOutputStream out,
diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TxzFormat.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TxzFormat.java
index d74ca9b..1483935 100644
--- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TxzFormat.java
+++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TxzFormat.java
@@ -47,6 +47,7 @@ import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream;
@@ -57,7 +58,8 @@ import org.eclipse.jgit.lib.ObjectLoader;
/**
* Xz-compressed tar (tar.xz) format.
*/
-public final class TxzFormat implements ArchiveCommand.Format<ArchiveOutputStream> {
+public final class TxzFormat extends BaseFormat implements
+ ArchiveCommand.Format<ArchiveOutputStream> {
private static final List<String> SUFFIXES = Collections
.unmodifiableList(Arrays.asList(".tar.xz", ".txz")); //$NON-NLS-1$ //$NON-NLS-2$
@@ -65,8 +67,17 @@ public final class TxzFormat implements ArchiveCommand.Format<ArchiveOutputStrea
public ArchiveOutputStream createArchiveOutputStream(OutputStream s)
throws IOException {
+ return createArchiveOutputStream(s,
+ Collections.<String, Object> emptyMap());
+ }
+
+ /**
+ * @since 4.0
+ */
+ public ArchiveOutputStream createArchiveOutputStream(OutputStream s,
+ Map<String, Object> o) throws IOException {
XZCompressorOutputStream out = new XZCompressorOutputStream(s);
- return tarFormat.createArchiveOutputStream(out);
+ return tarFormat.createArchiveOutputStream(out, o);
}
public void putEntry(ArchiveOutputStream out,
diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/ZipFormat.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/ZipFormat.java
index 988ef90..0e1b253 100644
--- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/ZipFormat.java
+++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/ZipFormat.java
@@ -48,6 +48,7 @@ import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
@@ -60,12 +61,23 @@ import org.eclipse.jgit.lib.ObjectLoader;
/**
* PKWARE's ZIP format.
*/
-public final class ZipFormat implements ArchiveCommand.Format<ArchiveOutputStream> {
+public final class ZipFormat extends BaseFormat implements
+ ArchiveCommand.Format<ArchiveOutputStream> {
private static final List<String> SUFFIXES = Collections
.unmodifiableList(Arrays.asList(".zip")); //$NON-NLS-1$
- public ArchiveOutputStream createArchiveOutputStream(OutputStream s) {
- return new ZipArchiveOutputStream(s);
+ public ArchiveOutputStream createArchiveOutputStream(OutputStream s)
+ throws IOException {
+ return createArchiveOutputStream(s,
+ Collections.<String, Object> emptyMap());
+ }
+
+ /**
+ * @since 4.0
+ */
+ public ArchiveOutputStream createArchiveOutputStream(OutputStream s,
+ Map<String, Object> o) throws IOException {
+ return applyFormatOptions(new ZipArchiveOutputStream(s), o);
}
public void putEntry(ArchiveOutputStream out,
diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/internal/ArchiveText.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/internal/ArchiveText.java
index edadf1c..f631cf8 100644
--- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/internal/ArchiveText.java
+++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/internal/ArchiveText.java
@@ -58,6 +58,7 @@ public class ArchiveText extends TranslationBundle {
}
// @formatter:off
+ /***/ public String cannotSetOption;
/***/ public String pathDoesNotMatchMode;
/***/ public String unsupportedMode;
}
diff --git a/org.eclipse.jgit.console/.classpath b/org.eclipse.jgit.console/.classpath
deleted file mode 100644
index 31db9a9..0000000
--- a/org.eclipse.jgit.console/.classpath
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
- <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="src" path="resources"/>
- <classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/org.eclipse.jgit.console/.gitignore b/org.eclipse.jgit.console/.gitignore
deleted file mode 100644
index 934e0e0..0000000
--- a/org.eclipse.jgit.console/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/bin
-/target
diff --git a/org.eclipse.jgit.console/.project b/org.eclipse.jgit.console/.project
deleted file mode 100644
index 4f272a7..0000000
--- a/org.eclipse.jgit.console/.project
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>org.eclipse.jgit.console</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.ManifestBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.SchemaBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>org.eclipse.pde.PluginNature</nature>
- <nature>org.eclipse.jdt.core.javanature</nature>
- <nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
- </natures>
-</projectDescription>
diff --git a/org.eclipse.jgit.console/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.console/.settings/org.eclipse.core.resources.prefs
deleted file mode 100644
index 190e5c5..0000000
--- a/org.eclipse.jgit.console/.settings/org.eclipse.core.resources.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Sun Oct 11 08:00:39 EEST 2009
-eclipse.preferences.version=1
-encoding/<project>=UTF-8
diff --git a/org.eclipse.jgit.console/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.console/.settings/org.eclipse.core.runtime.prefs
deleted file mode 100644
index 006e07e..0000000
--- a/org.eclipse.jgit.console/.settings/org.eclipse.core.runtime.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Mon Mar 24 18:55:50 EDT 2008
-eclipse.preferences.version=1
-line.separator=\n
diff --git a/org.eclipse.jgit.console/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.console/.settings/org.eclipse.jdt.core.prefs
deleted file mode 100644
index 285baa8..0000000
--- a/org.eclipse.jgit.console/.settings/org.eclipse.jdt.core.prefs
+++ /dev/null
@@ -1,398 +0,0 @@
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled
-org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
-org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
-org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
-org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
-org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
-org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
-org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.6
-org.eclipse.jdt.core.compiler.debug.lineNumber=generate
-org.eclipse.jdt.core.compiler.debug.localVariable=generate
-org.eclipse.jdt.core.compiler.debug.sourceFile=generate
-org.eclipse.jdt.core.compiler.doc.comment.support=enabled
-org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
-org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
-org.eclipse.jdt.core.compiler.problem.autoboxing=warning
-org.eclipse.jdt.core.compiler.problem.comparingIdentical=error
-org.eclipse.jdt.core.compiler.problem.deadCode=error
-org.eclipse.jdt.core.compiler.problem.deprecation=warning
-org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
-org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
-org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
-org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
-org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
-org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
-org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
-org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
-org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
-org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
-org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
-org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
-org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
-org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
-org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
-org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
-org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error
-org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
-org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
-org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
-org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
-org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
-org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
-org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
-org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
-org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
-org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
-org.eclipse.jdt.core.compiler.problem.missingJavadocComments=error
-org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
-org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
-org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
-org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
-org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
-org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
-org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
-org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning
-org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
-org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
-org.eclipse.jdt.core.compiler.problem.nullReference=error
-org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
-org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
-org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
-org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
-org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
-org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
-org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
-org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
-org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
-org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
-org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
-org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
-org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
-org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
-org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
-org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
-org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
-org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled
-org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
-org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
-org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
-org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
-org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
-org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
-org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
-org.eclipse.jdt.core.compiler.problem.unusedImport=error
-org.eclipse.jdt.core.compiler.problem.unusedLabel=error
-org.eclipse.jdt.core.compiler.problem.unusedLocal=error
-org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
-org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
-org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
-org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
-org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
-org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
-org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
-org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
-org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
-org.eclipse.jdt.core.compiler.source=1.6
-org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_assignment=0
-org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
-org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
-org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
-org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
-org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
-org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
-org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
-org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
-org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
-org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
-org.eclipse.jdt.core.formatter.blank_lines_after_package=1
-org.eclipse.jdt.core.formatter.blank_lines_before_field=1
-org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
-org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
-org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
-org.eclipse.jdt.core.formatter.blank_lines_before_method=1
-org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
-org.eclipse.jdt.core.formatter.blank_lines_before_package=0
-org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
-org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
-org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
-org.eclipse.jdt.core.formatter.comment.clear_blank_lines=false
-org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
-org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
-org.eclipse.jdt.core.formatter.comment.format_block_comments=true
-org.eclipse.jdt.core.formatter.comment.format_comments=true
-org.eclipse.jdt.core.formatter.comment.format_header=false
-org.eclipse.jdt.core.formatter.comment.format_html=true
-org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
-org.eclipse.jdt.core.formatter.comment.format_line_comments=true
-org.eclipse.jdt.core.formatter.comment.format_source_code=true
-org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
-org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
-org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
-org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
-org.eclipse.jdt.core.formatter.comment.line_length=80
-org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
-org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
-org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
-org.eclipse.jdt.core.formatter.compact_else_if=true
-org.eclipse.jdt.core.formatter.continuation_indentation=2
-org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
-org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
-org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
-org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
-org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
-org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
-org.eclipse.jdt.core.formatter.indent_empty_lines=false
-org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
-org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
-org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
-org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
-org.eclipse.jdt.core.formatter.indentation.size=4
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
-org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
-org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
-org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
-org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
-org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
-org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.join_lines_in_comments=true
-org.eclipse.jdt.core.formatter.join_wrapped_lines=true
-org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
-org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
-org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
-org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
-org.eclipse.jdt.core.formatter.lineSplit=80
-org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
-org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
-org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
-org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
-org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
-org.eclipse.jdt.core.formatter.tabulation.char=tab
-org.eclipse.jdt.core.formatter.tabulation.size=4
-org.eclipse.jdt.core.formatter.use_on_off_tags=true
-org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
-org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
-org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
-org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
diff --git a/org.eclipse.jgit.console/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.console/.settings/org.eclipse.jdt.ui.prefs
deleted file mode 100644
index c336cce..0000000
--- a/org.eclipse.jgit.console/.settings/org.eclipse.jdt.ui.prefs
+++ /dev/null
@@ -1,61 +0,0 @@
-eclipse.preferences.version=1
-editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
-formatter_profile=_JGit Format
-formatter_settings_version=12
-org.eclipse.jdt.ui.ignorelowercasenames=true
-org.eclipse.jdt.ui.importorder=java;javax;org;com;
-org.eclipse.jdt.ui.ondemandthreshold=99
-org.eclipse.jdt.ui.staticondemandthreshold=99
-org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/>
-sp_cleanup.add_default_serial_version_id=true
-sp_cleanup.add_generated_serial_version_id=false
-sp_cleanup.add_missing_annotations=false
-sp_cleanup.add_missing_deprecated_annotations=true
-sp_cleanup.add_missing_methods=false
-sp_cleanup.add_missing_nls_tags=false
-sp_cleanup.add_missing_override_annotations=true
-sp_cleanup.add_missing_override_annotations_interface_methods=false
-sp_cleanup.add_serial_version_id=false
-sp_cleanup.always_use_blocks=true
-sp_cleanup.always_use_parentheses_in_expressions=false
-sp_cleanup.always_use_this_for_non_static_field_access=false
-sp_cleanup.always_use_this_for_non_static_method_access=false
-sp_cleanup.convert_to_enhanced_for_loop=false
-sp_cleanup.correct_indentation=false
-sp_cleanup.format_source_code=true
-sp_cleanup.format_source_code_changes_only=true
-sp_cleanup.make_local_variable_final=false
-sp_cleanup.make_parameters_final=false
-sp_cleanup.make_private_fields_final=true
-sp_cleanup.make_type_abstract_if_missing_method=false
-sp_cleanup.make_variable_declarations_final=false
-sp_cleanup.never_use_blocks=false
-sp_cleanup.never_use_parentheses_in_expressions=true
-sp_cleanup.on_save_use_additional_actions=true
-sp_cleanup.organize_imports=false
-sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
-sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
-sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
-sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
-sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
-sp_cleanup.remove_private_constructors=true
-sp_cleanup.remove_trailing_whitespaces=true
-sp_cleanup.remove_trailing_whitespaces_all=true
-sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
-sp_cleanup.remove_unnecessary_casts=false
-sp_cleanup.remove_unnecessary_nls_tags=false
-sp_cleanup.remove_unused_imports=false
-sp_cleanup.remove_unused_local_variables=false
-sp_cleanup.remove_unused_private_fields=true
-sp_cleanup.remove_unused_private_members=false
-sp_cleanup.remove_unused_private_methods=true
-sp_cleanup.remove_unused_private_types=true
-sp_cleanup.sort_members=false
-sp_cleanup.sort_members_all=false
-sp_cleanup.use_blocks=false
-sp_cleanup.use_blocks_only_for_return_and_throw=false
-sp_cleanup.use_parentheses_in_expressions=false
-sp_cleanup.use_this_for_non_static_field_access=false
-sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
-sp_cleanup.use_this_for_non_static_method_access=false
-sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/org.eclipse.jgit.console/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.console/.settings/org.eclipse.mylyn.tasks.ui.prefs
deleted file mode 100644
index 823c0f5..0000000
--- a/org.eclipse.jgit.console/.settings/org.eclipse.mylyn.tasks.ui.prefs
+++ /dev/null
@@ -1,4 +0,0 @@
-#Tue Jul 19 20:11:28 CEST 2011
-eclipse.preferences.version=1
-project.repository.kind=bugzilla
-project.repository.url=https\://bugs.eclipse.org/bugs
diff --git a/org.eclipse.jgit.console/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.console/.settings/org.eclipse.mylyn.team.ui.prefs
deleted file mode 100644
index 0cba949..0000000
--- a/org.eclipse.jgit.console/.settings/org.eclipse.mylyn.team.ui.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
-eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.console/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.jgit.console/.settings/org.eclipse.pde.api.tools.prefs
deleted file mode 100644
index cd148d9..0000000
--- a/org.eclipse.jgit.console/.settings/org.eclipse.pde.api.tools.prefs
+++ /dev/null
@@ -1,94 +0,0 @@
-#Tue Oct 18 00:52:01 CEST 2011
-ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error
-ANNOTATION_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
-ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error
-ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD=Error
-ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
-API_COMPONENT_ELEMENT_TYPE_REMOVED_API_TYPE=Error
-API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_API_TYPE=Error
-API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_TYPE=Error
-API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error
-CLASS_ELEMENT_TYPE_ADDED_METHOD=Error
-CLASS_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
-CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
-CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
-CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
-CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
-CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
-CLASS_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
-CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
-CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error
-CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error
-CLASS_ELEMENT_TYPE_REMOVED_SUPERCLASS=Error
-CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
-CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
-CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
-CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
-CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
-CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
-ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
-ENUM_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
-ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error
-ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error
-ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error
-ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
-FIELD_ELEMENT_TYPE_ADDED_VALUE=Error
-FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
-FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Error
-FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
-FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
-FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
-FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error
-FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error
-FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENT=Error
-FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error
-ILLEGAL_EXTEND=Warning
-ILLEGAL_IMPLEMENT=Warning
-ILLEGAL_INSTANTIATE=Warning
-ILLEGAL_OVERRIDE=Warning
-ILLEGAL_REFERENCE=Warning
-INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error
-INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error
-INTERFACE_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
-INTERFACE_ELEMENT_TYPE_ADDED_SUPER_INTERFACE_WITH_METHODS=Error
-INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
-INTERFACE_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
-INTERFACE_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
-INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error
-INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error
-INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
-INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
-INVALID_JAVADOC_TAG=Ignore
-INVALID_REFERENCE_IN_SYSTEM_LIBRARIES=Error
-LEAK_EXTEND=Warning
-LEAK_FIELD_DECL=Warning
-LEAK_IMPLEMENT=Warning
-LEAK_METHOD_PARAM=Warning
-LEAK_METHOD_RETURN_TYPE=Warning
-METHOD_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
-METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
-METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
-METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
-METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
-METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
-METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
-METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
-METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
-METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
-TYPE_PARAMETER_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
-TYPE_PARAMETER_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
-TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
-TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
-TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
-TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
-UNUSED_PROBLEM_FILTERS=Warning
-automatically_removed_unused_problem_filters=false
-eclipse.preferences.version=1
-incompatible_api_component_version=Error
-incompatible_api_component_version_include_major_without_breaking_change=Disabled
-incompatible_api_component_version_include_minor_without_api_change=Disabled
-invalid_since_tag_version=Error
-malformed_since_tag=Error
-missing_since_tag=Error
-report_api_breakage_when_major_version_incremented=Disabled
-report_resolution_errors_api_component=Warning
diff --git a/org.eclipse.jgit.console/.settings/org.eclipse.pde.core.prefs b/org.eclipse.jgit.console/.settings/org.eclipse.pde.core.prefs
deleted file mode 100644
index 9482745..0000000
--- a/org.eclipse.jgit.console/.settings/org.eclipse.pde.core.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Sun Oct 11 07:58:06 EEST 2009
-eclipse.preferences.version=1
-resolve.requirebundle=false
diff --git a/org.eclipse.jgit.console/META-INF/MANIFEST.MF b/org.eclipse.jgit.console/META-INF/MANIFEST.MF
deleted file mode 100644
index 25155d5..0000000
--- a/org.eclipse.jgit.console/META-INF/MANIFEST.MF
+++ /dev/null
@@ -1,13 +0,0 @@
-Bundle-Localization: plugin
-Manifest-Version: 1.0
-Bundle-ManifestVersion: 2
-Bundle-Name: %plugin_name
-Bundle-SymbolicName: org.eclipse.jgit.console
-Bundle-Version: 3.7.1.201504261725-r
-Bundle-Vendor: %provider_name
-Bundle-RequiredExecutionEnvironment: JavaSE-1.6
-Export-Package: org.eclipse.jgit.console;version="3.7.1"
-Import-Package: org.eclipse.jgit.errors;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.nls;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.transport;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.util;version="[3.7.1,3.8.0)"
diff --git a/org.eclipse.jgit.console/about.html b/org.eclipse.jgit.console/about.html
deleted file mode 100644
index 01a2671..0000000
--- a/org.eclipse.jgit.console/about.html
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1" ?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
-<title>Eclipse Distribution License - Version 1.0</title>
-<style type="text/css">
- body {
- size: 8.5in 11.0in;
- margin: 0.25in 0.5in 0.25in 0.5in;
- tab-interval: 0.5in;
- }
- p {
- margin-left: auto;
- margin-top: 0.5em;
- margin-bottom: 0.5em;
- }
- p.list {
- margin-left: 0.5in;
- margin-top: 0.05em;
- margin-bottom: 0.05em;
- }
- </style>
-
-</head>
-
-<body lang="EN-US">
-
-<p><b>Eclipse Distribution License - v 1.0</b></p>
-
-<p>Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. </p>
-
-<p>All rights reserved.</p>
-<p>Redistribution and use in source and binary forms, with or without modification,
- are permitted provided that the following conditions are met:
-<ul><li>Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer. </li>
-<li>Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution. </li>
-<li>Neither the name of the Eclipse Foundation, Inc. nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission. </li></ul>
-</p>
-<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.</p>
-
-</body>
-
-</html>
diff --git a/org.eclipse.jgit.console/build.properties b/org.eclipse.jgit.console/build.properties
deleted file mode 100644
index 8148271..0000000
--- a/org.eclipse.jgit.console/build.properties
+++ /dev/null
@@ -1,7 +0,0 @@
-source.. = src/,\
- resources/
-output.. = bin/
-bin.includes = META-INF/,\
- .,\
- plugin.properties,\
- about.html
diff --git a/org.eclipse.jgit.console/plugin.properties b/org.eclipse.jgit.console/plugin.properties
deleted file mode 100644
index 73a746b..0000000
--- a/org.eclipse.jgit.console/plugin.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-plugin_name=JGit Console User Interface
-provider_name=Eclipse JGit
diff --git a/org.eclipse.jgit.console/pom.xml b/org.eclipse.jgit.console/pom.xml
deleted file mode 100644
index e542d25..0000000
--- a/org.eclipse.jgit.console/pom.xml
+++ /dev/null
@@ -1,132 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2009, Google Inc.
- Copyright (C) 2008, Imran M Yousuf <imyousuf at smartitengineering.com>
- Copyright (C) 2010, Matthias Sohn <matthias.sohn at sap.com>
- and other copyright owners as documented in the project's IP log.
-
- This program and the accompanying materials are made available
- under the terms of the Eclipse Distribution License v1.0 which
- accompanies this distribution, is reproduced below, and is
- available at http://www.eclipse.org/org/documents/edl-v10.php
-
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or
- without modification, are permitted provided that the following
- conditions are met:
-
- - Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- - Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- - Neither the name of the Eclipse Foundation, Inc. nor the
- names of its contributors may be used to endorse or promote
- products derived from this software without specific prior
- written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <groupId>org.eclipse.jgit</groupId>
- <artifactId>org.eclipse.jgit-parent</artifactId>
- <version>3.7.1.201504261725-r</version>
- </parent>
-
- <artifactId>org.eclipse.jgit.console</artifactId>
- <name>JGit - Console User Interface</name>
-
- <description>
- Console based user interface
- </description>
-
- <properties>
- <translate-qualifier/>
- </properties>
-
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jgit</groupId>
- <artifactId>org.eclipse.jgit</artifactId>
- <version>${project.version}</version>
- </dependency>
- </dependencies>
-
- <build>
- <sourceDirectory>src/</sourceDirectory>
-
- <resources>
- <resource>
- <directory>.</directory>
- <includes>
- <include>plugin.properties</include>
- <include>about.html</include>
- </includes>
- </resource>
- <resource>
- <directory>resources/</directory>
- </resource>
- </resources>
-
- <plugins>
- <plugin>
- <artifactId>maven-jar-plugin</artifactId>
- <configuration>
- <archive>
- <manifestFile>${bundle-manifest}</manifestFile>
- </archive>
- </configuration>
- </plugin>
-
- <plugin>
- <artifactId>maven-compiler-plugin</artifactId>
- <configuration>
- <source>1.6</source>
- <target>1.6</target>
- <encoding>UTF-8</encoding>
- </configuration>
- </plugin>
-
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>clirr-maven-plugin</artifactId>
- </plugin>
- </plugins>
- </build>
-
- <reporting>
- <plugins>
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>clirr-maven-plugin</artifactId>
- <version>${clirr-version}</version>
- <configuration>
- <comparisonVersion>${jgit-last-release-version}</comparisonVersion>
- <minSeverity>info</minSeverity>
- </configuration>
- </plugin>
- </plugins>
- </reporting>
-</project>
diff --git a/org.eclipse.jgit.console/resources/org/eclipse/jgit/console/ConsoleText.properties b/org.eclipse.jgit.console/resources/org/eclipse/jgit/console/ConsoleText.properties
deleted file mode 100644
index 9aaf229..0000000
--- a/org.eclipse.jgit.console/resources/org/eclipse/jgit/console/ConsoleText.properties
+++ /dev/null
@@ -1,5 +0,0 @@
-answerNo=n
-answerYes=y
-noSystemConsoleAvailable=No System.console available
-password=Password:
-usernameFor=Username for {0}:
diff --git a/org.eclipse.jgit.http.apache/.classpath b/org.eclipse.jgit.http.apache/.classpath
index b3d21cc..a14ade4 100644
--- a/org.eclipse.jgit.http.apache/.classpath
+++ b/org.eclipse.jgit.http.apache/.classpath
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="resources"/>
diff --git a/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs
index 8b5e9d7..13a031a 100644
--- a/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.http.apache/.settings/org.eclipse.jdt.core.prefs
@@ -7,9 +7,9 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
@@ -111,7 +111,7 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
-org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.compiler.source=1.7
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
diff --git a/org.eclipse.jgit.http.apache/BUCK b/org.eclipse.jgit.http.apache/BUCK
new file mode 100644
index 0000000..f48f33a
--- /dev/null
+++ b/org.eclipse.jgit.http.apache/BUCK
@@ -0,0 +1,12 @@
+java_library(
+ name = 'http-apache',
+ srcs = glob(['src/**']),
+ resources = glob(['resources/**']),
+ deps = [
+ '//org.eclipse.jgit:jgit',
+ '//lib:commons-logging',
+ '//lib:httpcomponents',
+ '//lib:httpcore',
+ ],
+ visibility = ['PUBLIC'],
+)
diff --git a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
index cc1f543..b313515 100644
--- a/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.apache/META-INF/MANIFEST.MF
@@ -2,11 +2,13 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Bundle-SymbolicName: org.eclipse.jgit.http.apache
-Bundle-Version: 3.7.1.201504261725-r
-Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Bundle-Version: 4.2.0.201601211800-r
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Bundle-Localization: plugin
Bundle-Vendor: %Provider-Name
-Import-Package: org.apache.http;version="[4.1.0,5.0.0)",
+Bundle-ActivationPolicy: lazy
+Import-Package: org.apache.commons.logging;version="[1.1.1,2.0.0)",
+ org.apache.http;version="[4.1.0,5.0.0)",
org.apache.http.client;version="[4.1.0,5.0.0)",
org.apache.http.client.methods;version="[4.1.0,5.0.0)",
org.apache.http.client.params;version="[4.1.0,5.0.0)",
@@ -18,12 +20,13 @@ Import-Package: org.apache.http;version="[4.1.0,5.0.0)",
org.apache.http.impl.client;version="[4.1.0,5.0.0)",
org.apache.http.impl.client.cache;version="[4.1.0,5.0.0)",
org.apache.http.params;version="[4.1.0,5.0.0)",
- org.eclipse.jgit.nls;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.transport.http;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.util;version="[3.7.1,3.8.0)"
-Export-Package: org.eclipse.jgit.transport.http.apache;version="3.7.1";
+ org.eclipse.jgit.nls;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.transport.http;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.util;version="[4.2.0,4.3.0)"
+Export-Package: org.eclipse.jgit.transport.http.apache;version="4.2.0";
uses:="org.eclipse.jgit.transport.http,
javax.net.ssl,
org.apache.http.client,
org.apache.http.client.methods,
- org.apache.http"
+ org.apache.http",
+ org.eclipse.jgit.transport.http.apache.internal;x-internal:=true
diff --git a/org.eclipse.jgit.http.apache/pom.xml b/org.eclipse.jgit.http.apache/pom.xml
index 0154071..84a0717 100644
--- a/org.eclipse.jgit.http.apache/pom.xml
+++ b/org.eclipse.jgit.http.apache/pom.xml
@@ -48,7 +48,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>3.7.1.201504261725-r</version>
+ <version>4.2.0.201601211800-r</version>
</parent>
<artifactId>org.eclipse.jgit.http.apache</artifactId>
@@ -96,33 +96,92 @@
</configuration>
</plugin>
- <plugin>
- <artifactId>maven-compiler-plugin</artifactId>
- <configuration>
- <source>1.5</source>
- <target>1.5</target>
- <encoding>UTF-8</encoding>
- </configuration>
- </plugin>
-
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>clirr-maven-plugin</artifactId>
- </plugin>
+ <plugin>
+ <groupId>com.github.siom79.japicmp</groupId>
+ <artifactId>japicmp-maven-plugin</artifactId>
+ <version>${japicmp-version}</version>
+ <configuration>
+ <oldVersion>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>${project.artifactId}</artifactId>
+ <version>${jgit-last-release-version}</version>
+ </dependency>
+ </oldVersion>
+ <newVersion>
+ <file>
+ <path>${project.build.directory}/${project.artifactId}-${project.version}.jar</path>
+ </file>
+ </newVersion>
+ <parameter>
+ <onlyModified>true</onlyModified>
+ <includes>
+ <include>org.eclipse.jgit.*</include>
+ </includes>
+ <accessModifier>public</accessModifier>
+ <breakBuildOnModifications>false</breakBuildOnModifications>
+ <breakBuildOnBinaryIncompatibleModifications>false</breakBuildOnBinaryIncompatibleModifications>
+ <onlyBinaryIncompatible>false</onlyBinaryIncompatible>
+ <includeSynthetic>false</includeSynthetic>
+ <ignoreMissingClasses>false</ignoreMissingClasses>
+ <skipPomModules>true</skipPomModules>
+ </parameter>
+ <skip>false</skip>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>verify</phase>
+ <goals>
+ <goal>cmp</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
<reporting>
- <plugins>
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>clirr-maven-plugin</artifactId>
- <version>${clirr-version}</version>
- <configuration>
- <comparisonVersion>${jgit-last-release-version}</comparisonVersion>
- <minSeverity>info</minSeverity>
- </configuration>
- </plugin>
- </plugins>
- </reporting>
+ <plugins>
+ <plugin>
+ <groupId>com.github.siom79.japicmp</groupId>
+ <artifactId>japicmp-maven-plugin</artifactId>
+ <version>${japicmp-version}</version>
+ <reportSets>
+ <reportSet>
+ <reports>
+ <report>cmp-report</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ <configuration>
+ <oldVersion>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>${project.artifactId}</artifactId>
+ <version>${jgit-last-release-version}</version>
+ </dependency>
+ </oldVersion>
+ <newVersion>
+ <file>
+ <path>${project.build.directory}/${project.artifactId}-${project.version}.jar</path>
+ </file>
+ </newVersion>
+ <parameter>
+ <onlyModified>true</onlyModified>
+ <includes>
+ <include>org.eclipse.jgit.*</include>
+ </includes>
+ <accessModifier>public</accessModifier>
+ <breakBuildOnModifications>false</breakBuildOnModifications>
+ <breakBuildOnBinaryIncompatibleModifications>false</breakBuildOnBinaryIncompatibleModifications>
+ <onlyBinaryIncompatible>false</onlyBinaryIncompatible>
+ <includeSynthetic>false</includeSynthetic>
+ <ignoreMissingClasses>false</ignoreMissingClasses>
+ <skipPomModules>true</skipPomModules>
+ </parameter>
+ <skip>false</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </reporting>
</project>
diff --git a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java
index d42d6f2..de81bf8 100644
--- a/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java
+++ b/org.eclipse.jgit.http.apache/src/org/eclipse/jgit/transport/http/apache/HttpClientConnection.java
@@ -100,7 +100,7 @@ import org.eclipse.jgit.util.TemporaryBuffer.LocalFile;
public class HttpClientConnection implements HttpConnection {
HttpClient client;
- String urlStr;
+ URL url;
HttpUriRequest req;
@@ -176,16 +176,19 @@ public class HttpClientConnection implements HttpConnection {
/**
* @param urlStr
+ * @throws MalformedURLException
*/
- public HttpClientConnection(String urlStr) {
+ public HttpClientConnection(String urlStr) throws MalformedURLException {
this(urlStr, null);
}
/**
* @param urlStr
* @param proxy
+ * @throws MalformedURLException
*/
- public HttpClientConnection(String urlStr, Proxy proxy) {
+ public HttpClientConnection(String urlStr, Proxy proxy)
+ throws MalformedURLException {
this(urlStr, proxy, null);
}
@@ -193,10 +196,12 @@ public class HttpClientConnection implements HttpConnection {
* @param urlStr
* @param proxy
* @param cl
+ * @throws MalformedURLException
*/
- public HttpClientConnection(String urlStr, Proxy proxy, HttpClient cl) {
+ public HttpClientConnection(String urlStr, Proxy proxy, HttpClient cl)
+ throws MalformedURLException {
this.client = cl;
- this.urlStr = urlStr;
+ this.url = new URL(urlStr);
this.proxy = proxy;
}
@@ -206,11 +211,7 @@ public class HttpClientConnection implements HttpConnection {
}
public URL getURL() {
- try {
- return new URL(urlStr);
- } catch (MalformedURLException e) {
- return null;
- }
+ return url;
}
public String getResponseMessage() throws IOException {
@@ -250,11 +251,11 @@ public class HttpClientConnection implements HttpConnection {
public void setRequestMethod(String method) throws ProtocolException {
this.method = method;
if ("GET".equalsIgnoreCase(method)) //$NON-NLS-1$
- req = new HttpGet(urlStr);
+ req = new HttpGet(url.toString());
else if ("PUT".equalsIgnoreCase(method)) //$NON-NLS-1$
- req = new HttpPut(urlStr);
+ req = new HttpPut(url.toString());
else if ("POST".equalsIgnoreCase(method)) //$NON-NLS-1$
- req = new HttpPost(urlStr);
+ req = new HttpPost(url.toString());
else {
this.method = null;
throw new UnsupportedOperationException();
diff --git a/org.eclipse.jgit.http.server/.classpath b/org.eclipse.jgit.http.server/.classpath
index d7edf52..04a2be7 100644
--- a/org.eclipse.jgit.http.server/.classpath
+++ b/org.eclipse.jgit.http.server/.classpath
@@ -2,7 +2,7 @@
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="resources"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs
index 8b5e9d7..13a031a 100644
--- a/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.http.server/.settings/org.eclipse.jdt.core.prefs
@@ -7,9 +7,9 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
@@ -111,7 +111,7 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
-org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.compiler.source=1.7
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
diff --git a/org.eclipse.jgit.http.server/BUCK b/org.eclipse.jgit.http.server/BUCK
new file mode 100644
index 0000000..3743557
--- /dev/null
+++ b/org.eclipse.jgit.http.server/BUCK
@@ -0,0 +1,10 @@
+java_library(
+ name = 'jgit-servlet',
+ srcs = glob(['src/**']),
+ resources = glob(['resources/**']),
+ provided_deps = [
+ '//org.eclipse.jgit:jgit',
+ '//lib:servlet-api',
+ ],
+ visibility = ['PUBLIC'],
+)
diff --git a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
index af45217..e228b51 100644
--- a/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.server/META-INF/MANIFEST.MF
@@ -2,26 +2,27 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Bundle-SymbolicName: org.eclipse.jgit.http.server
-Bundle-Version: 3.7.1.201504261725-r
+Bundle-Version: 4.2.0.201601211800-r
Bundle-Localization: plugin
Bundle-Vendor: %provider_name
-Export-Package: org.eclipse.jgit.http.server;version="3.7.1",
- org.eclipse.jgit.http.server.glue;version="3.7.1";
+Export-Package: org.eclipse.jgit.http.server;version="4.2.0",
+ org.eclipse.jgit.http.server.glue;version="4.2.0";
uses:="javax.servlet,javax.servlet.http",
- org.eclipse.jgit.http.server.resolver;version="3.7.1";
+ org.eclipse.jgit.http.server.resolver;version="4.2.0";
uses:="org.eclipse.jgit.transport.resolver,
org.eclipse.jgit.lib,
org.eclipse.jgit.transport,
javax.servlet.http"
Bundle-ActivationPolicy: lazy
-Bundle-RequiredExecutionEnvironment: J2SE-1.5
-Import-Package: javax.servlet;version="[2.5.0,3.0.0)",
- javax.servlet.http;version="[2.5.0,3.0.0)",
- org.eclipse.jgit.errors;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.internal.storage.file;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.lib;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.nls;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.revwalk;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.transport;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.transport.resolver;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.util;version="[3.7.1,3.8.0)"
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
+Import-Package: javax.servlet;version="[2.5.0,3.2.0)",
+ javax.servlet.http;version="[2.5.0,3.2.0)",
+ org.eclipse.jgit.errors;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.lib;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.nls;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.revwalk;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.transport;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.util;version="[4.2.0,4.3.0)"
diff --git a/org.eclipse.jgit.http.server/pom.xml b/org.eclipse.jgit.http.server/pom.xml
index 7df85ff..b7b91d7 100644
--- a/org.eclipse.jgit.http.server/pom.xml
+++ b/org.eclipse.jgit.http.server/pom.xml
@@ -52,7 +52,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>3.7.1.201504261725-r</version>
+ <version>4.2.0.201601211800-r</version>
</parent>
<artifactId>org.eclipse.jgit.http.server</artifactId>
@@ -75,7 +75,7 @@
<dependency>
<groupId>javax.servlet</groupId>
- <artifactId>servlet-api</artifactId>
+ <artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
@@ -127,8 +127,45 @@
</plugin>
<plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>clirr-maven-plugin</artifactId>
+ <groupId>com.github.siom79.japicmp</groupId>
+ <artifactId>japicmp-maven-plugin</artifactId>
+ <version>${japicmp-version}</version>
+ <configuration>
+ <oldVersion>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>${project.artifactId}</artifactId>
+ <version>${jgit-last-release-version}</version>
+ </dependency>
+ </oldVersion>
+ <newVersion>
+ <file>
+ <path>${project.build.directory}/${project.artifactId}-${project.version}.jar</path>
+ </file>
+ </newVersion>
+ <parameter>
+ <onlyModified>true</onlyModified>
+ <includes>
+ <include>org.eclipse.jgit.*</include>
+ </includes>
+ <accessModifier>public</accessModifier>
+ <breakBuildOnModifications>false</breakBuildOnModifications>
+ <breakBuildOnBinaryIncompatibleModifications>false</breakBuildOnBinaryIncompatibleModifications>
+ <onlyBinaryIncompatible>false</onlyBinaryIncompatible>
+ <includeSynthetic>false</includeSynthetic>
+ <ignoreMissingClasses>false</ignoreMissingClasses>
+ <skipPomModules>true</skipPomModules>
+ </parameter>
+ <skip>false</skip>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>verify</phase>
+ <goals>
+ <goal>cmp</goal>
+ </goals>
+ </execution>
+ </executions>
</plugin>
</plugins>
</build>
@@ -136,13 +173,44 @@
<reporting>
<plugins>
<plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>clirr-maven-plugin</artifactId>
- <version>${clirr-version}</version>
- <configuration>
- <comparisonVersion>${jgit-last-release-version}</comparisonVersion>
- <minSeverity>info</minSeverity>
- </configuration>
+ <groupId>com.github.siom79.japicmp</groupId>
+ <artifactId>japicmp-maven-plugin</artifactId>
+ <version>${japicmp-version}</version>
+ <reportSets>
+ <reportSet>
+ <reports>
+ <report>cmp-report</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ <configuration>
+ <oldVersion>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>${project.artifactId}</artifactId>
+ <version>${jgit-last-release-version}</version>
+ </dependency>
+ </oldVersion>
+ <newVersion>
+ <file>
+ <path>${project.build.directory}/${project.artifactId}-${project.version}.jar</path>
+ </file>
+ </newVersion>
+ <parameter>
+ <onlyModified>true</onlyModified>
+ <includes>
+ <include>org.eclipse.jgit.*</include>
+ </includes>
+ <accessModifier>public</accessModifier>
+ <breakBuildOnModifications>false</breakBuildOnModifications>
+ <breakBuildOnBinaryIncompatibleModifications>false</breakBuildOnBinaryIncompatibleModifications>
+ <onlyBinaryIncompatible>false</onlyBinaryIncompatible>
+ <includeSynthetic>false</includeSynthetic>
+ <ignoreMissingClasses>false</ignoreMissingClasses>
+ <skipPomModules>true</skipPomModules>
+ </parameter>
+ <skip>false</skip>
+ </configuration>
</plugin>
</plugins>
</reporting>
diff --git a/org.eclipse.jgit.http.server/resources/org/eclipse/jgit/http/server/HttpServerText.properties b/org.eclipse.jgit.http.server/resources/org/eclipse/jgit/http/server/HttpServerText.properties
index dbc5bf7..28432b0 100644
--- a/org.eclipse.jgit.http.server/resources/org/eclipse/jgit/http/server/HttpServerText.properties
+++ b/org.eclipse.jgit.http.server/resources/org/eclipse/jgit/http/server/HttpServerText.properties
@@ -11,8 +11,8 @@ encodingNotSupportedByThisLibrary={0} "{1}": not supported by this library.
expectedRepositoryAttribute=Expected Repository attribute
filterMustNotBeNull=filter must not be null
internalServerError=Internal server error
-internalErrorDuringReceivePack=Internal error during receive-pack
-internalErrorDuringUploadPack=Internal error during upload-pack
+internalErrorDuringReceivePack=Internal error during receive-pack to {0}
+internalErrorDuringUploadPack=Internal error during upload-pack from {0}
internalServerErrorRequestAttributeWasAlreadySet=Internal server error, request attribute {0} was already set when {1} was invoked.
invalidBoolean=Invalid boolean {0} = {1}
invalidIndex=Invalid index: {0}
@@ -21,6 +21,7 @@ noResolverAvailable=No resolver available
parameterNotSet=Parameter {0} not set
pathForParamNotFound={0} (for {1}) not found
pathNotSupported={0} not supported
+receivedCorruptObject=Cannot receive {0} into {1}
repositoryAccessForbidden=Git access forbidden
repositoryNotFound=Git repository not found
servletAlreadyInitialized=Servlet already initialized
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/AsIsFileFilter.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/AsIsFileFilter.java
index b2250e3..d33362b 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/AsIsFileFilter.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/AsIsFileFilter.java
@@ -80,14 +80,16 @@ class AsIsFileFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
+ HttpServletRequest req = (HttpServletRequest) request;
+ HttpServletResponse res = (HttpServletResponse) response;
try {
final Repository db = getRepository(request);
- asIs.access((HttpServletRequest) request, db);
+ asIs.access(req, db);
chain.doFilter(request, response);
} catch (ServiceNotAuthorizedException e) {
- ((HttpServletResponse) response).sendError(SC_UNAUTHORIZED);
+ res.sendError(SC_UNAUTHORIZED, e.getMessage());
} catch (ServiceNotEnabledException e) {
- ((HttpServletResponse) response).sendError(SC_FORBIDDEN);
+ res.sendError(SC_FORBIDDEN, e.getMessage());
}
}
}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/HttpServerText.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/HttpServerText.java
index a8dd1b1..dff90a6 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/HttpServerText.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/HttpServerText.java
@@ -76,6 +76,7 @@ public class HttpServerText extends TranslationBundle {
/***/ public String parameterNotSet;
/***/ public String pathForParamNotFound;
/***/ public String pathNotSupported;
+ /***/ public String receivedCorruptObject;
/***/ public String repositoryAccessForbidden;
/***/ public String repositoryNotFound;
/***/ public String servletAlreadyInitialized;
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
index d1c2580..c88670e 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
@@ -62,6 +62,7 @@ import static org.eclipse.jgit.http.server.ServletUtils.getRepository;
import static org.eclipse.jgit.util.HttpSupport.HDR_USER_AGENT;
import java.io.IOException;
+import java.text.MessageFormat;
import java.util.List;
import javax.servlet.Filter;
@@ -74,8 +75,11 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.PackProtocolException;
import org.eclipse.jgit.errors.UnpackException;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.InternalHttpServerGlue;
import org.eclipse.jgit.transport.ReceivePack;
import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
@@ -100,6 +104,9 @@ class ReceivePackServlet extends HttpServlet {
throws IOException, ServiceNotEnabledException,
ServiceNotAuthorizedException {
ReceivePack rp = receivePackFactory.create(req, db);
+ InternalHttpServerGlue.setPeerUserAgent(
+ rp,
+ req.getHeader(HDR_USER_AGENT));
req.setAttribute(ATTRIBUTE_HANDLER, rp);
}
@@ -111,7 +118,7 @@ class ReceivePackServlet extends HttpServlet {
try {
rp.sendAdvertisedRefs(pck);
} finally {
- rp.getRevWalk().release();
+ rp.getRevWalk().close();
}
}
}
@@ -131,11 +138,10 @@ class ReceivePackServlet extends HttpServlet {
try {
rp = receivePackFactory.create(req, getRepository(req));
} catch (ServiceNotAuthorizedException e) {
- rsp.sendError(SC_UNAUTHORIZED);
+ rsp.sendError(SC_UNAUTHORIZED, e.getMessage());
return;
-
} catch (ServiceNotEnabledException e) {
- sendError(req, rsp, SC_FORBIDDEN);
+ sendError(req, rsp, SC_FORBIDDEN, e.getMessage());
return;
}
@@ -186,16 +192,23 @@ class ReceivePackServlet extends HttpServlet {
rp.receive(getInputStream(req), out, null);
out.close();
- } catch (UnpackException e) {
+ } catch (CorruptObjectException e ) {
// This should be already reported to the client.
- getServletContext().log(
- HttpServerText.get().internalErrorDuringReceivePack,
- e.getCause());
+ getServletContext().log(MessageFormat.format(
+ HttpServerText.get().receivedCorruptObject,
+ e.getMessage(),
+ ServletUtils.identify(rp.getRepository())));
+ consumeRequestBody(req);
+ out.close();
+
+ } catch (UnpackException | PackProtocolException e) {
+ // This should be already reported to the client.
+ log(rp.getRepository(), e.getCause());
consumeRequestBody(req);
out.close();
} catch (Throwable e) {
- getServletContext().log(HttpServerText.get().internalErrorDuringReceivePack, e);
+ log(rp.getRepository(), e);
if (!rsp.isCommitted()) {
rsp.reset();
sendError(req, rsp, SC_INTERNAL_SERVER_ERROR);
@@ -203,4 +216,10 @@ class ReceivePackServlet extends HttpServlet {
return;
}
}
+
+ private void log(Repository git, Throwable e) {
+ getServletContext().log(MessageFormat.format(
+ HttpServerText.get().internalErrorDuringReceivePack,
+ ServletUtils.identify(git)), e);
+ }
}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/RepositoryFilter.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/RepositoryFilter.java
index 5840402..a021c1f 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/RepositoryFilter.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/RepositoryFilter.java
@@ -137,10 +137,10 @@ public class RepositoryFilter implements Filter {
sendError(req, res, SC_NOT_FOUND);
return;
} catch (ServiceNotEnabledException e) {
- sendError(req, res, SC_FORBIDDEN);
+ sendError(req, res, SC_FORBIDDEN, e.getMessage());
return;
} catch (ServiceNotAuthorizedException e) {
- res.sendError(SC_UNAUTHORIZED);
+ res.sendError(SC_UNAUTHORIZED, e.getMessage());
return;
} catch (ServiceMayNotContinueException e) {
sendError(req, res, SC_FORBIDDEN, e.getMessage());
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java
index 8d56d84..042ccf3 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java
@@ -62,6 +62,7 @@ import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jgit.internal.storage.dfs.DfsRepository;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
@@ -273,6 +274,15 @@ public final class ServletUtils {
return ObjectId.fromRaw(md.digest()).getName();
}
+ static String identify(Repository git) {
+ if (git instanceof DfsRepository) {
+ return ((DfsRepository) git).getDescription().getRepositoryName();
+ } else if (git.getDirectory() != null) {
+ return git.getDirectory().getPath();
+ }
+ return "unknown";
+ }
+
private ServletUtils() {
// static utility class only
}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java
index 4810753..7d4f21b 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java
@@ -98,10 +98,10 @@ abstract class SmartServiceInfoRefs implements Filter {
try {
begin(req, db);
} catch (ServiceNotAuthorizedException e) {
- res.sendError(SC_UNAUTHORIZED);
+ res.sendError(SC_UNAUTHORIZED, e.getMessage());
return;
} catch (ServiceNotEnabledException e) {
- sendError(req, res, SC_FORBIDDEN);
+ sendError(req, res, SC_FORBIDDEN, e.getMessage());
return;
}
@@ -132,11 +132,9 @@ abstract class SmartServiceInfoRefs implements Filter {
advertise(req, new PacketLineOutRefAdvertiser(out));
buf.close();
} catch (ServiceNotAuthorizedException e) {
- res.sendError(SC_UNAUTHORIZED);
-
+ res.sendError(SC_UNAUTHORIZED, e.getMessage());
} catch (ServiceNotEnabledException e) {
- sendError(req, res, SC_FORBIDDEN);
-
+ sendError(req, res, SC_FORBIDDEN, e.getMessage());
} catch (ServiceMayNotContinueException e) {
if (e.isOutput())
buf.close();
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java
index c5272b5..8c27b71 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java
@@ -61,6 +61,7 @@ import static org.eclipse.jgit.http.server.ServletUtils.getRepository;
import static org.eclipse.jgit.util.HttpSupport.HDR_USER_AGENT;
import java.io.IOException;
+import java.text.MessageFormat;
import java.util.List;
import javax.servlet.Filter;
@@ -74,10 +75,11 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.InternalHttpServerGlue;
import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
+import org.eclipse.jgit.transport.ServiceMayNotContinueException;
import org.eclipse.jgit.transport.UploadPack;
import org.eclipse.jgit.transport.UploadPackInternalServerErrorException;
-import org.eclipse.jgit.transport.ServiceMayNotContinueException;
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
import org.eclipse.jgit.transport.resolver.UploadPackFactory;
@@ -100,6 +102,9 @@ class UploadPackServlet extends HttpServlet {
throws IOException, ServiceNotEnabledException,
ServiceNotAuthorizedException {
UploadPack up = uploadPackFactory.create(req, db);
+ InternalHttpServerGlue.setPeerUserAgent(
+ up,
+ req.getHeader(HDR_USER_AGENT));
req.setAttribute(ATTRIBUTE_HANDLER, up);
}
@@ -112,7 +117,7 @@ class UploadPackServlet extends HttpServlet {
up.setBiDirectionalPipe(false);
up.sendAdvertisedRefs(pck);
} finally {
- up.getRevWalk().release();
+ up.getRevWalk().close();
}
}
}
@@ -132,11 +137,10 @@ class UploadPackServlet extends HttpServlet {
try {
rp = uploadPackFactory.create(req, getRepository(req));
} catch (ServiceNotAuthorizedException e) {
- rsp.sendError(SC_UNAUTHORIZED);
+ rsp.sendError(SC_UNAUTHORIZED, e.getMessage());
return;
-
} catch (ServiceNotEnabledException e) {
- sendError(req, rsp, SC_FORBIDDEN);
+ sendError(req, rsp, SC_FORBIDDEN, e.getMessage());
return;
}
@@ -199,14 +203,12 @@ class UploadPackServlet extends HttpServlet {
} catch (UploadPackInternalServerErrorException e) {
// Special case exception, error message was sent to client.
- getServletContext().log(
- HttpServerText.get().internalErrorDuringUploadPack,
- e.getCause());
+ log(up.getRepository(), e.getCause());
consumeRequestBody(req);
out.close();
} catch (Throwable e) {
- getServletContext().log(HttpServerText.get().internalErrorDuringUploadPack, e);
+ log(up.getRepository(), e);
if (!rsp.isCommitted()) {
rsp.reset();
sendError(req, rsp, SC_INTERNAL_SERVER_ERROR);
@@ -214,4 +216,10 @@ class UploadPackServlet extends HttpServlet {
return;
}
}
+
+ private void log(Repository git, Throwable e) {
+ getServletContext().log(MessageFormat.format(
+ HttpServerText.get().internalErrorDuringUploadPack,
+ ServletUtils.identify(git)), e);
+ }
}
diff --git a/org.eclipse.jgit.http.test/.classpath b/org.eclipse.jgit.http.test/.classpath
index db269cf..2fdcc94 100644
--- a/org.eclipse.jgit.http.test/.classpath
+++ b/org.eclipse.jgit.http.test/.classpath
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="tst"/>
<classpathentry kind="output" path="bin"/>
diff --git a/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs
index 5fb6234..dcc0d3a 100644
--- a/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.http.test/.settings/org.eclipse.jdt.core.prefs
@@ -7,9 +7,9 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
@@ -111,7 +111,7 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
-org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.compiler.source=1.7
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
diff --git a/org.eclipse.jgit.http.test/BUCK b/org.eclipse.jgit.http.test/BUCK
new file mode 100644
index 0000000..d2ced7a
--- /dev/null
+++ b/org.eclipse.jgit.http.test/BUCK
@@ -0,0 +1,40 @@
+TESTS = glob(['tst/**/*.java'])
+
+for t in TESTS:
+ n = t[len('tst/'):len(t)-len('.java')].replace('/', '.')
+ java_test(
+ name = n,
+ labels = ['http'],
+ srcs = [t],
+ deps = [
+ ':helpers',
+ '//org.eclipse.jgit:jgit',
+ '//org.eclipse.jgit.http.apache:http-apache',
+ '//org.eclipse.jgit.http.server:jgit-servlet',
+ '//org.eclipse.jgit.junit:junit',
+ '//org.eclipse.jgit.junit.http:junit-http',
+ '//lib:hamcrest-core',
+ '//lib:hamcrest-library',
+ '//lib:junit',
+ '//lib:servlet-api',
+ '//lib/jetty:http',
+ '//lib/jetty:io',
+ '//lib/jetty:server',
+ '//lib/jetty:servlet',
+ '//lib/jetty:security',
+ '//lib/jetty:util',
+ ],
+ source_under_test = ['//org.eclipse.jgit.http.server:jgit-servlet'],
+ )
+
+java_library(
+ name = 'helpers',
+ srcs = glob(['src/**/*.java']),
+ deps = [
+ '//org.eclipse.jgit:jgit',
+ '//org.eclipse.jgit.http.server:jgit-servlet',
+ '//org.eclipse.jgit.junit:junit',
+ '//org.eclipse.jgit.junit.http:junit-http',
+ '//lib:junit',
+ ],
+)
diff --git a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
index bd98ed3..a80f210 100644
--- a/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.http.test/META-INF/MANIFEST.MF
@@ -2,43 +2,43 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Bundle-SymbolicName: org.eclipse.jgit.http.test
-Bundle-Version: 3.7.1.201504261725-r
+Bundle-Version: 4.2.0.201601211800-r
Bundle-Vendor: %provider_name
Bundle-Localization: plugin
-Bundle-RequiredExecutionEnvironment: J2SE-1.5
-Import-Package: javax.servlet;version="[2.5.0,3.0.0)",
- javax.servlet.http;version="[2.5.0,3.0.0)",
- org.eclipse.jetty.continuation;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.http;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.io;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.security;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.security.authentication;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.server;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.server.handler;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.server.nio;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.servlet;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.util;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.util.component;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.util.log;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.util.security;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.util.thread;version="[7.6.0,8.0.0)",
- org.eclipse.jgit.errors;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.http.server;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.http.server.glue;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.http.server.resolver;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.internal;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.internal.storage.file;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.junit;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.junit.http;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.lib;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.nls;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.revwalk;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.storage.file;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.transport;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.transport.http;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.transport.http.apache;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.transport.resolver;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.util;version="[3.7.1,3.8.0)",
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
+Import-Package: javax.servlet;version="[2.5.0,3.2.0)",
+ javax.servlet.http;version="[2.5.0,3.2.0)",
+ org.eclipse.jetty.continuation;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.http;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.io;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.security;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.security.authentication;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.server;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.server.handler;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.server.nio;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.servlet;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.util;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.util.component;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.util.log;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.util.security;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.util.thread;version="[9.0.0,10.0.0)",
+ org.eclipse.jgit.errors;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.http.server;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.http.server.glue;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.http.server.resolver;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.internal;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.junit;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.junit.http;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.lib;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.nls;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.revwalk;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.storage.file;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.transport;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.transport.http;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.transport.http.apache;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.util;version="[4.2.0,4.3.0)",
org.hamcrest.core;version="[1.1.0,2.0.0)",
org.junit;version="[4.0.0,5.0.0)",
org.junit.runner;version="[4.0.0,5.0.0)",
diff --git a/org.eclipse.jgit.http.test/pom.xml b/org.eclipse.jgit.http.test/pom.xml
index 5a03cc8..4bd1fe3 100644
--- a/org.eclipse.jgit.http.test/pom.xml
+++ b/org.eclipse.jgit.http.test/pom.xml
@@ -51,7 +51,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>3.7.1.201504261725-r</version>
+ <version>4.2.0.201601211800-r</version>
</parent>
<artifactId>org.eclipse.jgit.http.test</artifactId>
@@ -134,6 +134,10 @@
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Djava.io.tmpdir=${project.build.directory} -Xmx300m</argLine>
+ <includes>
+ <include>**/*Test.java</include>
+ <include>**/*Tests.java</include>
+ </includes>
</configuration>
</plugin>
</plugins>
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AsIsServiceTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AsIsServiceTest.java
index a86ae09..c6b8f09 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AsIsServiceTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/AsIsServiceTest.java
@@ -133,7 +133,7 @@ public class AsIsServiceTest extends LocalDiskRepositoryTestCase {
private final String host;
R(final String user, final String host) {
- super(new Request() /* can't pass null, sigh */);
+ super(new Request(null, null) /* can't pass null, sigh */);
this.user = user;
this.host = host;
}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultReceivePackFactoryTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultReceivePackFactoryTest.java
index 04f7c52..772865d 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultReceivePackFactoryTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultReceivePackFactoryTest.java
@@ -204,7 +204,7 @@ public class DefaultReceivePackFactoryTest extends LocalDiskRepositoryTestCase {
private final String host;
R(final String user, final String host) {
- super(new Request() /* can't pass null, sigh */);
+ super(new Request(null, null) /* can't pass null, sigh */);
this.user = user;
this.host = host;
}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultUploadPackFactoryTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultUploadPackFactoryTest.java
index bc13703..c9d43cd 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultUploadPackFactoryTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DefaultUploadPackFactoryTest.java
@@ -160,7 +160,7 @@ public class DefaultUploadPackFactoryTest extends LocalDiskRepositoryTestCase {
private final String host;
R(final String user, final String host) {
- super(new Request() /* can't pass null, sigh */);
+ super(new Request(null, null) /* can't pass null, sigh */);
this.user = user;
this.host = host;
}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java
index dec9b59..677132d 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java
@@ -140,8 +140,7 @@ public class DumbClientDumbServerTest extends HttpTestCase {
assertEquals("http", remoteURI.getScheme());
Map<String, Ref> map;
- Transport t = Transport.open(dst, remoteURI);
- try {
+ try (Transport t = Transport.open(dst, remoteURI)) {
// I didn't make up these public interface names, I just
// approved them for inclusion into the code base. Sorry.
// --spearce
@@ -149,14 +148,9 @@ public class DumbClientDumbServerTest extends HttpTestCase {
assertTrue("isa TransportHttp", t instanceof TransportHttp);
assertTrue("isa HttpTransport", t instanceof HttpTransport);
- FetchConnection c = t.openFetch();
- try {
+ try (FetchConnection c = t.openFetch()) {
map = c.getRefsMap();
- } finally {
- c.close();
}
- } finally {
- t.close();
}
assertNotNull("have map of refs", map);
@@ -201,15 +195,12 @@ public class DumbClientDumbServerTest extends HttpTestCase {
Repository dst = createBareRepository();
assertFalse(dst.hasObject(A_txt));
- Transport t = Transport.open(dst, remoteURI);
- try {
+ try (Transport t = Transport.open(dst, remoteURI)) {
t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
- } finally {
- t.close();
}
assertTrue(dst.hasObject(A_txt));
- assertEquals(B, dst.getRef(master).getObjectId());
+ assertEquals(B, dst.exactRef(master).getObjectId());
fsck(dst, B);
List<AccessEvent> loose = getRequests(loose(remoteURI, A_txt));
@@ -226,15 +217,12 @@ public class DumbClientDumbServerTest extends HttpTestCase {
Repository dst = createBareRepository();
assertFalse(dst.hasObject(A_txt));
- Transport t = Transport.open(dst, remoteURI);
- try {
+ try (Transport t = Transport.open(dst, remoteURI)) {
t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
- } finally {
- t.close();
}
assertTrue(dst.hasObject(A_txt));
- assertEquals(B, dst.getRef(master).getObjectId());
+ assertEquals(B, dst.exactRef(master).getObjectId());
fsck(dst, B);
List<AccessEvent> req;
@@ -265,8 +253,7 @@ public class DumbClientDumbServerTest extends HttpTestCase {
final RevCommit Q = src.commit().create();
final Repository db = src.getRepository();
- Transport t = Transport.open(db, remoteURI);
- try {
+ try (Transport t = Transport.open(db, remoteURI)) {
try {
t.push(NullProgressMonitor.INSTANCE, push(src, Q));
fail("push incorrectly completed against a dumb server");
@@ -274,8 +261,6 @@ public class DumbClientDumbServerTest extends HttpTestCase {
String exp = "remote does not support smart HTTP push";
assertEquals(exp, nse.getMessage());
}
- } finally {
- t.close();
}
}
}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java
index 7b4270f..da3a098 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java
@@ -199,7 +199,8 @@ public class DumbClientSmartServerTest extends HttpTestCase {
.startsWith("JGit/"));
assertEquals("*/*", info.getRequestHeader(HDR_ACCEPT));
assertEquals(200, info.getStatus());
- assertEquals("text/plain;charset=UTF-8", info
+ assertEquals("text/plain; charset=UTF-8",
+ info
.getResponseHeader(HDR_CONTENT_TYPE));
AccessEvent head = requests.get(1);
@@ -224,7 +225,7 @@ public class DumbClientSmartServerTest extends HttpTestCase {
}
assertTrue(dst.hasObject(A_txt));
- assertEquals(B, dst.getRef(master).getObjectId());
+ assertEquals(B, dst.exactRef(master).getObjectId());
fsck(dst, B);
List<AccessEvent> loose = getRequests(loose(remoteURI, A_txt));
@@ -252,7 +253,7 @@ public class DumbClientSmartServerTest extends HttpTestCase {
}
assertTrue(dst.hasObject(A_txt));
- assertEquals(B, dst.getRef(master).getObjectId());
+ assertEquals(B, dst.exactRef(master).getObjectId());
fsck(dst, B);
List<AccessEvent> req;
@@ -268,7 +269,8 @@ public class DumbClientSmartServerTest extends HttpTestCase {
assertEquals("GET", req.get(0).getMethod());
assertEquals(0, req.get(0).getParameters().size());
assertEquals(200, req.get(0).getStatus());
- assertEquals("text/plain;charset=UTF-8", req.get(0).getResponseHeader(
+ assertEquals("text/plain; charset=UTF-8",
+ req.get(0).getResponseHeader(
HDR_CONTENT_TYPE));
}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletResponseTests.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletResponseTests.java
new file mode 100644
index 0000000..4b15d4b
--- /dev/null
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/GitServletResponseTests.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2015, christian.Halstrick <christian.halstrick at sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.http.test;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.errors.TooLargePackException;
+import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.http.server.GitServlet;
+import org.eclipse.jgit.http.server.resolver.DefaultReceivePackFactory;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.junit.http.HttpTestCase;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectChecker;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.transport.PostReceiveHook;
+import org.eclipse.jgit.transport.PreReceiveHook;
+import org.eclipse.jgit.transport.ReceiveCommand;
+import org.eclipse.jgit.transport.ReceivePack;
+import org.eclipse.jgit.transport.RemoteRefUpdate;
+import org.eclipse.jgit.transport.Transport;
+import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.transport.resolver.RepositoryResolver;
+import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
+import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for correct responses of {@link GitServlet}. Especially error
+ * situations where the {@link GitServlet} faces exceptions during request
+ * processing are tested
+ */
+public class GitServletResponseTests extends HttpTestCase {
+ private Repository srvRepo;
+
+ private URIish srvURI;
+
+ private GitServlet gs;
+
+ private long maxPackSize = 0; // the maximum pack file size used by
+ // the server
+
+ private PostReceiveHook postHook = null;
+
+ private PreReceiveHook preHook = null;
+
+ private ObjectChecker oc = null;
+
+ /**
+ * Setup a http server using {@link GitServlet}. Tests should be able to
+ * configure the maximum pack file size, the object checker and custom hooks
+ * just before they talk to the server.
+ */
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ final TestRepository<Repository> srv = createTestRepository();
+ final String repoName = srv.getRepository().getDirectory().getName();
+
+ ServletContextHandler app = server.addContext("/git");
+ gs = new GitServlet();
+ gs.setRepositoryResolver(new RepositoryResolver<HttpServletRequest>() {
+ public Repository open(HttpServletRequest req, String name)
+ throws RepositoryNotFoundException,
+ ServiceNotEnabledException {
+ if (!name.equals(repoName))
+ throw new RepositoryNotFoundException(name);
+
+ final Repository db = srv.getRepository();
+ db.incrementOpen();
+ return db;
+ }
+ });
+ gs.setReceivePackFactory(new DefaultReceivePackFactory() {
+ public ReceivePack create(HttpServletRequest req, Repository db)
+ throws ServiceNotEnabledException,
+ ServiceNotAuthorizedException {
+ ReceivePack recv = super.create(req, db);
+ if (maxPackSize > 0)
+ recv.setMaxPackSizeLimit(maxPackSize);
+ if (postHook != null)
+ recv.setPostReceiveHook(postHook);
+ if (preHook != null)
+ recv.setPreReceiveHook(preHook);
+ if (oc != null)
+ recv.setObjectChecker(oc);
+ return recv;
+ }
+
+ });
+ app.addServlet(new ServletHolder(gs), "/*");
+
+ server.setUp();
+
+ srvRepo = srv.getRepository();
+ srvURI = toURIish(app, repoName);
+
+ StoredConfig cfg = srvRepo.getConfig();
+ cfg.setBoolean("http", null, "receivepack", true);
+ cfg.save();
+ }
+
+ /**
+ * Configure a {@link GitServlet} that faces a {@link IllegalStateException}
+ * during executing preReceiveHooks. This used to lead to exceptions with a
+ * description of "invalid channel 101" on the client side. Make sure
+ * clients receive the correct response on the correct sideband.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testRuntimeExceptionInPreReceiveHook() throws Exception {
+ final TestRepository client = createTestRepository();
+ final RevBlob Q_txt = client
+ .blob("some blob content to measure pack size");
+ final RevCommit Q = client.commit().add("Q", Q_txt).create();
+ final Repository clientRepo = client.getRepository();
+ final String srvBranchName = Constants.R_HEADS + "new.branch";
+ Transport t;
+
+ maxPackSize = 0;
+ postHook = null;
+ preHook = new PreReceiveHook() {
+ @Override
+ public void onPreReceive(ReceivePack rp,
+ Collection<ReceiveCommand> commands) {
+ throw new IllegalStateException();
+ }
+ };
+
+ t = Transport.open(clientRepo, srvURI);
+ try {
+ RemoteRefUpdate update = new RemoteRefUpdate(clientRepo, Q.name(),
+ srvBranchName, false, null, null);
+ try {
+ t.push(NullProgressMonitor.INSTANCE,
+ Collections.singleton(update));
+ fail("should not reach this line");
+ } catch (Exception e) {
+ assertTrue(e instanceof TransportException);
+ }
+ } finally {
+ t.close();
+ }
+ }
+
+ /**
+ * Configure a {@link GitServlet} that faces a {@link IllegalStateException}
+ * during executing objectChecking.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testObjectCheckerException() throws Exception {
+ final TestRepository client = createTestRepository();
+ final RevBlob Q_txt = client
+ .blob("some blob content to measure pack size");
+ final RevCommit Q = client.commit().add("Q", Q_txt).create();
+ final Repository clientRepo = client.getRepository();
+ final String srvBranchName = Constants.R_HEADS + "new.branch";
+ Transport t;
+
+ maxPackSize = 0;
+ postHook = null;
+ preHook = null;
+ oc = new ObjectChecker() {
+ @Override
+ public void checkCommit(AnyObjectId id, byte[] raw)
+ throws CorruptObjectException {
+ throw new CorruptObjectException("refusing all commits");
+ }
+ };
+
+ t = Transport.open(clientRepo, srvURI);
+ try {
+ RemoteRefUpdate update = new RemoteRefUpdate(clientRepo, Q.name(),
+ srvBranchName, false, null, null);
+ try {
+ t.push(NullProgressMonitor.INSTANCE,
+ Collections.singleton(update));
+ fail("should not reach this line");
+ } catch (Exception e) {
+ assertTrue(e instanceof TransportException);
+ }
+ } finally {
+ t.close();
+ }
+ }
+
+ /**
+ * Configure a {@link GitServlet} that faces a {@link TooLargePackException}
+ * during persisting the pack and a {@link IllegalStateException} during
+ * executing postReceiveHooks. This used to lead to exceptions with a
+ * description of "invalid channel 101" on the client side. Make sure
+ * clients receive the correct response about the too large pack on the
+ * correct sideband.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testUnpackErrorWithSubsequentExceptionInPostReceiveHook()
+ throws Exception {
+ final TestRepository client = createTestRepository();
+ final RevBlob Q_txt = client
+ .blob("some blob content to measure pack size");
+ final RevCommit Q = client.commit().add("Q", Q_txt).create();
+ final Repository clientRepo = client.getRepository();
+ final String srvBranchName = Constants.R_HEADS + "new.branch";
+ Transport t;
+
+ // this maxPackSize leads to an unPackError
+ maxPackSize = 400;
+ // this PostReceiveHook when called after an unsuccesfull unpack will
+ // lead to an IllegalStateException
+ postHook = new PostReceiveHook() {
+ public void onPostReceive(ReceivePack rp,
+ Collection<ReceiveCommand> commands) {
+ // the maxPackSize setting caused that the packfile couldn't be
+ // saved to disk. Calling getPackSize() now will lead to a
+ // IllegalStateException.
+ rp.getPackSize();
+ }
+ };
+
+ t = Transport.open(clientRepo, srvURI);
+ try {
+ RemoteRefUpdate update = new RemoteRefUpdate(clientRepo, Q.name(),
+ srvBranchName, false, null, null);
+ try {
+ t.push(NullProgressMonitor.INSTANCE,
+ Collections.singleton(update));
+ fail("should not reach this line");
+ } catch (Exception e) {
+ assertTrue(e instanceof TooLargePackException);
+ }
+ } finally {
+ t.close();
+ }
+ }
+}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
index f1056f2..d67c817 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
@@ -164,8 +164,8 @@ public class HookMessageTest extends HttpTestCase {
}
assertTrue(remoteRepository.hasObject(Q_txt));
- assertNotNull("has " + dstName, remoteRepository.getRef(dstName));
- assertEquals(Q, remoteRepository.getRef(dstName).getObjectId());
+ assertNotNull("has " + dstName, remoteRepository.exactRef(dstName));
+ assertEquals(Q, remoteRepository.exactRef(dstName).getObjectId());
fsck(remoteRepository, Q);
List<AccessEvent> requests = getRequests();
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
index 6fb1302..ce78442 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
@@ -157,8 +157,7 @@ public class HttpClientTests extends HttpTestCase {
public void testRepositoryNotFound_Dumb() throws Exception {
URIish uri = toURIish("/dumb.none/not-found");
Repository dst = createBareRepository();
- Transport t = Transport.open(dst, uri);
- try {
+ try (Transport t = Transport.open(dst, uri)) {
try {
t.openFetch();
fail("connection opened to not found repository");
@@ -167,8 +166,6 @@ public class HttpClientTests extends HttpTestCase {
+ "/info/refs?service=git-upload-pack not found";
assertEquals(exp, err.getMessage());
}
- } finally {
- t.close();
}
}
@@ -176,8 +173,7 @@ public class HttpClientTests extends HttpTestCase {
public void testRepositoryNotFound_Smart() throws Exception {
URIish uri = toURIish("/smart.none/not-found");
Repository dst = createBareRepository();
- Transport t = Transport.open(dst, uri);
- try {
+ try (Transport t = Transport.open(dst, uri)) {
try {
t.openFetch();
fail("connection opened to not found repository");
@@ -186,8 +182,6 @@ public class HttpClientTests extends HttpTestCase {
+ "/info/refs?service=git-upload-pack not found";
assertEquals(exp, err.getMessage());
}
- } finally {
- t.close();
}
}
@@ -201,16 +195,9 @@ public class HttpClientTests extends HttpTestCase {
Repository dst = createBareRepository();
Ref head;
- Transport t = Transport.open(dst, dumbAuthNoneURI);
- try {
- FetchConnection c = t.openFetch();
- try {
- head = c.getRef(Constants.HEAD);
- } finally {
- c.close();
- }
- } finally {
- t.close();
+ try (Transport t = Transport.open(dst, dumbAuthNoneURI);
+ FetchConnection c = t.openFetch()) {
+ head = c.getRef(Constants.HEAD);
}
assertNotNull("has " + Constants.HEAD, head);
assertEquals(Q, head.getObjectId());
@@ -225,16 +212,9 @@ public class HttpClientTests extends HttpTestCase {
Repository dst = createBareRepository();
Ref head;
- Transport t = Transport.open(dst, dumbAuthNoneURI);
- try {
- FetchConnection c = t.openFetch();
- try {
- head = c.getRef(Constants.HEAD);
- } finally {
- c.close();
- }
- } finally {
- t.close();
+ try (Transport t = Transport.open(dst, dumbAuthNoneURI);
+ FetchConnection c = t.openFetch()) {
+ head = c.getRef(Constants.HEAD);
}
assertNull("has no " + Constants.HEAD, head);
}
@@ -249,16 +229,9 @@ public class HttpClientTests extends HttpTestCase {
Repository dst = createBareRepository();
Ref head;
- Transport t = Transport.open(dst, smartAuthNoneURI);
- try {
- FetchConnection c = t.openFetch();
- try {
- head = c.getRef(Constants.HEAD);
- } finally {
- c.close();
- }
- } finally {
- t.close();
+ try (Transport t = Transport.open(dst, smartAuthNoneURI);
+ FetchConnection c = t.openFetch()) {
+ head = c.getRef(Constants.HEAD);
}
assertNotNull("has " + Constants.HEAD, head);
assertEquals(Q, head.getObjectId());
@@ -268,16 +241,13 @@ public class HttpClientTests extends HttpTestCase {
public void testListRemote_Smart_WithQueryParameters() throws Exception {
URIish myURI = toURIish("/snone/do?r=1&p=test.git");
Repository dst = createBareRepository();
- Transport t = Transport.open(dst, myURI);
- try {
+ try (Transport t = Transport.open(dst, myURI)) {
try {
t.openFetch();
fail("test did not fail to find repository as expected");
} catch (NoRemoteRepositoryException err) {
// expected
}
- } finally {
- t.close();
}
List<AccessEvent> requests = getRequests();
@@ -296,62 +266,52 @@ public class HttpClientTests extends HttpTestCase {
@Test
public void testListRemote_Dumb_NeedsAuth() throws Exception {
Repository dst = createBareRepository();
- Transport t = Transport.open(dst, dumbAuthBasicURI);
- try {
+ try (Transport t = Transport.open(dst, dumbAuthBasicURI)) {
try {
t.openFetch();
fail("connection opened even info/refs needs auth basic");
} catch (TransportException err) {
String exp = dumbAuthBasicURI + ": "
- + JGitText.get().notAuthorized;
+ + JGitText.get().noCredentialsProvider;
assertEquals(exp, err.getMessage());
}
- } finally {
- t.close();
}
}
@Test
public void testListRemote_Dumb_Auth() throws Exception {
Repository dst = createBareRepository();
- Transport t = Transport.open(dst, dumbAuthBasicURI);
- t.setCredentialsProvider(new UsernamePasswordCredentialsProvider(
- AppServer.username, AppServer.password));
- try {
- t.openFetch();
- } finally {
- t.close();
+ try (Transport t = Transport.open(dst, dumbAuthBasicURI)) {
+ t.setCredentialsProvider(new UsernamePasswordCredentialsProvider(
+ AppServer.username, AppServer.password));
+ t.openFetch().close();
}
- t = Transport.open(dst, dumbAuthBasicURI);
- t.setCredentialsProvider(new UsernamePasswordCredentialsProvider(
- AppServer.username, ""));
- try {
- t.openFetch();
- fail("connection opened even info/refs needs auth basic and we provide wrong password");
- } catch (TransportException err) {
- String exp = dumbAuthBasicURI + ": "
- + JGitText.get().notAuthorized;
- assertEquals(exp, err.getMessage());
- } finally {
- t.close();
+ try (Transport t = Transport.open(dst, dumbAuthBasicURI)) {
+ t.setCredentialsProvider(new UsernamePasswordCredentialsProvider(
+ AppServer.username, ""));
+ try {
+ t.openFetch();
+ fail("connection opened even info/refs needs auth basic and we provide wrong password");
+ } catch (TransportException err) {
+ String exp = dumbAuthBasicURI + ": "
+ + JGitText.get().notAuthorized;
+ assertEquals(exp, err.getMessage());
+ }
}
}
@Test
public void testListRemote_Smart_UploadPackNeedsAuth() throws Exception {
Repository dst = createBareRepository();
- Transport t = Transport.open(dst, smartAuthBasicURI);
- try {
+ try (Transport t = Transport.open(dst, smartAuthBasicURI)) {
try {
t.openFetch();
fail("connection opened even though service disabled");
} catch (TransportException err) {
String exp = smartAuthBasicURI + ": "
- + JGitText.get().notAuthorized;
+ + JGitText.get().noCredentialsProvider;
assertEquals(exp, err.getMessage());
}
- } finally {
- t.close();
}
}
@@ -363,33 +323,24 @@ public class HttpClientTests extends HttpTestCase {
cfg.save();
Repository dst = createBareRepository();
- Transport t = Transport.open(dst, smartAuthNoneURI);
- try {
+ try (Transport t = Transport.open(dst, smartAuthNoneURI)) {
try {
t.openFetch();
fail("connection opened even though service disabled");
} catch (TransportException err) {
- String exp = smartAuthNoneURI + ": Git access forbidden";
+ String exp = smartAuthNoneURI + ": "
+ + JGitText.get().serviceNotEnabledNoName;
assertEquals(exp, err.getMessage());
}
- } finally {
- t.close();
}
}
@Test
public void testListRemoteWithoutLocalRepository() throws Exception {
- Transport t = Transport.open(smartAuthNoneURI);
- try {
- FetchConnection c = t.openFetch();
- try {
- Ref head = c.getRef(Constants.HEAD);
- assertNotNull(head);
- } finally {
- c.close();
- }
- } finally {
- t.close();
+ try (Transport t = Transport.open(smartAuthNoneURI);
+ FetchConnection c = t.openFetch()) {
+ Ref head = c.getRef(Constants.HEAD);
+ assertNotNull(head);
}
}
}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
index 4e75a56..82861ed 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
@@ -59,9 +59,11 @@ import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.EnumSet;
import java.util.List;
import java.util.Map;
+import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
@@ -72,7 +74,6 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.servlet.FilterHolder;
-import org.eclipse.jetty.servlet.FilterMapping;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jgit.errors.RemoteRepositoryException;
@@ -185,7 +186,8 @@ public class SmartClientSmartServerTest extends HttpTestCase {
public void destroy() {
//
}
- }), "/" + srcName + "/git-upload-pack", FilterMapping.DEFAULT);
+ }), "/" + srcName + "/git-upload-pack",
+ EnumSet.of(DispatcherType.REQUEST));
broken.addServlet(new ServletHolder(gs), "/*");
server.setUp();
@@ -209,8 +211,7 @@ public class SmartClientSmartServerTest extends HttpTestCase {
assertEquals("http", remoteURI.getScheme());
Map<String, Ref> map;
- Transport t = Transport.open(dst, remoteURI);
- try {
+ try (Transport t = Transport.open(dst, remoteURI)) {
// I didn't make up these public interface names, I just
// approved them for inclusion into the code base. Sorry.
// --spearce
@@ -224,8 +225,6 @@ public class SmartClientSmartServerTest extends HttpTestCase {
} finally {
c.close();
}
- } finally {
- t.close();
}
assertNotNull("have map of refs", map);
@@ -255,8 +254,7 @@ public class SmartClientSmartServerTest extends HttpTestCase {
public void testListRemote_BadName() throws IOException, URISyntaxException {
Repository dst = createBareRepository();
URIish uri = new URIish(this.remoteURI.toString() + ".invalid");
- Transport t = Transport.open(dst, uri);
- try {
+ try (Transport t = Transport.open(dst, uri)) {
try {
t.openFetch();
fail("fetch connection opened");
@@ -264,8 +262,6 @@ public class SmartClientSmartServerTest extends HttpTestCase {
assertEquals(uri + ": Git repository not found",
notFound.getMessage());
}
- } finally {
- t.close();
}
List<AccessEvent> requests = getRequests();
@@ -286,15 +282,12 @@ public class SmartClientSmartServerTest extends HttpTestCase {
Repository dst = createBareRepository();
assertFalse(dst.hasObject(A_txt));
- Transport t = Transport.open(dst, remoteURI);
- try {
+ try (Transport t = Transport.open(dst, remoteURI)) {
t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
- } finally {
- t.close();
}
assertTrue(dst.hasObject(A_txt));
- assertEquals(B, dst.getRef(master).getObjectId());
+ assertEquals(B, dst.exactRef(master).getObjectId());
fsck(dst, B);
List<AccessEvent> requests = getRequests();
@@ -329,13 +322,10 @@ public class SmartClientSmartServerTest extends HttpTestCase {
// Bootstrap by doing the clone.
//
TestRepository dst = createTestRepository();
- Transport t = Transport.open(dst.getRepository(), remoteURI);
- try {
+ try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
- } finally {
- t.close();
}
- assertEquals(B, dst.getRepository().getRef(master).getObjectId());
+ assertEquals(B, dst.getRepository().exactRef(master).getObjectId());
List<AccessEvent> cloneRequests = getRequests();
// Only create a few new commits.
@@ -350,13 +340,10 @@ public class SmartClientSmartServerTest extends HttpTestCase {
// Now incrementally update.
//
- t = Transport.open(dst.getRepository(), remoteURI);
- try {
+ try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
- } finally {
- t.close();
}
- assertEquals(Z, dst.getRepository().getRef(master).getObjectId());
+ assertEquals(Z, dst.getRepository().exactRef(master).getObjectId());
List<AccessEvent> requests = getRequests();
requests.removeAll(cloneRequests);
@@ -392,13 +379,10 @@ public class SmartClientSmartServerTest extends HttpTestCase {
// Bootstrap by doing the clone.
//
TestRepository dst = createTestRepository();
- Transport t = Transport.open(dst.getRepository(), remoteURI);
- try {
+ try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
- } finally {
- t.close();
}
- assertEquals(B, dst.getRepository().getRef(master).getObjectId());
+ assertEquals(B, dst.getRepository().exactRef(master).getObjectId());
List<AccessEvent> cloneRequests = getRequests();
// Force enough into the local client that enumeration will
@@ -416,13 +400,10 @@ public class SmartClientSmartServerTest extends HttpTestCase {
// Now incrementally update.
//
- t = Transport.open(dst.getRepository(), remoteURI);
- try {
+ try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
- } finally {
- t.close();
}
- assertEquals(Z, dst.getRepository().getRef(master).getObjectId());
+ assertEquals(Z, dst.getRepository().exactRef(master).getObjectId());
List<AccessEvent> requests = getRequests();
requests.removeAll(cloneRequests);
@@ -472,19 +453,16 @@ public class SmartClientSmartServerTest extends HttpTestCase {
Repository dst = createBareRepository();
assertFalse(dst.hasObject(A_txt));
- Transport t = Transport.open(dst, brokenURI);
- try {
+ try (Transport t = Transport.open(dst, brokenURI)) {
try {
t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
fail("fetch completed despite upload-pack being broken");
} catch (TransportException err) {
String exp = brokenURI + ": expected"
+ " Content-Type application/x-git-upload-pack-result;"
- + " received Content-Type text/plain;charset=UTF-8";
+ + " received Content-Type text/plain; charset=UTF-8";
assertEquals(exp, err.getMessage());
}
- } finally {
- t.close();
}
List<AccessEvent> requests = getRequests();
@@ -504,8 +482,8 @@ public class SmartClientSmartServerTest extends HttpTestCase {
assertEquals(join(brokenURI, "git-upload-pack"), service.getPath());
assertEquals(0, service.getParameters().size());
assertEquals(200, service.getStatus());
- assertEquals("text/plain;charset=UTF-8", service
- .getResponseHeader(HDR_CONTENT_TYPE));
+ assertEquals("text/plain; charset=UTF-8",
+ service.getResponseHeader(HDR_CONTENT_TYPE));
}
@Test
@@ -515,12 +493,10 @@ public class SmartClientSmartServerTest extends HttpTestCase {
final RevCommit Q = src.commit().add("Q", Q_txt).create();
final Repository db = src.getRepository();
final String dstName = Constants.R_HEADS + "new.branch";
- Transport t;
// push anonymous shouldn't be allowed.
//
- t = Transport.open(db, remoteURI);
- try {
+ try (Transport t = Transport.open(db, remoteURI)) {
final String srcExpr = Q.name();
final boolean forceUpdate = false;
final String localName = null;
@@ -536,8 +512,6 @@ public class SmartClientSmartServerTest extends HttpTestCase {
+ JGitText.get().authenticationNotSupported;
assertEquals(exp, e.getMessage());
}
- } finally {
- t.close();
}
List<AccessEvent> requests = getRequests();
@@ -558,12 +532,10 @@ public class SmartClientSmartServerTest extends HttpTestCase {
final RevCommit Q = src.commit().add("Q", Q_txt).create();
final Repository db = src.getRepository();
final String dstName = Constants.R_HEADS + "new.branch";
- Transport t;
enableReceivePack();
- t = Transport.open(db, remoteURI);
- try {
+ try (Transport t = Transport.open(db, remoteURI)) {
final String srcExpr = Q.name();
final boolean forceUpdate = false;
final String localName = null;
@@ -572,13 +544,11 @@ public class SmartClientSmartServerTest extends HttpTestCase {
RemoteRefUpdate u = new RemoteRefUpdate(src.getRepository(),
srcExpr, dstName, forceUpdate, localName, oldId);
t.push(NullProgressMonitor.INSTANCE, Collections.singleton(u));
- } finally {
- t.close();
}
assertTrue(remoteRepository.hasObject(Q_txt));
- assertNotNull("has " + dstName, remoteRepository.getRef(dstName));
- assertEquals(Q, remoteRepository.getRef(dstName).getObjectId());
+ assertNotNull("has " + dstName, remoteRepository.exactRef(dstName));
+ assertEquals(Q, remoteRepository.exactRef(dstName).getObjectId());
fsck(remoteRepository, Q);
final ReflogReader log = remoteRepository.getReflogReader(dstName);
@@ -631,7 +601,6 @@ public class SmartClientSmartServerTest extends HttpTestCase {
final RevCommit Q = src.commit().add("Q", Q_bin).create();
final Repository db = src.getRepository();
final String dstName = Constants.R_HEADS + "new.branch";
- Transport t;
enableReceivePack();
@@ -640,8 +609,7 @@ public class SmartClientSmartServerTest extends HttpTestCase {
cfg.setInt("http", null, "postbuffer", 8 * 1024);
cfg.save();
- t = Transport.open(db, remoteURI);
- try {
+ try (Transport t = Transport.open(db, remoteURI)) {
final String srcExpr = Q.name();
final boolean forceUpdate = false;
final String localName = null;
@@ -650,13 +618,11 @@ public class SmartClientSmartServerTest extends HttpTestCase {
RemoteRefUpdate u = new RemoteRefUpdate(src.getRepository(),
srcExpr, dstName, forceUpdate, localName, oldId);
t.push(NullProgressMonitor.INSTANCE, Collections.singleton(u));
- } finally {
- t.close();
}
assertTrue(remoteRepository.hasObject(Q_bin));
- assertNotNull("has " + dstName, remoteRepository.getRef(dstName));
- assertEquals(Q, remoteRepository.getRef(dstName).getObjectId());
+ assertNotNull("has " + dstName, remoteRepository.exactRef(dstName));
+ assertEquals(Q, remoteRepository.exactRef(dstName).getObjectId());
fsck(remoteRepository, Q);
List<AccessEvent> requests = getRequests();
diff --git a/org.eclipse.jgit.java7.test/.classpath b/org.eclipse.jgit.java7.test/.classpath
deleted file mode 100644
index 131f635..0000000
--- a/org.eclipse.jgit.java7.test/.classpath
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
- <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
- <classpathentry combineaccessrules="false" kind="src" path="/org.eclipse.jgit.java7"/>
- <classpathentry combineaccessrules="false" kind="src" path="/org.eclipse.jgit.test"/>
- <classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/org.eclipse.jgit.java7.test/.gitignore b/org.eclipse.jgit.java7.test/.gitignore
deleted file mode 100644
index 934e0e0..0000000
--- a/org.eclipse.jgit.java7.test/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/bin
-/target
diff --git a/org.eclipse.jgit.java7.test/.project b/org.eclipse.jgit.java7.test/.project
deleted file mode 100644
index fd4cc60..0000000
--- a/org.eclipse.jgit.java7.test/.project
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>org.eclipse.jgit.java7.test</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.ManifestBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.SchemaBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>org.eclipse.pde.PluginNature</nature>
- <nature>org.eclipse.jdt.core.javanature</nature>
- <nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
- </natures>
-</projectDescription>
diff --git a/org.eclipse.jgit.java7.test/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.java7.test/.settings/org.eclipse.core.resources.prefs
deleted file mode 100644
index f77db3b..0000000
--- a/org.eclipse.jgit.java7.test/.settings/org.eclipse.core.resources.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Sat Dec 20 21:21:24 CET 2008
-eclipse.preferences.version=1
-encoding/<project>=UTF-8
diff --git a/org.eclipse.jgit.java7.test/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.java7.test/.settings/org.eclipse.core.runtime.prefs
deleted file mode 100644
index 9f733ee..0000000
--- a/org.eclipse.jgit.java7.test/.settings/org.eclipse.core.runtime.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Mon Mar 24 18:55:56 EDT 2008
-eclipse.preferences.version=1
-line.separator=\n
diff --git a/org.eclipse.jgit.java7.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.java7.test/.settings/org.eclipse.jdt.core.prefs
deleted file mode 100644
index dcc0d3a..0000000
--- a/org.eclipse.jgit.java7.test/.settings/org.eclipse.jdt.core.prefs
+++ /dev/null
@@ -1,398 +0,0 @@
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled
-org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
-org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
-org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
-org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
-org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
-org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
-org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.7
-org.eclipse.jdt.core.compiler.debug.lineNumber=generate
-org.eclipse.jdt.core.compiler.debug.localVariable=generate
-org.eclipse.jdt.core.compiler.debug.sourceFile=generate
-org.eclipse.jdt.core.compiler.doc.comment.support=enabled
-org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
-org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
-org.eclipse.jdt.core.compiler.problem.autoboxing=warning
-org.eclipse.jdt.core.compiler.problem.comparingIdentical=error
-org.eclipse.jdt.core.compiler.problem.deadCode=error
-org.eclipse.jdt.core.compiler.problem.deprecation=warning
-org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
-org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
-org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
-org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
-org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
-org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
-org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
-org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
-org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
-org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
-org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
-org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
-org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
-org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
-org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
-org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
-org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error
-org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
-org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
-org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
-org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
-org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
-org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
-org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
-org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
-org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
-org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
-org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore
-org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
-org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
-org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
-org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
-org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
-org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
-org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
-org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
-org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
-org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
-org.eclipse.jdt.core.compiler.problem.nullReference=error
-org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
-org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
-org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
-org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
-org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
-org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
-org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
-org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
-org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
-org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
-org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
-org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
-org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
-org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
-org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
-org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
-org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
-org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled
-org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
-org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
-org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
-org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
-org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
-org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
-org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=error
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
-org.eclipse.jdt.core.compiler.problem.unusedImport=error
-org.eclipse.jdt.core.compiler.problem.unusedLabel=error
-org.eclipse.jdt.core.compiler.problem.unusedLocal=error
-org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
-org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
-org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
-org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
-org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
-org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
-org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
-org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
-org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
-org.eclipse.jdt.core.compiler.source=1.7
-org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_assignment=0
-org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
-org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
-org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
-org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
-org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
-org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
-org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
-org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
-org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
-org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
-org.eclipse.jdt.core.formatter.blank_lines_after_package=1
-org.eclipse.jdt.core.formatter.blank_lines_before_field=1
-org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
-org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
-org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
-org.eclipse.jdt.core.formatter.blank_lines_before_method=1
-org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
-org.eclipse.jdt.core.formatter.blank_lines_before_package=0
-org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
-org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
-org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
-org.eclipse.jdt.core.formatter.comment.clear_blank_lines=false
-org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
-org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
-org.eclipse.jdt.core.formatter.comment.format_block_comments=true
-org.eclipse.jdt.core.formatter.comment.format_comments=true
-org.eclipse.jdt.core.formatter.comment.format_header=false
-org.eclipse.jdt.core.formatter.comment.format_html=true
-org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
-org.eclipse.jdt.core.formatter.comment.format_line_comments=true
-org.eclipse.jdt.core.formatter.comment.format_source_code=true
-org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
-org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
-org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
-org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
-org.eclipse.jdt.core.formatter.comment.line_length=80
-org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
-org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
-org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
-org.eclipse.jdt.core.formatter.compact_else_if=true
-org.eclipse.jdt.core.formatter.continuation_indentation=2
-org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
-org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
-org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
-org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
-org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
-org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
-org.eclipse.jdt.core.formatter.indent_empty_lines=false
-org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
-org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
-org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
-org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
-org.eclipse.jdt.core.formatter.indentation.size=4
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
-org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
-org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
-org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
-org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
-org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
-org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.join_lines_in_comments=true
-org.eclipse.jdt.core.formatter.join_wrapped_lines=true
-org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
-org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
-org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
-org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
-org.eclipse.jdt.core.formatter.lineSplit=80
-org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
-org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
-org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
-org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
-org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
-org.eclipse.jdt.core.formatter.tabulation.char=tab
-org.eclipse.jdt.core.formatter.tabulation.size=4
-org.eclipse.jdt.core.formatter.use_on_off_tags=true
-org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
-org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
-org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
-org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
diff --git a/org.eclipse.jgit.java7.test/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.java7.test/.settings/org.eclipse.jdt.ui.prefs
deleted file mode 100644
index c336cce..0000000
--- a/org.eclipse.jgit.java7.test/.settings/org.eclipse.jdt.ui.prefs
+++ /dev/null
@@ -1,61 +0,0 @@
-eclipse.preferences.version=1
-editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
-formatter_profile=_JGit Format
-formatter_settings_version=12
-org.eclipse.jdt.ui.ignorelowercasenames=true
-org.eclipse.jdt.ui.importorder=java;javax;org;com;
-org.eclipse.jdt.ui.ondemandthreshold=99
-org.eclipse.jdt.ui.staticondemandthreshold=99
-org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/>
-sp_cleanup.add_default_serial_version_id=true
-sp_cleanup.add_generated_serial_version_id=false
-sp_cleanup.add_missing_annotations=false
-sp_cleanup.add_missing_deprecated_annotations=true
-sp_cleanup.add_missing_methods=false
-sp_cleanup.add_missing_nls_tags=false
-sp_cleanup.add_missing_override_annotations=true
-sp_cleanup.add_missing_override_annotations_interface_methods=false
-sp_cleanup.add_serial_version_id=false
-sp_cleanup.always_use_blocks=true
-sp_cleanup.always_use_parentheses_in_expressions=false
-sp_cleanup.always_use_this_for_non_static_field_access=false
-sp_cleanup.always_use_this_for_non_static_method_access=false
-sp_cleanup.convert_to_enhanced_for_loop=false
-sp_cleanup.correct_indentation=false
-sp_cleanup.format_source_code=true
-sp_cleanup.format_source_code_changes_only=true
-sp_cleanup.make_local_variable_final=false
-sp_cleanup.make_parameters_final=false
-sp_cleanup.make_private_fields_final=true
-sp_cleanup.make_type_abstract_if_missing_method=false
-sp_cleanup.make_variable_declarations_final=false
-sp_cleanup.never_use_blocks=false
-sp_cleanup.never_use_parentheses_in_expressions=true
-sp_cleanup.on_save_use_additional_actions=true
-sp_cleanup.organize_imports=false
-sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
-sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
-sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
-sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
-sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
-sp_cleanup.remove_private_constructors=true
-sp_cleanup.remove_trailing_whitespaces=true
-sp_cleanup.remove_trailing_whitespaces_all=true
-sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
-sp_cleanup.remove_unnecessary_casts=false
-sp_cleanup.remove_unnecessary_nls_tags=false
-sp_cleanup.remove_unused_imports=false
-sp_cleanup.remove_unused_local_variables=false
-sp_cleanup.remove_unused_private_fields=true
-sp_cleanup.remove_unused_private_members=false
-sp_cleanup.remove_unused_private_methods=true
-sp_cleanup.remove_unused_private_types=true
-sp_cleanup.sort_members=false
-sp_cleanup.sort_members_all=false
-sp_cleanup.use_blocks=false
-sp_cleanup.use_blocks_only_for_return_and_throw=false
-sp_cleanup.use_parentheses_in_expressions=false
-sp_cleanup.use_this_for_non_static_field_access=false
-sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
-sp_cleanup.use_this_for_non_static_method_access=false
-sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/org.eclipse.jgit.java7.test/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.java7.test/.settings/org.eclipse.mylyn.tasks.ui.prefs
deleted file mode 100644
index 823c0f5..0000000
--- a/org.eclipse.jgit.java7.test/.settings/org.eclipse.mylyn.tasks.ui.prefs
+++ /dev/null
@@ -1,4 +0,0 @@
-#Tue Jul 19 20:11:28 CEST 2011
-eclipse.preferences.version=1
-project.repository.kind=bugzilla
-project.repository.url=https\://bugs.eclipse.org/bugs
diff --git a/org.eclipse.jgit.java7.test/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.java7.test/.settings/org.eclipse.mylyn.team.ui.prefs
deleted file mode 100644
index 0cba949..0000000
--- a/org.eclipse.jgit.java7.test/.settings/org.eclipse.mylyn.team.ui.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
-eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.java7.test/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.jgit.java7.test/.settings/org.eclipse.pde.api.tools.prefs
deleted file mode 100644
index cd148d9..0000000
--- a/org.eclipse.jgit.java7.test/.settings/org.eclipse.pde.api.tools.prefs
+++ /dev/null
@@ -1,94 +0,0 @@
-#Tue Oct 18 00:52:01 CEST 2011
-ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error
-ANNOTATION_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
-ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error
-ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD=Error
-ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
-API_COMPONENT_ELEMENT_TYPE_REMOVED_API_TYPE=Error
-API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_API_TYPE=Error
-API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_TYPE=Error
-API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error
-CLASS_ELEMENT_TYPE_ADDED_METHOD=Error
-CLASS_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
-CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
-CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
-CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
-CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
-CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
-CLASS_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
-CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
-CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error
-CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error
-CLASS_ELEMENT_TYPE_REMOVED_SUPERCLASS=Error
-CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
-CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
-CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
-CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
-CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
-CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
-ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
-ENUM_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
-ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error
-ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error
-ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error
-ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
-FIELD_ELEMENT_TYPE_ADDED_VALUE=Error
-FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
-FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Error
-FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
-FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
-FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
-FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error
-FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error
-FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENT=Error
-FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error
-ILLEGAL_EXTEND=Warning
-ILLEGAL_IMPLEMENT=Warning
-ILLEGAL_INSTANTIATE=Warning
-ILLEGAL_OVERRIDE=Warning
-ILLEGAL_REFERENCE=Warning
-INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error
-INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error
-INTERFACE_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
-INTERFACE_ELEMENT_TYPE_ADDED_SUPER_INTERFACE_WITH_METHODS=Error
-INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
-INTERFACE_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
-INTERFACE_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
-INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error
-INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error
-INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
-INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
-INVALID_JAVADOC_TAG=Ignore
-INVALID_REFERENCE_IN_SYSTEM_LIBRARIES=Error
-LEAK_EXTEND=Warning
-LEAK_FIELD_DECL=Warning
-LEAK_IMPLEMENT=Warning
-LEAK_METHOD_PARAM=Warning
-LEAK_METHOD_RETURN_TYPE=Warning
-METHOD_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
-METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
-METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
-METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
-METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
-METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
-METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
-METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
-METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
-METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
-TYPE_PARAMETER_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
-TYPE_PARAMETER_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
-TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
-TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
-TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
-TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
-UNUSED_PROBLEM_FILTERS=Warning
-automatically_removed_unused_problem_filters=false
-eclipse.preferences.version=1
-incompatible_api_component_version=Error
-incompatible_api_component_version_include_major_without_breaking_change=Disabled
-incompatible_api_component_version_include_minor_without_api_change=Disabled
-invalid_since_tag_version=Error
-malformed_since_tag=Error
-missing_since_tag=Error
-report_api_breakage_when_major_version_incremented=Disabled
-report_resolution_errors_api_component=Warning
diff --git a/org.eclipse.jgit.java7.test/.settings/org.eclipse.pde.core.prefs b/org.eclipse.jgit.java7.test/.settings/org.eclipse.pde.core.prefs
deleted file mode 100644
index 82793f2..0000000
--- a/org.eclipse.jgit.java7.test/.settings/org.eclipse.pde.core.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Thu Jan 14 14:34:32 CST 2010
-eclipse.preferences.version=1
-resolve.requirebundle=false
diff --git a/org.eclipse.jgit.java7.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.java7.test/META-INF/MANIFEST.MF
deleted file mode 100644
index cf14855..0000000
--- a/org.eclipse.jgit.java7.test/META-INF/MANIFEST.MF
+++ /dev/null
@@ -1,19 +0,0 @@
-Manifest-Version: 1.0
-Bundle-ManifestVersion: 2
-Bundle-Name: %plugin_name
-Bundle-SymbolicName: org.eclipse.jgit.java7.test
-Bundle-Version: 3.7.1.201504261725-r
-Bundle-Vendor: %provider_name
-Bundle-RequiredExecutionEnvironment: JavaSE-1.7
-Import-Package: org.eclipse.jgit.api;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.api.errors;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.diff;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.dircache;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.internal.storage.file;version="3.7.1",
- org.eclipse.jgit.junit;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.lib;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.revwalk;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.storage.file;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.treewalk;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.util;version="[3.7.1,3.8.0)",
- org.junit;version="[4.11.0,5.0.0)"
diff --git a/org.eclipse.jgit.java7.test/build.properties b/org.eclipse.jgit.java7.test/build.properties
deleted file mode 100644
index b1e8ac1..0000000
--- a/org.eclipse.jgit.java7.test/build.properties
+++ /dev/null
@@ -1,3 +0,0 @@
-bin.includes = META-INF/,\
- .,\
- plugin.properties
diff --git a/org.eclipse.jgit.java7.test/org.eclipse.jgit.java7 -- Java7 feature test (Java 8).launch b/org.eclipse.jgit.java7.test/org.eclipse.jgit.java7 -- Java7 feature test (Java 8).launch
deleted file mode 100644
index 0c460f0..0000000
--- a/org.eclipse.jgit.java7.test/org.eclipse.jgit.java7 -- Java7 feature test (Java 8).launch
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
-<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
-<listEntry value="/org.eclipse.jgit.java7.test"/>
-</listAttribute>
-<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
-<listEntry value="4"/>
-</listAttribute>
-<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
-<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
-<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
-</listAttribute>
-<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=org.eclipse.jgit.java7.test"/>
-<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
-<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
-<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
-<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
-<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
-<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
-<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.jgit.java7.test"/>
-</launchConfiguration>
diff --git a/org.eclipse.jgit.java7.test/org.eclipse.jgit.java7 -- Java7 feature test.launch b/org.eclipse.jgit.java7.test/org.eclipse.jgit.java7 -- Java7 feature test.launch
deleted file mode 100644
index d65d045..0000000
--- a/org.eclipse.jgit.java7.test/org.eclipse.jgit.java7 -- Java7 feature test.launch
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
-<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
-<listEntry value="/org.eclipse.jgit.java7.test"/>
-</listAttribute>
-<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
-<listEntry value="4"/>
-</listAttribute>
-<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
-<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
-<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
-</listAttribute>
-<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=org.eclipse.jgit.java7.test"/>
-<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
-<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
-<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
-<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
-<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
-<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.jgit.java7.test"/>
-</launchConfiguration>
diff --git a/org.eclipse.jgit.java7.test/plugin.properties b/org.eclipse.jgit.java7.test/plugin.properties
deleted file mode 100644
index 5f9d756..0000000
--- a/org.eclipse.jgit.java7.test/plugin.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-plugin_name=JGit Core Java 7 Tests
-provider_name=Eclipse.org
diff --git a/org.eclipse.jgit.java7.test/pom.xml b/org.eclipse.jgit.java7.test/pom.xml
deleted file mode 100644
index 33a0dd8..0000000
--- a/org.eclipse.jgit.java7.test/pom.xml
+++ /dev/null
@@ -1,126 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2012-2013, Robin Rosenberg <robin.rosenberg at dewire.com>
- and other copyright owners as documented in the project's IP log.
-
- This program and the accompanying materials are made available
- under the terms of the Eclipse Distribution License v1.0 which
- accompanies this distribution, is reproduced below, and is
- available at http://www.eclipse.org/org/documents/edl-v10.php
-
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or
- without modification, are permitted provided that the following
- conditions are met:
-
- - Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- - Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- - Neither the name of the Eclipse Foundation, Inc. nor the
- names of its contributors may be used to endorse or promote
- products derived from this software without specific prior
- written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <groupId>org.eclipse.jgit</groupId>
- <artifactId>org.eclipse.jgit-parent</artifactId>
- <version>3.7.1.201504261725-r</version>
- </parent>
-
- <artifactId>org.eclipse.jgit.java7.test</artifactId>
- <name>JGit - Core Java 7 Tests</name>
-
- <description>
- JUnit tests for Java7 support in the core library.
- </description>
-
- <dependencies>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
-
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest-library</artifactId>
- <scope>test</scope>
- <version>[1.1.0,2.0.0)</version>
- </dependency>
-
- <dependency>
- <groupId>org.eclipse.jgit</groupId>
- <artifactId>org.eclipse.jgit</artifactId>
- <version>${project.version}</version>
- </dependency>
-
- <dependency>
- <groupId>org.eclipse.jgit</groupId>
- <artifactId>org.eclipse.jgit.junit</artifactId>
- <version>${project.version}</version>
- </dependency>
-
- <dependency>
- <groupId>org.eclipse.jgit</groupId>
- <artifactId>org.eclipse.jgit.java7</artifactId>
- <version>${project.version}</version>
- </dependency>
-
- </dependencies>
-
- <build>
- <testSourceDirectory>src/</testSourceDirectory>
-
- <testResources>
- <testResource>
- <directory>tst-rsrc/</directory>
- </testResource>
- </testResources>
-
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-jar-plugin</artifactId>
- <executions>
- <execution>
- <goals>
- <goal>test-jar</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <argLine>-Xmx256m -Dfile.encoding=UTF-8 -Djava.io.tmpdir=${project.build.directory}</argLine>
- </configuration>
- </plugin>
- </plugins>
- </build>
-</project>
diff --git a/org.eclipse.jgit.java7/.classpath b/org.eclipse.jgit.java7/.classpath
deleted file mode 100644
index 098194c..0000000
--- a/org.eclipse.jgit.java7/.classpath
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
- <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/org.eclipse.jgit.java7/.fbprefs b/org.eclipse.jgit.java7/.fbprefs
deleted file mode 100644
index 81a0767..0000000
--- a/org.eclipse.jgit.java7/.fbprefs
+++ /dev/null
@@ -1,125 +0,0 @@
-#FindBugs User Preferences
-#Mon May 04 16:24:13 PDT 2009
-detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true
-detectorBadAppletConstructor=BadAppletConstructor|false
-detectorBadResultSetAccess=BadResultSetAccess|true
-detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true
-detectorBadUseOfReturnValue=BadUseOfReturnValue|true
-detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true
-detectorBooleanReturnNull=BooleanReturnNull|true
-detectorCallToUnsupportedMethod=CallToUnsupportedMethod|true
-detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true
-detectorCheckTypeQualifiers=CheckTypeQualifiers|true
-detectorCloneIdiom=CloneIdiom|false
-detectorComparatorIdiom=ComparatorIdiom|true
-detectorConfusedInheritance=ConfusedInheritance|true
-detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true
-detectorCrossSiteScripting=CrossSiteScripting|true
-detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true
-detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true
-detectorDontUseEnum=DontUseEnum|true
-detectorDroppedException=DroppedException|true
-detectorDumbMethodInvocations=DumbMethodInvocations|true
-detectorDumbMethods=DumbMethods|true
-detectorDuplicateBranches=DuplicateBranches|true
-detectorEmptyZipFileEntry=EmptyZipFileEntry|true
-detectorEqualsOperandShouldHaveClassCompatibleWithThis=EqualsOperandShouldHaveClassCompatibleWithThis|true
-detectorFinalizerNullsFields=FinalizerNullsFields|true
-detectorFindBadCast2=FindBadCast2|true
-detectorFindBadForLoop=FindBadForLoop|true
-detectorFindCircularDependencies=FindCircularDependencies|false
-detectorFindDeadLocalStores=FindDeadLocalStores|true
-detectorFindDoubleCheck=FindDoubleCheck|true
-detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true
-detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true
-detectorFindFinalizeInvocations=FindFinalizeInvocations|true
-detectorFindFloatEquality=FindFloatEquality|true
-detectorFindHEmismatch=FindHEmismatch|true
-detectorFindInconsistentSync2=FindInconsistentSync2|true
-detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true
-detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true
-detectorFindMaskedFields=FindMaskedFields|true
-detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true
-detectorFindNakedNotify=FindNakedNotify|true
-detectorFindNonSerializableStoreIntoSession=FindNonSerializableStoreIntoSession|true
-detectorFindNonSerializableValuePassedToWriteObject=FindNonSerializableValuePassedToWriteObject|true
-detectorFindNonShortCircuit=FindNonShortCircuit|true
-detectorFindNullDeref=FindNullDeref|true
-detectorFindNullDerefsInvolvingNonShortCircuitEvaluation=FindNullDerefsInvolvingNonShortCircuitEvaluation|true
-detectorFindOpenStream=FindOpenStream|true
-detectorFindPuzzlers=FindPuzzlers|true
-detectorFindRefComparison=FindRefComparison|true
-detectorFindReturnRef=FindReturnRef|true
-detectorFindRunInvocations=FindRunInvocations|true
-detectorFindSelfComparison=FindSelfComparison|true
-detectorFindSelfComparison2=FindSelfComparison2|true
-detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true
-detectorFindSpinLoop=FindSpinLoop|true
-detectorFindSqlInjection=FindSqlInjection|true
-detectorFindTwoLockWait=FindTwoLockWait|true
-detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true
-detectorFindUnconditionalWait=FindUnconditionalWait|true
-detectorFindUninitializedGet=FindUninitializedGet|true
-detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true
-detectorFindUnreleasedLock=FindUnreleasedLock|true
-detectorFindUnsatisfiedObligation=FindUnsatisfiedObligation|true
-detectorFindUnsyncGet=FindUnsyncGet|true
-detectorFindUselessControlFlow=FindUselessControlFlow|true
-detectorFormatStringChecker=FormatStringChecker|true
-detectorHugeSharedStringConstants=HugeSharedStringConstants|true
-detectorIDivResultCastToDouble=IDivResultCastToDouble|true
-detectorIncompatMask=IncompatMask|true
-detectorInconsistentAnnotations=InconsistentAnnotations|true
-detectorInefficientMemberAccess=InefficientMemberAccess|false
-detectorInefficientToArray=InefficientToArray|true
-detectorInfiniteLoop=InfiniteLoop|true
-detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true
-detectorInfiniteRecursiveLoop2=InfiniteRecursiveLoop2|false
-detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true
-detectorInitializationChain=InitializationChain|true
-detectorInstantiateStaticClass=InstantiateStaticClass|true
-detectorInvalidJUnitTest=InvalidJUnitTest|true
-detectorIteratorIdioms=IteratorIdioms|true
-detectorLazyInit=LazyInit|true
-detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true
-detectorMethodReturnCheck=MethodReturnCheck|true
-detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true
-detectorMutableLock=MutableLock|true
-detectorMutableStaticFields=MutableStaticFields|true
-detectorNaming=Naming|true
-detectorNumberConstructor=NumberConstructor|true
-detectorOverridingEqualsNotSymmetrical=OverridingEqualsNotSymmetrical|true
-detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true
-detectorPublicSemaphores=PublicSemaphores|false
-detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true
-detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true
-detectorRedundantInterfaces=RedundantInterfaces|true
-detectorRepeatedConditionals=RepeatedConditionals|true
-detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true
-detectorSerializableIdiom=SerializableIdiom|true
-detectorStartInConstructor=StartInConstructor|true
-detectorStaticCalendarDetector=StaticCalendarDetector|true
-detectorStringConcatenation=StringConcatenation|true
-detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true
-detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true
-detectorSwitchFallthrough=SwitchFallthrough|true
-detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true
-detectorSynchronizeOnClassLiteralNotGetClass=SynchronizeOnClassLiteralNotGetClass|true
-detectorSynchronizingOnContentsOfFieldToProtectField=SynchronizingOnContentsOfFieldToProtectField|true
-detectorURLProblems=URLProblems|true
-detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true
-detectorUnnecessaryMath=UnnecessaryMath|true
-detectorUnreadFields=UnreadFields|true
-detectorUseObjectEquals=UseObjectEquals|false
-detectorUselessSubclassMethod=UselessSubclassMethod|false
-detectorVarArgsProblems=VarArgsProblems|true
-detectorVolatileUsage=VolatileUsage|true
-detectorWaitInLoop=WaitInLoop|true
-detectorWrongMapIterator=WrongMapIterator|true
-detectorXMLFactoryBypass=XMLFactoryBypass|true
-detector_threshold=2
-effort=default
-excludefilter0=findBugs/FindBugsExcludeFilter.xml
-filter_settings=Medium|BAD_PRACTICE,CORRECTNESS,MT_CORRECTNESS,PERFORMANCE,STYLE|false
-filter_settings_neg=MALICIOUS_CODE,NOISE,I18N,SECURITY,EXPERIMENTAL|
-run_at_full_build=true
diff --git a/org.eclipse.jgit.java7/.gitignore b/org.eclipse.jgit.java7/.gitignore
deleted file mode 100644
index 934e0e0..0000000
--- a/org.eclipse.jgit.java7/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/bin
-/target
diff --git a/org.eclipse.jgit.java7/.project b/org.eclipse.jgit.java7/.project
deleted file mode 100644
index 291c8ff..0000000
--- a/org.eclipse.jgit.java7/.project
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>org.eclipse.jgit.java7</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.ManifestBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.SchemaBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>org.eclipse.pde.PluginNature</nature>
- <nature>org.eclipse.jdt.core.javanature</nature>
- <nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
- </natures>
-</projectDescription>
diff --git a/org.eclipse.jgit.java7/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.java7/.settings/org.eclipse.core.resources.prefs
deleted file mode 100644
index 66ac15c..0000000
--- a/org.eclipse.jgit.java7/.settings/org.eclipse.core.resources.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Mon Aug 11 16:46:12 PDT 2008
-eclipse.preferences.version=1
-encoding/<project>=UTF-8
diff --git a/org.eclipse.jgit.java7/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.java7/.settings/org.eclipse.core.runtime.prefs
deleted file mode 100644
index 006e07e..0000000
--- a/org.eclipse.jgit.java7/.settings/org.eclipse.core.runtime.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Mon Mar 24 18:55:50 EDT 2008
-eclipse.preferences.version=1
-line.separator=\n
diff --git a/org.eclipse.jgit.java7/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.java7/.settings/org.eclipse.jdt.core.prefs
deleted file mode 100644
index ff39d16..0000000
--- a/org.eclipse.jgit.java7/.settings/org.eclipse.jdt.core.prefs
+++ /dev/null
@@ -1,398 +0,0 @@
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled
-org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
-org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
-org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
-org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
-org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
-org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
-org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.7
-org.eclipse.jdt.core.compiler.debug.lineNumber=generate
-org.eclipse.jdt.core.compiler.debug.localVariable=generate
-org.eclipse.jdt.core.compiler.debug.sourceFile=generate
-org.eclipse.jdt.core.compiler.doc.comment.support=enabled
-org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
-org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
-org.eclipse.jdt.core.compiler.problem.autoboxing=warning
-org.eclipse.jdt.core.compiler.problem.comparingIdentical=error
-org.eclipse.jdt.core.compiler.problem.deadCode=error
-org.eclipse.jdt.core.compiler.problem.deprecation=warning
-org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
-org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
-org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
-org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
-org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
-org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
-org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
-org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
-org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
-org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
-org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
-org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
-org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
-org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
-org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
-org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
-org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error
-org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
-org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
-org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
-org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private
-org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning
-org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
-org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
-org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
-org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
-org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
-org.eclipse.jdt.core.compiler.problem.missingJavadocComments=error
-org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
-org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag
-org.eclipse.jdt.core.compiler.problem.missingJavadocTags=error
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
-org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
-org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
-org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
-org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
-org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
-org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning
-org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
-org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
-org.eclipse.jdt.core.compiler.problem.nullReference=error
-org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
-org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
-org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
-org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
-org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
-org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
-org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
-org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
-org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
-org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
-org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
-org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
-org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
-org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
-org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
-org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
-org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
-org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled
-org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
-org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
-org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
-org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
-org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
-org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
-org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
-org.eclipse.jdt.core.compiler.problem.unusedImport=error
-org.eclipse.jdt.core.compiler.problem.unusedLabel=error
-org.eclipse.jdt.core.compiler.problem.unusedLocal=error
-org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
-org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
-org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
-org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
-org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
-org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
-org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
-org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
-org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
-org.eclipse.jdt.core.compiler.source=1.7
-org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_assignment=0
-org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
-org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
-org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
-org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
-org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
-org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
-org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
-org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
-org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
-org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
-org.eclipse.jdt.core.formatter.blank_lines_after_package=1
-org.eclipse.jdt.core.formatter.blank_lines_before_field=1
-org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
-org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
-org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
-org.eclipse.jdt.core.formatter.blank_lines_before_method=1
-org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
-org.eclipse.jdt.core.formatter.blank_lines_before_package=0
-org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
-org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
-org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
-org.eclipse.jdt.core.formatter.comment.clear_blank_lines=false
-org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
-org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
-org.eclipse.jdt.core.formatter.comment.format_block_comments=true
-org.eclipse.jdt.core.formatter.comment.format_comments=true
-org.eclipse.jdt.core.formatter.comment.format_header=false
-org.eclipse.jdt.core.formatter.comment.format_html=true
-org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
-org.eclipse.jdt.core.formatter.comment.format_line_comments=true
-org.eclipse.jdt.core.formatter.comment.format_source_code=true
-org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
-org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
-org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
-org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
-org.eclipse.jdt.core.formatter.comment.line_length=80
-org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
-org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
-org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
-org.eclipse.jdt.core.formatter.compact_else_if=true
-org.eclipse.jdt.core.formatter.continuation_indentation=2
-org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
-org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
-org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
-org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
-org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
-org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
-org.eclipse.jdt.core.formatter.indent_empty_lines=false
-org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
-org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
-org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
-org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
-org.eclipse.jdt.core.formatter.indentation.size=4
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
-org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
-org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
-org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
-org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
-org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
-org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.join_lines_in_comments=true
-org.eclipse.jdt.core.formatter.join_wrapped_lines=true
-org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
-org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
-org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
-org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
-org.eclipse.jdt.core.formatter.lineSplit=80
-org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
-org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
-org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
-org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
-org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
-org.eclipse.jdt.core.formatter.tabulation.char=tab
-org.eclipse.jdt.core.formatter.tabulation.size=4
-org.eclipse.jdt.core.formatter.use_on_off_tags=true
-org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
-org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
-org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
-org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
diff --git a/org.eclipse.jgit.java7/.settings/org.eclipse.jdt.ui.prefs b/org.eclipse.jgit.java7/.settings/org.eclipse.jdt.ui.prefs
deleted file mode 100644
index c336cce..0000000
--- a/org.eclipse.jgit.java7/.settings/org.eclipse.jdt.ui.prefs
+++ /dev/null
@@ -1,61 +0,0 @@
-eclipse.preferences.version=1
-editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
-formatter_profile=_JGit Format
-formatter_settings_version=12
-org.eclipse.jdt.ui.ignorelowercasenames=true
-org.eclipse.jdt.ui.importorder=java;javax;org;com;
-org.eclipse.jdt.ui.ondemandthreshold=99
-org.eclipse.jdt.ui.staticondemandthreshold=99
-org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/>
-sp_cleanup.add_default_serial_version_id=true
-sp_cleanup.add_generated_serial_version_id=false
-sp_cleanup.add_missing_annotations=false
-sp_cleanup.add_missing_deprecated_annotations=true
-sp_cleanup.add_missing_methods=false
-sp_cleanup.add_missing_nls_tags=false
-sp_cleanup.add_missing_override_annotations=true
-sp_cleanup.add_missing_override_annotations_interface_methods=false
-sp_cleanup.add_serial_version_id=false
-sp_cleanup.always_use_blocks=true
-sp_cleanup.always_use_parentheses_in_expressions=false
-sp_cleanup.always_use_this_for_non_static_field_access=false
-sp_cleanup.always_use_this_for_non_static_method_access=false
-sp_cleanup.convert_to_enhanced_for_loop=false
-sp_cleanup.correct_indentation=false
-sp_cleanup.format_source_code=true
-sp_cleanup.format_source_code_changes_only=true
-sp_cleanup.make_local_variable_final=false
-sp_cleanup.make_parameters_final=false
-sp_cleanup.make_private_fields_final=true
-sp_cleanup.make_type_abstract_if_missing_method=false
-sp_cleanup.make_variable_declarations_final=false
-sp_cleanup.never_use_blocks=false
-sp_cleanup.never_use_parentheses_in_expressions=true
-sp_cleanup.on_save_use_additional_actions=true
-sp_cleanup.organize_imports=false
-sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
-sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
-sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
-sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
-sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
-sp_cleanup.remove_private_constructors=true
-sp_cleanup.remove_trailing_whitespaces=true
-sp_cleanup.remove_trailing_whitespaces_all=true
-sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
-sp_cleanup.remove_unnecessary_casts=false
-sp_cleanup.remove_unnecessary_nls_tags=false
-sp_cleanup.remove_unused_imports=false
-sp_cleanup.remove_unused_local_variables=false
-sp_cleanup.remove_unused_private_fields=true
-sp_cleanup.remove_unused_private_members=false
-sp_cleanup.remove_unused_private_methods=true
-sp_cleanup.remove_unused_private_types=true
-sp_cleanup.sort_members=false
-sp_cleanup.sort_members_all=false
-sp_cleanup.use_blocks=false
-sp_cleanup.use_blocks_only_for_return_and_throw=false
-sp_cleanup.use_parentheses_in_expressions=false
-sp_cleanup.use_this_for_non_static_field_access=false
-sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
-sp_cleanup.use_this_for_non_static_method_access=false
-sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/org.eclipse.jgit.java7/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.java7/.settings/org.eclipse.mylyn.tasks.ui.prefs
deleted file mode 100644
index 823c0f5..0000000
--- a/org.eclipse.jgit.java7/.settings/org.eclipse.mylyn.tasks.ui.prefs
+++ /dev/null
@@ -1,4 +0,0 @@
-#Tue Jul 19 20:11:28 CEST 2011
-eclipse.preferences.version=1
-project.repository.kind=bugzilla
-project.repository.url=https\://bugs.eclipse.org/bugs
diff --git a/org.eclipse.jgit.java7/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.java7/.settings/org.eclipse.mylyn.team.ui.prefs
deleted file mode 100644
index 0cba949..0000000
--- a/org.eclipse.jgit.java7/.settings/org.eclipse.mylyn.team.ui.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
-eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.java7/.settings/org.eclipse.pde.api.tools.prefs b/org.eclipse.jgit.java7/.settings/org.eclipse.pde.api.tools.prefs
deleted file mode 100644
index cd148d9..0000000
--- a/org.eclipse.jgit.java7/.settings/org.eclipse.pde.api.tools.prefs
+++ /dev/null
@@ -1,94 +0,0 @@
-#Tue Oct 18 00:52:01 CEST 2011
-ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error
-ANNOTATION_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
-ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error
-ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD=Error
-ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
-API_COMPONENT_ELEMENT_TYPE_REMOVED_API_TYPE=Error
-API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_API_TYPE=Error
-API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_TYPE=Error
-API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error
-CLASS_ELEMENT_TYPE_ADDED_METHOD=Error
-CLASS_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
-CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
-CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
-CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
-CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
-CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
-CLASS_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
-CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
-CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error
-CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error
-CLASS_ELEMENT_TYPE_REMOVED_SUPERCLASS=Error
-CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
-CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
-CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
-CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
-CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
-CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
-ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
-ENUM_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
-ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error
-ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error
-ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error
-ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
-FIELD_ELEMENT_TYPE_ADDED_VALUE=Error
-FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
-FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Error
-FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
-FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
-FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
-FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error
-FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error
-FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENT=Error
-FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error
-ILLEGAL_EXTEND=Warning
-ILLEGAL_IMPLEMENT=Warning
-ILLEGAL_INSTANTIATE=Warning
-ILLEGAL_OVERRIDE=Warning
-ILLEGAL_REFERENCE=Warning
-INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error
-INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error
-INTERFACE_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
-INTERFACE_ELEMENT_TYPE_ADDED_SUPER_INTERFACE_WITH_METHODS=Error
-INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
-INTERFACE_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
-INTERFACE_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
-INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error
-INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error
-INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
-INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
-INVALID_JAVADOC_TAG=Ignore
-INVALID_REFERENCE_IN_SYSTEM_LIBRARIES=Error
-LEAK_EXTEND=Warning
-LEAK_FIELD_DECL=Warning
-LEAK_IMPLEMENT=Warning
-LEAK_METHOD_PARAM=Warning
-LEAK_METHOD_RETURN_TYPE=Warning
-METHOD_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
-METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
-METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
-METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
-METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
-METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
-METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
-METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
-METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
-METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
-TYPE_PARAMETER_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
-TYPE_PARAMETER_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
-TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
-TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
-TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
-TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
-UNUSED_PROBLEM_FILTERS=Warning
-automatically_removed_unused_problem_filters=false
-eclipse.preferences.version=1
-incompatible_api_component_version=Error
-incompatible_api_component_version_include_major_without_breaking_change=Disabled
-incompatible_api_component_version_include_minor_without_api_change=Disabled
-invalid_since_tag_version=Error
-malformed_since_tag=Error
-missing_since_tag=Error
-report_api_breakage_when_major_version_incremented=Disabled
-report_resolution_errors_api_component=Warning
diff --git a/org.eclipse.jgit.java7/.settings/org.eclipse.pde.core.prefs b/org.eclipse.jgit.java7/.settings/org.eclipse.pde.core.prefs
deleted file mode 100644
index 82793f2..0000000
--- a/org.eclipse.jgit.java7/.settings/org.eclipse.pde.core.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Thu Jan 14 14:34:32 CST 2010
-eclipse.preferences.version=1
-resolve.requirebundle=false
diff --git a/org.eclipse.jgit.java7/META-INF/MANIFEST.MF b/org.eclipse.jgit.java7/META-INF/MANIFEST.MF
deleted file mode 100644
index 88e9103..0000000
--- a/org.eclipse.jgit.java7/META-INF/MANIFEST.MF
+++ /dev/null
@@ -1,10 +0,0 @@
-Manifest-Version: 1.0
-Bundle-ManifestVersion: 2
-Fragment-Host: org.eclipse.jgit;bundle-version="3.1.1"
-Bundle-Name: %plugin_name
-Bundle-SymbolicName: org.eclipse.jgit.java7
-Bundle-Version: 3.7.1.201504261725-r
-Bundle-Localization: plugin
-Bundle-Vendor: %provider_name
-Bundle-RequiredExecutionEnvironment: JavaSE-1.7
-Export-Package: org.eclipse.jgit.util;version="3.7.1"
diff --git a/org.eclipse.jgit.java7/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.java7/META-INF/SOURCE-MANIFEST.MF
deleted file mode 100644
index 5dce9d4..0000000
--- a/org.eclipse.jgit.java7/META-INF/SOURCE-MANIFEST.MF
+++ /dev/null
@@ -1,8 +0,0 @@
-Manifest-Version: 1.0
-Bundle-ManifestVersion: 2
-Bundle-Name: org.eclipse.jgit.java7 - Sources
-Bundle-SymbolicName: org.eclipse.jgit.java7.source
-Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 3.7.1.201504261725-r
-Eclipse-SourceBundle: org.eclipse.jgit.java7;version="3.7.1.201504261725-r";roots="."
-
diff --git a/org.eclipse.jgit.java7/about.html b/org.eclipse.jgit.java7/about.html
deleted file mode 100644
index 2cb2663..0000000
--- a/org.eclipse.jgit.java7/about.html
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1" ?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
-<title>Eclipse Distribution License - Version 1.0</title>
-<style type="text/css">
- body {
- size: 8.5in 11.0in;
- margin: 0.25in 0.5in 0.25in 0.5in;
- tab-interval: 0.5in;
- }
- p {
- margin-left: auto;
- margin-top: 0.5em;
- margin-bottom: 0.5em;
- }
- p.list {
- margin-left: 0.5in;
- margin-top: 0.05em;
- margin-bottom: 0.05em;
- }
- </style>
-
-</head>
-
-<body lang="EN-US">
-
-<p><b>Eclipse Distribution License - v 1.0</b></p>
-
-<p>Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. </p>
-
-<p>All rights reserved.</p>
-<p>Redistribution and use in source and binary forms, with or without modification,
- are permitted provided that the following conditions are met:
-<ul><li>Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer. </li>
-<li>Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution. </li>
-<li>Neither the name of the Eclipse Foundation, Inc. nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission. </li></ul>
-</p>
-<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.</p>
-
-</body>
-
-</html>
diff --git a/org.eclipse.jgit.java7/build.properties b/org.eclipse.jgit.java7/build.properties
deleted file mode 100644
index 185d310..0000000
--- a/org.eclipse.jgit.java7/build.properties
+++ /dev/null
@@ -1,7 +0,0 @@
-source.. = src/
-output.. = bin/
-bin.includes = META-INF/,\
- .,\
- about.html,\
- plugin.properties
-
diff --git a/org.eclipse.jgit.java7/plugin.properties b/org.eclipse.jgit.java7/plugin.properties
deleted file mode 100644
index fc2c16a..0000000
--- a/org.eclipse.jgit.java7/plugin.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-plugin_name=JGit Java 7 Support
-provider_name=Eclipse JGit
diff --git a/org.eclipse.jgit.java7/pom.xml b/org.eclipse.jgit.java7/pom.xml
deleted file mode 100644
index c71d1a4..0000000
--- a/org.eclipse.jgit.java7/pom.xml
+++ /dev/null
@@ -1,196 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2012-2013, Robin Rosenberg
- and other copyright owners as documented in the project's IP log.
-
- This program and the accompanying materials are made available
- under the terms of the Eclipse Distribution License v1.0 which
- accompanies this distribution, is reproduced below, and is
- available at http://www.eclipse.org/org/documents/edl-v10.php
-
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or
- without modification, are permitted provided that the following
- conditions are met:
-
- - Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- - Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- - Neither the name of the Eclipse Foundation, Inc. nor the
- names of its contributors may be used to endorse or promote
- products derived from this software without specific prior
- written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <groupId>org.eclipse.jgit</groupId>
- <artifactId>org.eclipse.jgit-parent</artifactId>
- <version>3.7.1.201504261725-r</version>
- </parent>
-
- <artifactId>org.eclipse.jgit.java7</artifactId>
- <name>JGit - Core Java7 Support</name>
-
- <description>
- Java7 support for symbolic links etc
- </description>
-
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jgit</groupId>
- <artifactId>org.eclipse.jgit</artifactId>
- <version>${project.version}</version>
- </dependency>
- </dependencies>
-
- <properties>
- <translate-qualifier/>
- <source-bundle-manifest>${project.build.directory}/META-INF/SOURCE-MANIFEST.MF</source-bundle-manifest>
- </properties>
-
- <build>
- <sourceDirectory>src/</sourceDirectory>
-
- <resources>
- <resource>
- <directory>.</directory>
- <includes>
- <include>plugin.properties</include>
- <include>about.html</include>
- </includes>
- </resource>
- <resource>
- <directory>resources/</directory>
- </resource>
- </resources>
-
- <plugins>
- <plugin>
- <artifactId>maven-compiler-plugin</artifactId>
- <configuration>
- <source>7</source>
- <target>7</target>
- <encoding>UTF-8</encoding>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-antrun-plugin</artifactId>
- <executions>
- <execution>
- <id>translate-source-qualifier</id>
- <phase>generate-resources</phase>
- <configuration>
- <target>
- <copy file="META-INF/SOURCE-MANIFEST.MF" tofile="${source-bundle-manifest}" overwrite="true"/>
- <replace file="${source-bundle-manifest}">
- <replacefilter token=".qualifier" value=".${maven.build.timestamp}"/>
- </replace>
- </target>
- </configuration>
- <goals>
- <goal>run</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
-
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-source-plugin</artifactId>
- <inherited>true</inherited>
- <executions>
- <execution>
- <id>attach-sources</id>
- <phase>process-classes</phase>
- <goals>
- <goal>jar</goal>
- </goals>
- <configuration>
- <archive>
- <manifestFile>${source-bundle-manifest}</manifestFile>
- </archive>
- </configuration>
- </execution>
- </executions>
- </plugin>
-
- <plugin>
- <artifactId>maven-jar-plugin</artifactId>
- <configuration>
- <archive>
- <manifestFile>${bundle-manifest}</manifestFile>
- </archive>
- </configuration>
- </plugin>
-
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>clirr-maven-plugin</artifactId>
- </plugin>
- </plugins>
-
- <pluginManagement>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-pmd-plugin</artifactId>
- <version>2.7.1</version>
- <configuration>
- <sourceEncoding>utf-8</sourceEncoding>
- <minimumTokens>100</minimumTokens>
- <targetJdk>1.5</targetJdk>
- <format>xml</format>
- <failOnViolation>false</failOnViolation>
- </configuration>
- <executions>
- <execution>
- <goals>
- <goal>cpd-check</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </pluginManagement>
- </build>
-
- <reporting>
- <plugins>
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>clirr-maven-plugin</artifactId>
- <version>${clirr-version}</version>
- <configuration>
- <comparisonVersion>${jgit-last-release-version}</comparisonVersion>
- <minSeverity>info</minSeverity>
- </configuration>
- </plugin>
- </plugins>
- </reporting>
-</project>
diff --git a/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_POSIX_Java7.java b/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_POSIX_Java7.java
deleted file mode 100644
index 300cf93..0000000
--- a/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_POSIX_Java7.java
+++ /dev/null
@@ -1,363 +0,0 @@
-/*
- * Copyright (C) 2012, Robin Rosenberg <robin.rosenberg at dewire.com>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.util;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.nio.charset.Charset;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.attribute.PosixFilePermission;
-import java.util.Set;
-
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.Repository;
-
-/**
- * FS implementation for Java7 on unix like systems
- */
-public class FS_POSIX_Java7 extends FS_POSIX {
-
- /*
- * True if the current user "umask" allows to set execute bit for "others".
- * Can be null if "umask" is not supported (or returns unexpected values) by
- * current user shell.
- *
- * Bug 424395: with the umask of 0002 (user: rwx group: rwx others: rx) egit
- * checked out files as rwx,rwx,r (execution not allowed for "others"). To
- * fix this and properly set "executable" permission bit for "others", we
- * must consider the user umask on checkout
- */
- private static final Boolean EXECUTE_FOR_OTHERS;
-
- /*
- * True if the current user "umask" allows to set execute bit for "group".
- * Can be null if "umask" is not supported (or returns unexpected values) by
- * current user shell.
- */
- private static final Boolean EXECUTE_FOR_GROUP;
-
- static {
- String umask = readUmask();
-
- // umask return value consists of 3 or 4 digits, like "002" or "0002"
- if (umask != null && umask.length() > 0 && umask.matches("\\d{3,4}")) { //$NON-NLS-1$
- EXECUTE_FOR_OTHERS = isGranted(PosixFilePermission.OTHERS_EXECUTE,
- umask);
- EXECUTE_FOR_GROUP = isGranted(PosixFilePermission.GROUP_EXECUTE,
- umask);
- } else {
- EXECUTE_FOR_OTHERS = null;
- EXECUTE_FOR_GROUP = null;
- }
- }
-
- FS_POSIX_Java7(FS_POSIX_Java7 src) {
- super(src);
- }
-
- FS_POSIX_Java7() {
- // empty
- }
-
- @Override
- public FS newInstance() {
- return new FS_POSIX_Java7(this);
- }
-
- @Override
- public boolean supportsExecute() {
- return true;
- }
-
- @Override
- public boolean canExecute(File f) {
- return FileUtil.canExecute(f);
- }
-
- @Override
- public boolean setExecute(File f, boolean canExecute) {
- if (!isFile(f))
- return false;
- // only if the execute has to be set, and we know the umask
- if (canExecute && EXECUTE_FOR_OTHERS != null) {
- try {
- Path path = f.toPath();
- Set<PosixFilePermission> pset = Files
- .getPosixFilePermissions(path);
- // user is always allowed to set execute
- pset.add(PosixFilePermission.OWNER_EXECUTE);
-
- if (EXECUTE_FOR_GROUP.booleanValue())
- pset.add(PosixFilePermission.GROUP_EXECUTE);
-
- if (EXECUTE_FOR_OTHERS.booleanValue())
- pset.add(PosixFilePermission.OTHERS_EXECUTE);
-
- Files.setPosixFilePermissions(path, pset);
- return true;
- } catch (IOException e) {
- // The interface doesn't allow to throw IOException
- final boolean debug = Boolean.parseBoolean(SystemReader
- .getInstance().getProperty("jgit.fs.debug")); //$NON-NLS-1$
- if (debug)
- System.err.println(e);
- return false;
- }
- }
- // if umask is not working for some reason: fall back to default (buggy)
- // implementation which does not consider umask: see bug 424395
- return f.setExecutable(canExecute);
- }
-
- /**
- * Derives requested permission from given octal umask value as defined e.g.
- * in <a href="http://linux.die.net/man/2/umask">http://linux.die.net/man/2/
- * umask</a>.
- * <p>
- * The umask expected here must consist of 3 or 4 digits. Last three digits
- * are significant here because they represent file permissions granted to
- * the "owner", "group" and "others" (in this order).
- * <p>
- * Each single digit from the umask represents 3 bits of the mask standing
- * for "<b>r</b>ead, <b>w</b>rite, e<b>x</b>ecute" permissions (in this
- * order).
- * <p>
- * The possible umask values table:
- *
- * <pre>
- * Value : Bits:Abbr.: Permission
- * 0 : 000 :rwx : read, write and execute
- * 1 : 001 :rw : read and write
- * 2 : 010 :rx : read and execute
- * 3 : 011 :r : read only
- * 4 : 100 :wx : write and execute
- * 5 : 101 :w : write only
- * 6 : 110 :x : execute only
- * 7 : 111 : : no permissions
- * </pre>
- * <p>
- * Note, that umask value is used to "mask" the requested permissions on
- * file creation by combining the requested permission bit with the
- * <b>negated</b> value of the umask bit.
- * <p>
- * Simply speaking, if a bit is <b>not</b> set in the umask, then the
- * appropriate right <b>will</b> be granted <b>if</b> requested. If a bit is
- * set in the umask value, then the appropriate permission will be not
- * granted.
- * <p>
- * Example:
- * <li>umask 023 ("000 010 011" or rwx rx r) combined with the request to
- * create an executable file with full set of permissions for everyone (777)
- * results in the file with permissions 754 (rwx rx r).
- * <li>umask 002 ("000 000 010" or rwx rwx rx) combined with the request to
- * create an executable file with full set of permissions for everyone (777)
- * results in the file with permissions 775 (rwx rwx rx).
- * <li>umask 002 ("000 000 010" or rwx rwx rx) combined with the request to
- * create a file without executable rights for everyone (666) results in the
- * file with permissions 664 (rw rw r).
- *
- * @param p
- * non null permission
- * @param umask
- * octal umask value represented by at least three digits. The
- * digits (read from the end to beginning of the umask) represent
- * permissions for "others", "group" and "owner".
- *
- * @return true if the requested permission is set according to given umask
- */
- private static Boolean isGranted(PosixFilePermission p, String umask) {
- char val;
- switch (p) {
- case OTHERS_EXECUTE:
- // Read last digit, because umask is ordered as: User/Group/Others.
- val = umask.charAt(umask.length() - 1);
- return isExecuteGranted(val);
- case GROUP_EXECUTE:
- val = umask.charAt(umask.length() - 2);
- return isExecuteGranted(val);
- default:
- throw new UnsupportedOperationException(
- "isGranted() for " + p + " is not implemented!"); //$NON-NLS-1$ //$NON-NLS-2$
- }
- }
-
- /**
- * @param c
- * character representing octal permission value from the table
- * in {@link #isGranted(PosixFilePermission, String)}
- * @return true if the "execute" permission is granted according to given
- * character
- */
- private static Boolean isExecuteGranted(char c) {
- if (c == '0' || c == '2' || c == '4' || c == '6')
- return Boolean.TRUE;
- return Boolean.FALSE;
- }
-
- private static String readUmask() {
- Process p;
- try {
- p = Runtime.getRuntime().exec(
- new String[] { "sh", "-c", "umask" }, null, null); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
- try (BufferedReader lineRead = new BufferedReader(
- new InputStreamReader(p.getInputStream(), Charset
- .defaultCharset().name()))) {
- p.waitFor();
- return lineRead.readLine();
- }
- } catch (Exception e) {
- return null;
- }
- }
-
- @Override
- public boolean retryFailedLockFileCommit() {
- return false;
- }
-
- @Override
- public boolean supportsSymlinks() {
- return true;
- }
-
- @Override
- public boolean isSymLink(File path) throws IOException {
- return FileUtil.isSymlink(path);
- }
-
- @Override
- public long lastModified(File path) throws IOException {
- return FileUtil.lastModified(path);
- }
-
- @Override
- public void setLastModified(File path, long time) throws IOException {
- FileUtil.setLastModified(path, time);
- }
-
- @Override
- public void delete(File path) throws IOException {
- FileUtil.delete(path);
- }
-
- @Override
- public long length(File f) throws IOException {
- return FileUtil.getLength(f);
- }
-
- @Override
- public boolean exists(File path) {
- return FileUtil.exists(path);
- }
-
- @Override
- public boolean isDirectory(File path) {
- return FileUtil.isDirectory(path);
- }
-
- @Override
- public boolean isFile(File path) {
- return FileUtil.isFile(path);
- }
-
- @Override
- public boolean isHidden(File path) throws IOException {
- return FileUtil.isHidden(path);
- }
-
- @Override
- public void setHidden(File path, boolean hidden) throws IOException {
- // no action on POSIX
- }
-
- @Override
- public String readSymLink(File path) throws IOException {
- return FileUtil.readSymlink(path);
- }
-
- @Override
- public void createSymLink(File path, String target) throws IOException {
- FileUtil.createSymLink(path, target);
- }
-
- /**
- * @since 3.3
- */
- @Override
- public Attributes getAttributes(File path) {
- return FileUtil.getFileAttributesPosix(this, path);
- }
-
- /**
- * @since 3.3
- */
- @Override
- public File normalize(File file) {
- return FileUtil.normalize(file);
- }
-
- /**
- * @since 3.3
- */
- @Override
- public String normalize(String name) {
- return FileUtil.normalize(name);
- }
-
- /**
- * @since 3.7
- */
- @Override
- public File findHook(Repository repository, Hook hook) {
- final File gitdir = repository.getDirectory();
- final Path hookPath = gitdir.toPath().resolve(Constants.HOOKS)
- .resolve(hook.getName());
- if (Files.isExecutable(hookPath))
- return hookPath.toFile();
- return null;
- }
-}
diff --git a/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_Win32_Java7.java b/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_Win32_Java7.java
deleted file mode 100644
index 0711867..0000000
--- a/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_Win32_Java7.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (C) 2012, Robin Rosenberg <robin.rosenberg at dewire.com>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.util;
-
-import java.io.File;
-import java.io.IOException;
-
-/**
- * FS for Java7 on Windows
- */
-public class FS_Win32_Java7 extends FS_Win32 {
-
- private volatile Boolean supportSymlinks;
-
- FS_Win32_Java7(FS src) {
- super(src);
- }
-
- FS_Win32_Java7() {
- }
-
- @Override
- public FS newInstance() {
- return new FS_Win32_Java7(this);
- }
-
- @Override
- public boolean supportsSymlinks() {
- if (supportSymlinks == null)
- detectSymlinkSupport();
- return Boolean.TRUE.equals(supportSymlinks);
- }
-
- private void detectSymlinkSupport() {
- File tempFile = null;
- try {
- tempFile = File.createTempFile("tempsymlinktarget", ""); //$NON-NLS-1$ //$NON-NLS-2$
- File linkName = new File(tempFile.getParentFile(), "tempsymlink"); //$NON-NLS-1$
- FileUtil.createSymLink(linkName, tempFile.getPath());
- supportSymlinks = Boolean.TRUE;
- linkName.delete();
- } catch (IOException | UnsupportedOperationException e) {
- supportSymlinks = Boolean.FALSE;
- } finally {
- if (tempFile != null)
- try {
- FileUtils.delete(tempFile);
- } catch (IOException e) {
- throw new RuntimeException(e); // panic
- }
- }
- }
-
- @Override
- public boolean isSymLink(File path) throws IOException {
- return FileUtil.isSymlink(path);
- }
-
- @Override
- public long lastModified(File path) throws IOException {
- return FileUtil.lastModified(path);
- }
-
- @Override
- public void setLastModified(File path, long time) throws IOException {
- FileUtil.setLastModified(path, time);
- }
-
- @Override
- public void delete(File path) throws IOException {
- FileUtil.delete(path);
- }
-
- @Override
- public long length(File f) throws IOException {
- return FileUtil.getLength(f);
- }
-
- @Override
- public boolean exists(File path) {
- return FileUtil.exists(path);
- }
-
- @Override
- public boolean isDirectory(File path) {
- return FileUtil.isDirectory(path);
- }
-
- @Override
- public boolean isFile(File path) {
- return FileUtil.isFile(path);
- }
-
- @Override
- public boolean isHidden(File path) throws IOException {
- return FileUtil.isHidden(path);
- }
-
- @Override
- public void setHidden(File path, boolean hidden) throws IOException {
- FileUtil.setHidden(path, hidden);
- }
-
- @Override
- public String readSymLink(File path) throws IOException {
- return FileUtil.readSymlink(path);
- }
-
- @Override
- public void createSymLink(File path, String target) throws IOException {
- FileUtil.createSymLink(path, target);
- }
-
- /**
- * @since 3.3
- */
- @Override
- public Attributes getAttributes(File path) {
- return FileUtil.getFileAttributesBasic(this, path);
- }
-}
diff --git a/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_Win32_Java7Cygwin.java b/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_Win32_Java7Cygwin.java
deleted file mode 100644
index b6e5d93..0000000
--- a/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FS_Win32_Java7Cygwin.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2012, Robin Rosenberg <robin.rosenberg at dewire.com>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.util;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.Repository;
-
-/**
- * FS for Java7 on Windows with Cygwin
- */
-public class FS_Win32_Java7Cygwin extends FS_Win32_Cygwin {
-
- FS_Win32_Java7Cygwin(FS src) {
- super(src);
- }
-
- FS_Win32_Java7Cygwin() {
- }
-
- @Override
- public FS newInstance() {
- return new FS_Win32_Java7Cygwin(this);
- }
-
- @Override
- public boolean supportsSymlinks() {
- return true;
- }
-
- @Override
- public boolean isSymLink(File path) throws IOException {
- return FileUtil.isSymlink(path);
- }
-
- @Override
- public long lastModified(File path) throws IOException {
- return FileUtil.lastModified(path);
- }
-
- @Override
- public void setLastModified(File path, long time) throws IOException {
- FileUtil.setLastModified(path, time);
- }
-
- @Override
- public void delete(File path) throws IOException {
- FileUtil.delete(path);
- }
-
- @Override
- public long length(File f) throws IOException {
- return FileUtil.getLength(f);
- }
-
- @Override
- public boolean exists(File path) {
- return FileUtil.exists(path);
- }
-
- @Override
- public boolean isDirectory(File path) {
- return FileUtil.isDirectory(path);
- }
-
- @Override
- public boolean isFile(File path) {
- return FileUtil.isFile(path);
- }
-
- @Override
- public boolean isHidden(File path) throws IOException {
- return FileUtil.isHidden(path);
- }
-
- @Override
- public void setHidden(File path, boolean hidden) throws IOException {
- FileUtil.setHidden(path, hidden);
- }
-
- @Override
- public String readSymLink(File path) throws IOException {
- return FileUtil.readSymlink(path);
- }
-
- @Override
- public void createSymLink(File path, String target) throws IOException {
- FileUtil.createSymLink(path, target);
- }
-
- /**
- * @since 3.3
- */
- @Override
- public Attributes getAttributes(File path) {
- return FileUtil.getFileAttributesBasic(this, path);
- }
-
- /**
- * @since 3.7
- */
- @Override
- public File findHook(Repository repository, Hook hook) {
- final File gitdir = repository.getDirectory();
- final Path hookPath = gitdir.toPath().resolve(Constants.HOOKS)
- .resolve(hook.getName());
- if (Files.isExecutable(hookPath))
- return hookPath.toFile();
- return null;
- }
-}
diff --git a/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FileUtil.java b/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FileUtil.java
deleted file mode 100644
index 67fcc92..0000000
--- a/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/FileUtil.java
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * Copyright (C) 2012, Robin Rosenberg <robin.rosenberg at dewire.com>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.util;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.LinkOption;
-import java.nio.file.NoSuchFileException;
-import java.nio.file.Path;
-import java.nio.file.attribute.BasicFileAttributeView;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.nio.file.attribute.FileTime;
-import java.nio.file.attribute.PosixFileAttributeView;
-import java.nio.file.attribute.PosixFileAttributes;
-import java.nio.file.attribute.PosixFilePermission;
-import java.text.Normalizer;
-import java.text.Normalizer.Form;
-
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.util.FS.Attributes;
-
-class FileUtil {
-
- static class Java7BasicAttributes extends Attributes {
-
- Java7BasicAttributes(FS fs, File fPath, boolean exists,
- boolean isDirectory, boolean isExecutable,
- boolean isSymbolicLink, boolean isRegularFile,
- long creationTime, long lastModifiedTime, long length) {
- super(fs, fPath, exists, isDirectory, isExecutable, isSymbolicLink,
- isRegularFile, creationTime, lastModifiedTime, length);
- }
- }
-
- static String readSymlink(File path) throws IOException {
- Path nioPath = path.toPath();
- Path target = Files.readSymbolicLink(nioPath);
- String targetString = target.toString();
- if (SystemReader.getInstance().isWindows())
- targetString = targetString.replace('\\', '/');
- else if (SystemReader.getInstance().isMacOS())
- targetString = Normalizer.normalize(targetString, Form.NFC);
- return targetString;
- }
-
- public static void createSymLink(File path, String target)
- throws IOException {
- Path nioPath = path.toPath();
- if (Files.exists(nioPath, LinkOption.NOFOLLOW_LINKS))
- Files.delete(nioPath);
- if (SystemReader.getInstance().isWindows())
- target = target.replace('/', '\\');
- Path nioTarget = new File(target).toPath();
- Files.createSymbolicLink(nioPath, nioTarget);
- }
-
- public static boolean isSymlink(File path) {
- Path nioPath = path.toPath();
- return Files.isSymbolicLink(nioPath);
- }
-
- public static long lastModified(File path) throws IOException {
- Path nioPath = path.toPath();
- return Files.getLastModifiedTime(nioPath, LinkOption.NOFOLLOW_LINKS)
- .toMillis();
- }
-
- public static void setLastModified(File path, long time) throws IOException {
- Path nioPath = path.toPath();
- Files.setLastModifiedTime(nioPath, FileTime.fromMillis(time));
- }
-
- public static boolean exists(File path) {
- Path nioPath = path.toPath();
- return Files.exists(nioPath, LinkOption.NOFOLLOW_LINKS);
- }
-
- public static boolean isHidden(File path) throws IOException {
- Path nioPath = path.toPath();
- return Files.isHidden(nioPath);
- }
-
- public static void setHidden(File path, boolean hidden) throws IOException {
- Path nioPath = path.toPath();
- Files.setAttribute(nioPath, "dos:hidden", Boolean.valueOf(hidden), //$NON-NLS-1$
- LinkOption.NOFOLLOW_LINKS);
- }
-
- public static long getLength(File path) throws IOException {
- Path nioPath = path.toPath();
- if (Files.isSymbolicLink(nioPath))
- return Files.readSymbolicLink(nioPath).toString()
- .getBytes(Constants.CHARSET).length;
- return Files.size(nioPath);
- }
-
- public static boolean isDirectory(File path) {
- Path nioPath = path.toPath();
- return Files.isDirectory(nioPath, LinkOption.NOFOLLOW_LINKS);
- }
-
- public static boolean isFile(File path) {
- Path nioPath = path.toPath();
- return Files.isRegularFile(nioPath, LinkOption.NOFOLLOW_LINKS);
- }
-
- public static boolean canExecute(File path) {
- if (!isFile(path))
- return false;
- return path.canExecute();
- }
-
- /**
- * @param path
- * @param executable
- * @return true if succeeded, false if not supported or failed
- * @deprecated the implementation is highly platform dependent, consider
- * using {@link FS#setExecute(File, boolean)} instead
- */
- @Deprecated
- public static boolean setExecute(File path, boolean executable) {
- if (!isFile(path))
- return false;
- return path.setExecutable(executable);
- }
-
- public static void delete(File path) throws IOException {
- Path nioPath = path.toPath();
- Files.delete(nioPath);
- }
-
- static Attributes getFileAttributesBasic(FS fs, File path) {
- try {
- Path nioPath = path.toPath();
- BasicFileAttributes readAttributes = nioPath
- .getFileSystem()
- .provider()
- .getFileAttributeView(nioPath,
- BasicFileAttributeView.class,
- LinkOption.NOFOLLOW_LINKS).readAttributes();
- Attributes attributes = new FileUtil.Java7BasicAttributes(fs, path,
- true,
- readAttributes.isDirectory(),
- fs.supportsExecute() ? path.canExecute() : false,
- readAttributes.isSymbolicLink(),
- readAttributes.isRegularFile(), //
- readAttributes.creationTime().toMillis(), //
- readAttributes.lastModifiedTime().toMillis(),
- readAttributes.isSymbolicLink() ? Constants
- .encode(FileUtils.readSymLink(path)).length
- : readAttributes.size());
- return attributes;
- } catch (NoSuchFileException e) {
- return new FileUtil.Java7BasicAttributes(fs, path, false, false,
- false, false, false, 0L, 0L, 0L);
- } catch (IOException e) {
- return new Attributes(path, fs);
- }
- }
-
- static Attributes getFileAttributesPosix(FS fs, File path) {
- try {
- Path nioPath = path.toPath();
- PosixFileAttributes readAttributes = nioPath
- .getFileSystem()
- .provider()
- .getFileAttributeView(nioPath,
- PosixFileAttributeView.class,
- LinkOption.NOFOLLOW_LINKS).readAttributes();
- Attributes attributes = new FileUtil.Java7BasicAttributes(
- fs,
- path,
- true, //
- readAttributes.isDirectory(), //
- readAttributes.permissions().contains(
- PosixFilePermission.OWNER_EXECUTE),
- readAttributes.isSymbolicLink(),
- readAttributes.isRegularFile(), //
- readAttributes.creationTime().toMillis(), //
- readAttributes.lastModifiedTime().toMillis(),
- readAttributes.size());
- return attributes;
- } catch (NoSuchFileException e) {
- return new FileUtil.Java7BasicAttributes(fs, path, false, false,
- false, false, false, 0L, 0L, 0L);
- } catch (IOException e) {
- return new Attributes(path, fs);
- }
- }
-
- public static File normalize(File file) {
- if (SystemReader.getInstance().isMacOS()) {
- // TODO: Would it be faster to check with isNormalized first
- // assuming normalized paths are much more common
- String normalized = Normalizer.normalize(file.getPath(),
- Normalizer.Form.NFC);
- return new File(normalized);
- }
- return file;
- }
-
- public static String normalize(String name) {
- if (SystemReader.getInstance().isMacOS()) {
- if (name == null)
- return null;
- return Normalizer.normalize(name, Normalizer.Form.NFC);
- }
- return name;
- }
-
-}
diff --git a/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/Java7FSFactory.java b/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/Java7FSFactory.java
deleted file mode 100644
index 2e8c0ec..0000000
--- a/org.eclipse.jgit.java7/src/org/eclipse/jgit/util/Java7FSFactory.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2012, Robin Rosenberg <robin.rosenberg at dewire.com>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.util;
-
-import org.eclipse.jgit.util.FS;
-import org.eclipse.jgit.util.FS.FSFactory;
-import org.eclipse.jgit.util.SystemReader;
-
-/**
- * A factory for creating FS instances on Java7
- */
-public class Java7FSFactory extends FSFactory {
- @Override
- public FS detect(Boolean cygwinUsed) {
- if (SystemReader.getInstance().isWindows()) {
- if (cygwinUsed == null)
- cygwinUsed = Boolean.valueOf(FS_Win32_Cygwin.isCygwin());
- if (cygwinUsed.booleanValue())
- return new FS_Win32_Java7Cygwin();
- else
- return new FS_Win32_Java7();
- } else
- return new FS_POSIX_Java7();
- }
-}
diff --git a/org.eclipse.jgit.junit.http/.classpath b/org.eclipse.jgit.junit.http/.classpath
index 2d1a430..b1dabee 100644
--- a/org.eclipse.jgit.junit.http/.classpath
+++ b/org.eclipse.jgit.junit.http/.classpath
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
diff --git a/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs
index 5fb6234..dcc0d3a 100644
--- a/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.junit.http/.settings/org.eclipse.jdt.core.prefs
@@ -7,9 +7,9 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
@@ -111,7 +111,7 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
-org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.compiler.source=1.7
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
diff --git a/org.eclipse.jgit.junit.http/BUCK b/org.eclipse.jgit.junit.http/BUCK
new file mode 100644
index 0000000..68976a6
--- /dev/null
+++ b/org.eclipse.jgit.junit.http/BUCK
@@ -0,0 +1,18 @@
+java_library(
+ name = 'junit-http',
+ srcs = glob(['src/**']),
+ resources = glob(['resources/**']),
+ provided_deps = [
+ '//org.eclipse.jgit:jgit',
+ '//org.eclipse.jgit.http.server:jgit-servlet',
+ '//org.eclipse.jgit.junit:junit',
+ '//lib:junit',
+ '//lib:servlet-api',
+ '//lib/jetty:http',
+ '//lib/jetty:server',
+ '//lib/jetty:servlet',
+ '//lib/jetty:security',
+ '//lib/jetty:util',
+ ],
+ visibility = ['PUBLIC'],
+)
diff --git a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
index 0fab1f7..c0d59b9 100644
--- a/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit.http/META-INF/MANIFEST.MF
@@ -2,31 +2,41 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Bundle-SymbolicName: org.eclipse.jgit.junit.http
-Bundle-Version: 3.7.1.201504261725-r
+Bundle-Version: 4.2.0.201601211800-r
Bundle-Localization: plugin
Bundle-Vendor: %provider_name
Bundle-ActivationPolicy: lazy
-Bundle-RequiredExecutionEnvironment: J2SE-1.5
-Import-Package: javax.servlet;version="[2.5.0,3.0.0)",
- javax.servlet.http;version="[2.5.0,3.0.0)",
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
+Import-Package: javax.servlet;version="[2.5.0,3.2.0)",
+ javax.servlet.http;version="[2.5.0,3.2.0)",
org.apache.commons.logging;version="[1.1.1,2.0.0)",
- org.eclipse.jetty.http;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.security;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.security.authentication;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.server;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.server.handler;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.server.nio;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.servlet;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.util.component;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.util.log;version="[7.6.0,8.0.0)",
- org.eclipse.jetty.util.security;version="[7.6.0,8.0.0)",
- org.eclipse.jgit.errors;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.http.server;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.internal.storage.file;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.junit;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.lib;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.revwalk;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.transport;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.transport.resolver;version="[3.7.1,3.8.0)",
+ org.eclipse.jetty.http;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.security;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.security.authentication;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.server;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.server.handler;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.server.nio;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.servlet;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.util.component;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.util.log;version="[9.0.0,10.0.0)",
+ org.eclipse.jetty.util.security;version="[9.0.0,10.0.0)",
+ org.eclipse.jgit.errors;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.http.server;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.junit;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.lib;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.revwalk;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.transport;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.2.0,4.3.0)",
org.junit;version="[4.0.0,5.0.0)"
-Export-Package: org.eclipse.jgit.junit.http;version="3.7.1"
+Export-Package: org.eclipse.jgit.junit.http;version="4.2.0";
+ uses:="org.eclipse.jgit.transport,
+ org.eclipse.jgit.junit,
+ javax.servlet.http,
+ org.eclipse.jgit.lib,
+ org.eclipse.jgit.revwalk,
+ org.eclipse.jetty.server.handler,
+ javax.servlet,
+ org.eclipse.jetty.server,
+ org.eclipse.jetty.util.log,
+ org.eclipse.jetty.servlet"
diff --git a/org.eclipse.jgit.junit.http/pom.xml b/org.eclipse.jgit.junit.http/pom.xml
index 1cc96d7..7953d8c 100644
--- a/org.eclipse.jgit.junit.http/pom.xml
+++ b/org.eclipse.jgit.junit.http/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>3.7.1.201504261725-r</version>
+ <version>4.2.0.201601211800-r</version>
</parent>
<artifactId>org.eclipse.jgit.junit.http</artifactId>
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java
index 6b0e060..ce04bdf 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/AppServer.java
@@ -60,10 +60,12 @@ import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.security.MappedLoginService;
import org.eclipse.jetty.security.authentication.BasicAuthenticator;
import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
-import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.util.security.Password;
@@ -95,14 +97,22 @@ public class AppServer {
private final Server server;
- private final Connector connector;
+ private final ServerConnector connector;
private final ContextHandlerCollection contexts;
private final TestRequestLog log;
public AppServer() {
- connector = new SelectChannelConnector();
+ server = new Server();
+
+ HttpConfiguration http_config = new HttpConfiguration();
+ http_config.setSecureScheme("https");
+ http_config.setSecurePort(8443);
+ http_config.setOutputBufferSize(32768);
+
+ connector = new ServerConnector(server,
+ new HttpConnectionFactory(http_config));
connector.setPort(0);
try {
final InetAddress me = InetAddress.getByName("localhost");
@@ -116,7 +126,6 @@ public class AppServer {
log = new TestRequestLog();
log.setHandler(contexts);
- server = new Server();
server.setConnectors(new Connector[] { connector });
server.setHandler(log);
}
@@ -173,7 +182,6 @@ public class AppServer {
cm.setPathSpec("/*");
ConstraintSecurityHandler sec = new ConstraintSecurityHandler();
- sec.setStrict(false);
sec.setRealmName(realm);
sec.setAuthenticator(authType);
sec.setLoginService(users);
@@ -232,7 +240,7 @@ public class AppServer {
/** @return the local port number the server is listening on. */
public int getPort() {
assertAlreadySetUp();
- return ((SelectChannelConnector) connector).getLocalPort();
+ return connector.getLocalPort();
}
/** @return all requests since the server was started. */
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/RecordingLogger.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/RecordingLogger.java
index 0accfc8..7600843 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/RecordingLogger.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/RecordingLogger.java
@@ -187,4 +187,9 @@ public class RecordingLogger implements Logger {
public void ignore(Throwable arg0) {
// Ignore (not relevant to test failures)
}
+
+ @Override
+ public void debug(String msg, long value) {
+ // Ignore (not relevant to test failures)
+ }
}
diff --git a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java
index f71bc93..14ea03a 100644
--- a/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java
+++ b/org.eclipse.jgit.junit.http/src/org/eclipse/jgit/junit/http/TestRequestLog.java
@@ -48,11 +48,11 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;
+import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.eclipse.jetty.server.DispatcherType;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.handler.HandlerWrapper;
diff --git a/org.eclipse.jgit.junit/.classpath b/org.eclipse.jgit.junit/.classpath
index 64c5e31..098194c 100644
--- a/org.eclipse.jgit.junit/.classpath
+++ b/org.eclipse.jgit.junit/.classpath
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
diff --git a/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs
index 5fb6234..dcc0d3a 100644
--- a/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.junit/.settings/org.eclipse.jdt.core.prefs
@@ -7,9 +7,9 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
@@ -111,7 +111,7 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
-org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.compiler.source=1.7
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
diff --git a/org.eclipse.jgit.junit/BUCK b/org.eclipse.jgit.junit/BUCK
new file mode 100644
index 0000000..7e25432
--- /dev/null
+++ b/org.eclipse.jgit.junit/BUCK
@@ -0,0 +1,10 @@
+java_library(
+ name = 'junit',
+ srcs = glob(['src/**']),
+ resources = glob(['resources/**']),
+ provided_deps = [
+ '//org.eclipse.jgit:jgit',
+ '//lib:junit',
+ ],
+ visibility = ['PUBLIC'],
+)
diff --git a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
index 80bdae0..f380b5b 100644
--- a/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.junit/META-INF/MANIFEST.MF
@@ -2,23 +2,32 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Bundle-SymbolicName: org.eclipse.jgit.junit
-Bundle-Version: 3.7.1.201504261725-r
+Bundle-Version: 4.2.0.201601211800-r
Bundle-Localization: plugin
Bundle-Vendor: %provider_name
Bundle-ActivationPolicy: lazy
-Bundle-RequiredExecutionEnvironment: J2SE-1.5
-Import-Package: org.eclipse.jgit.api;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.api.errors;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.dircache;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.errors;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.internal.storage.file;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.internal.storage.pack;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.lib;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.revwalk;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.storage.file;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.treewalk;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.treewalk.filter;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.util;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.util.io;version="[3.7.1,3.8.0)",
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
+Import-Package: org.eclipse.jgit.api;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.api.errors;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.dircache;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.errors;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.lib;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.merge;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.revwalk;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.storage.file;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.treewalk;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.util;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.util.io;version="[4.2.0,4.3.0)",
org.junit;version="[4.0.0,5.0.0)"
-Export-Package: org.eclipse.jgit.junit;version="3.7.1"
+Export-Package: org.eclipse.jgit.junit;version="4.2.0";
+ uses:="org.eclipse.jgit.dircache,
+ org.eclipse.jgit.lib,
+ org.eclipse.jgit.revwalk,
+ org.eclipse.jgit.internal.storage.file,
+ org.eclipse.jgit.treewalk,
+ org.eclipse.jgit.util,
+ org.eclipse.jgit.storage.file,
+ org.eclipse.jgit.api"
diff --git a/org.eclipse.jgit.junit/pom.xml b/org.eclipse.jgit.junit/pom.xml
index ee7101b..84e7a31 100644
--- a/org.eclipse.jgit.junit/pom.xml
+++ b/org.eclipse.jgit.junit/pom.xml
@@ -52,7 +52,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>3.7.1.201504261725-r</version>
+ <version>4.2.0.201601211800-r</version>
</parent>
<artifactId>org.eclipse.jgit.junit</artifactId>
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java
index 136c647..2962e71 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java
@@ -55,6 +55,7 @@ import java.io.Writer;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.net.URL;
+import java.nio.file.Path;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.util.FileUtils;
@@ -240,4 +241,21 @@ public abstract class JGitTestUtil {
FileUtils.delete(path);
}
+ /**
+ * @param db
+ * the repository
+ * @param link
+ * the path of the symbolic link to create
+ * @param target
+ * the target of the symbolic link
+ * @return the path to the symbolic link
+ * @throws Exception
+ * @since 4.2
+ */
+ public static Path writeLink(Repository db, String link,
+ String target) throws Exception {
+ return FileUtils.createSymLink(new File(db.getWorkTree(), link),
+ target);
+ }
+
}
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
index b98db7d..4d713b5 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
@@ -50,18 +50,12 @@ import static org.junit.Assert.fail;
import java.io.File;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
+import java.util.*;
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.internal.storage.file.FileRepository;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.RepositoryCache;
+import org.eclipse.jgit.lib.*;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.storage.file.WindowCacheConfig;
import org.eclipse.jgit.util.FS;
@@ -97,11 +91,15 @@ public abstract class LocalDiskRepositoryTestCase {
/** A fake (but stable) identity for committer fields in the test. */
protected PersonIdent committer;
+ /**
+ * A {@link SystemReader} used to coordinate time, envars, etc.
+ * @since 4.2
+ */
+ protected MockSystemReader mockSystemReader;
+
private final List<Repository> toClose = new ArrayList<Repository>();
private File tmp;
- private MockSystemReader mockSystemReader;
-
@Before
public void setUp() throws Exception {
tmp = File.createTempFile("jgit_test_", "_tmp");
@@ -176,9 +174,8 @@ public abstract class LocalDiskRepositoryTestCase {
/** Increment the {@link #author} and {@link #committer} times. */
protected void tick() {
- final long delta = TimeUnit.MILLISECONDS.convert(5 * 60,
- TimeUnit.SECONDS);
- final long now = author.getWhen().getTime() + delta;
+ mockSystemReader.tick(5 * 60);
+ final long now = mockSystemReader.getCurrentTime();
final int tz = mockSystemReader.getTimezone(now);
author = new PersonIdent(author, now, tz);
@@ -229,6 +226,101 @@ public abstract class LocalDiskRepositoryTestCase {
System.err.println(msg);
}
+ public static final int MOD_TIME = 1;
+
+ public static final int SMUDGE = 2;
+
+ public static final int LENGTH = 4;
+
+ public static final int CONTENT_ID = 8;
+
+ public static final int CONTENT = 16;
+
+ public static final int ASSUME_UNCHANGED = 32;
+
+ /**
+ * Represent the state of the index in one String. This representation is
+ * useful when writing tests which do assertions on the state of the index.
+ * By default information about path, mode, stage (if different from 0) is
+ * included. A bitmask controls which additional info about
+ * modificationTimes, smudge state and length is included.
+ * <p>
+ * The format of the returned string is described with this BNF:
+ *
+ * <pre>
+ * result = ( "[" path mode stage? time? smudge? length? sha1? content? "]" )* .
+ * mode = ", mode:" number .
+ * stage = ", stage:" number .
+ * time = ", time:t" timestamp-index .
+ * smudge = "" | ", smudged" .
+ * length = ", length:" number .
+ * sha1 = ", sha1:" hex-sha1 .
+ * content = ", content:" blob-data .
+ * </pre>
+ *
+ * 'stage' is only presented when the stage is different from 0. All
+ * reported time stamps are mapped to strings like "t0", "t1", ... "tn". The
+ * smallest reported time-stamp will be called "t0". This allows to write
+ * assertions against the string although the concrete value of the time
+ * stamps is unknown.
+ *
+ * @param repo
+ * the repository the index state should be determined for
+ *
+ * @param includedOptions
+ * a bitmask constructed out of the constants {@link #MOD_TIME},
+ * {@link #SMUDGE}, {@link #LENGTH}, {@link #CONTENT_ID} and
+ * {@link #CONTENT} controlling which info is present in the
+ * resulting string.
+ * @return a string encoding the index state
+ * @throws IllegalStateException
+ * @throws IOException
+ */
+ public static String indexState(Repository repo, int includedOptions)
+ throws IllegalStateException, IOException {
+ DirCache dc = repo.readDirCache();
+ StringBuilder sb = new StringBuilder();
+ TreeSet<Long> timeStamps = new TreeSet<Long>();
+
+ // iterate once over the dircache just to collect all time stamps
+ if (0 != (includedOptions & MOD_TIME)) {
+ for (int i=0; i<dc.getEntryCount(); ++i)
+ timeStamps.add(Long.valueOf(dc.getEntry(i).getLastModified()));
+ }
+
+ // iterate again, now produce the result string
+ for (int i=0; i<dc.getEntryCount(); ++i) {
+ DirCacheEntry entry = dc.getEntry(i);
+ sb.append("["+entry.getPathString()+", mode:" + entry.getFileMode());
+ int stage = entry.getStage();
+ if (stage != 0)
+ sb.append(", stage:" + stage);
+ if (0 != (includedOptions & MOD_TIME)) {
+ sb.append(", time:t"+
+ timeStamps.headSet(Long.valueOf(entry.getLastModified())).size());
+ }
+ if (0 != (includedOptions & SMUDGE))
+ if (entry.isSmudged())
+ sb.append(", smudged");
+ if (0 != (includedOptions & LENGTH))
+ sb.append(", length:"
+ + Integer.toString(entry.getLength()));
+ if (0 != (includedOptions & CONTENT_ID))
+ sb.append(", sha1:" + ObjectId.toString(entry.getObjectId()));
+ if (0 != (includedOptions & CONTENT)) {
+ sb.append(", content:"
+ + new String(repo.open(entry.getObjectId(),
+ Constants.OBJ_BLOB).getCachedBytes(), "UTF-8"));
+ }
+ if (0 != (includedOptions & ASSUME_UNCHANGED))
+ sb.append(", assume-unchanged:"
+ + Boolean.toString(entry.isAssumeValid()));
+ sb.append("]");
+ }
+ return sb.toString();
+ }
+
+
/**
* Creates a new empty bare repository.
*
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
index 65551d6..28a9556 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
@@ -47,6 +47,7 @@ package org.eclipse.jgit.junit;
import java.io.File;
import java.io.IOException;
+import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.HashMap;
@@ -61,6 +62,9 @@ import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.SystemReader;
+/**
+ * Mock {@link SystemReader} for tests.
+ */
public class MockSystemReader extends SystemReader {
private final class MockConfig extends FileBasedConfig {
private MockConfig(File cfgLocation, FS fs) {
@@ -78,6 +82,8 @@ public class MockSystemReader extends SystemReader {
}
}
+ long now = 1250379778668L; // Sat Aug 15 20:12:58 GMT-03:30 2009
+
final Map<String, String> values = new HashMap<String, String>();
FileBasedConfig userGitConfig;
@@ -137,7 +143,18 @@ public class MockSystemReader extends SystemReader {
@Override
public long getCurrentTime() {
- return 1250379778668L; // Sat Aug 15 20:12:58 GMT-03:30 2009
+ return now;
+ }
+
+ /**
+ * Adjusts the current time in seconds.
+ *
+ * @param secDelta
+ * number of seconds to add to the current time.
+ * @since 4.2
+ */
+ public void tick(final int secDelta) {
+ now += secDelta * 1000L;
}
@Override
@@ -170,6 +187,7 @@ public class MockSystemReader extends SystemReader {
* Assign some properties for the currently executing platform
*/
public void setCurrentPlatform() {
+ resetOsNames();
setProperty("os.name", System.getProperty("os.name"));
setProperty("file.separator", System.getProperty("file.separator"));
setProperty("path.separator", System.getProperty("path.separator"));
@@ -180,6 +198,7 @@ public class MockSystemReader extends SystemReader {
* Emulate Windows
*/
public void setWindows() {
+ resetOsNames();
setProperty("os.name", "Windows");
setProperty("file.separator", "\\");
setProperty("path.separator", ";");
@@ -191,10 +210,25 @@ public class MockSystemReader extends SystemReader {
* Emulate Unix
*/
public void setUnix() {
+ resetOsNames();
setProperty("os.name", "*nix"); // Essentially anything but Windows
setProperty("file.separator", "/");
setProperty("path.separator", ":");
setProperty("line.separator", "\n");
setPlatformChecker();
}
+
+ private void resetOsNames() {
+ Field field;
+ try {
+ field = SystemReader.class.getDeclaredField("isWindows");
+ field.setAccessible(true);
+ field.set(null, null);
+ field = SystemReader.class.getDeclaredField("isMacOS");
+ field.setAccessible(true);
+ field.set(null, null);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
}
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java
index bc225cc..c649eb9 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java
@@ -55,12 +55,11 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
+import java.nio.file.Path;
import java.util.Map;
-import java.util.TreeSet;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
-import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheCheckout;
import org.eclipse.jgit.dircache.DirCacheEntry;
@@ -109,6 +108,22 @@ public abstract class RepositoryTestCase extends LocalDiskRepositoryTestCase {
return JGitTestUtil.writeTrashFile(db, name, data);
}
+ /**
+ * Create a symbolic link
+ *
+ * @param link
+ * the path of the symbolic link to create
+ * @param target
+ * the target of the symbolic link
+ * @return the path to the symbolic link
+ * @throws Exception
+ * @since 4.2
+ */
+ protected Path writeLink(final String link, final String target)
+ throws Exception {
+ return JGitTestUtil.writeLink(db, link, target);
+ }
+
protected File writeTrashFile(final String subdir, final String name,
final String data)
throws IOException {
@@ -154,101 +169,6 @@ public abstract class RepositoryTestCase extends LocalDiskRepositoryTestCase {
trash = db.getWorkTree();
}
- public static final int MOD_TIME = 1;
-
- public static final int SMUDGE = 2;
-
- public static final int LENGTH = 4;
-
- public static final int CONTENT_ID = 8;
-
- public static final int CONTENT = 16;
-
- public static final int ASSUME_UNCHANGED = 32;
-
- /**
- * Represent the state of the index in one String. This representation is
- * useful when writing tests which do assertions on the state of the index.
- * By default information about path, mode, stage (if different from 0) is
- * included. A bitmask controls which additional info about
- * modificationTimes, smudge state and length is included.
- * <p>
- * The format of the returned string is described with this BNF:
- *
- * <pre>
- * result = ( "[" path mode stage? time? smudge? length? sha1? content? "]" )* .
- * mode = ", mode:" number .
- * stage = ", stage:" number .
- * time = ", time:t" timestamp-index .
- * smudge = "" | ", smudged" .
- * length = ", length:" number .
- * sha1 = ", sha1:" hex-sha1 .
- * content = ", content:" blob-data .
- * </pre>
- *
- * 'stage' is only presented when the stage is different from 0. All
- * reported time stamps are mapped to strings like "t0", "t1", ... "tn". The
- * smallest reported time-stamp will be called "t0". This allows to write
- * assertions against the string although the concrete value of the time
- * stamps is unknown.
- *
- * @param repo
- * the repository the index state should be determined for
- *
- * @param includedOptions
- * a bitmask constructed out of the constants {@link #MOD_TIME},
- * {@link #SMUDGE}, {@link #LENGTH}, {@link #CONTENT_ID} and
- * {@link #CONTENT} controlling which info is present in the
- * resulting string.
- * @return a string encoding the index state
- * @throws IllegalStateException
- * @throws IOException
- */
- public String indexState(Repository repo, int includedOptions)
- throws IllegalStateException, IOException {
- DirCache dc = repo.readDirCache();
- StringBuilder sb = new StringBuilder();
- TreeSet<Long> timeStamps = null;
-
- // iterate once over the dircache just to collect all time stamps
- if (0 != (includedOptions & MOD_TIME)) {
- timeStamps = new TreeSet<Long>();
- for (int i=0; i<dc.getEntryCount(); ++i)
- timeStamps.add(Long.valueOf(dc.getEntry(i).getLastModified()));
- }
-
- // iterate again, now produce the result string
- for (int i=0; i<dc.getEntryCount(); ++i) {
- DirCacheEntry entry = dc.getEntry(i);
- sb.append("["+entry.getPathString()+", mode:" + entry.getFileMode());
- int stage = entry.getStage();
- if (stage != 0)
- sb.append(", stage:" + stage);
- if (0 != (includedOptions & MOD_TIME)) {
- sb.append(", time:t"+
- timeStamps.headSet(Long.valueOf(entry.getLastModified())).size());
- }
- if (0 != (includedOptions & SMUDGE))
- if (entry.isSmudged())
- sb.append(", smudged");
- if (0 != (includedOptions & LENGTH))
- sb.append(", length:"
- + Integer.toString(entry.getLength()));
- if (0 != (includedOptions & CONTENT_ID))
- sb.append(", sha1:" + ObjectId.toString(entry.getObjectId()));
- if (0 != (includedOptions & CONTENT)) {
- sb.append(", content:"
- + new String(db.open(entry.getObjectId(),
- Constants.OBJ_BLOB).getCachedBytes(), "UTF-8"));
- }
- if (0 != (includedOptions & ASSUME_UNCHANGED))
- sb.append(", assume-unchanged:"
- + Boolean.toString(entry.isAssumeValid()));
- sb.append("]");
- }
- return sb.toString();
- }
-
/**
* Represent the state of the index in one String. This representation is
* useful when writing tests which do assertions on the state of the index.
@@ -307,26 +227,27 @@ public abstract class RepositoryTestCase extends LocalDiskRepositoryTestCase {
*/
protected void resetIndex(FileTreeIterator treeItr)
throws FileNotFoundException, IOException {
- ObjectInserter inserter = db.newObjectInserter();
- DirCacheBuilder builder = db.lockDirCache().builder();
- DirCacheEntry dce;
-
- while (!treeItr.eof()) {
- long len = treeItr.getEntryLength();
-
- dce = new DirCacheEntry(treeItr.getEntryPathString());
- dce.setFileMode(treeItr.getEntryFileMode());
- dce.setLastModified(treeItr.getEntryLastModified());
- dce.setLength((int) len);
- FileInputStream in = new FileInputStream(treeItr.getEntryFile());
- dce.setObjectId(inserter.insert(Constants.OBJ_BLOB, len, in));
- in.close();
- builder.add(dce);
- treeItr.next(1);
+ try (ObjectInserter inserter = db.newObjectInserter()) {
+ DirCacheBuilder builder = db.lockDirCache().builder();
+ DirCacheEntry dce;
+
+ while (!treeItr.eof()) {
+ long len = treeItr.getEntryLength();
+
+ dce = new DirCacheEntry(treeItr.getEntryPathString());
+ dce.setFileMode(treeItr.getEntryFileMode());
+ dce.setLastModified(treeItr.getEntryLastModified());
+ dce.setLength((int) len);
+ FileInputStream in = new FileInputStream(
+ treeItr.getEntryFile());
+ dce.setObjectId(inserter.insert(Constants.OBJ_BLOB, len, in));
+ in.close();
+ builder.add(dce);
+ treeItr.next(1);
+ }
+ builder.commit();
+ inserter.flush();
}
- builder.commit();
- inserter.flush();
- inserter.release();
}
/**
@@ -362,6 +283,19 @@ public abstract class RepositoryTestCase extends LocalDiskRepositoryTestCase {
}
/**
+ * Replaces '\' by '/'
+ *
+ * @param str
+ * the string in which backslashes should be replaced
+ * @return the resulting string with slashes
+ * @since 4.2
+ */
+ public static String slashify(String str) {
+ str = str.replace('\\', '/');
+ return str;
+ }
+
+ /**
* Waits until it is guaranteed that a subsequent file modification has a
* younger modification timestamp than the modification timestamp of the
* given file. This is done by touching a temporary file, reading the
@@ -410,14 +344,15 @@ public abstract class RepositoryTestCase extends LocalDiskRepositoryTestCase {
protected void checkoutBranch(String branchName)
throws IllegalStateException, IOException {
- RevWalk walk = new RevWalk(db);
- RevCommit head = walk.parseCommit(db.resolve(Constants.HEAD));
- RevCommit branch = walk.parseCommit(db.resolve(branchName));
- DirCacheCheckout dco = new DirCacheCheckout(db, head.getTree().getId(),
- db.lockDirCache(), branch.getTree().getId());
- dco.setFailOnConflict(true);
- dco.checkout();
- walk.release();
+ try (RevWalk walk = new RevWalk(db)) {
+ RevCommit head = walk.parseCommit(db.resolve(Constants.HEAD));
+ RevCommit branch = walk.parseCommit(db.resolve(branchName));
+ DirCacheCheckout dco = new DirCacheCheckout(db,
+ head.getTree().getId(), db.lockDirCache(),
+ branch.getTree().getId());
+ dco.setFailOnConflict(true);
+ dco.checkout();
+ }
// update the HEAD
RefUpdate refUpdate = db.updateRef(Constants.HEAD);
refUpdate.setRefLogMessage("checkout: moving to " + branchName, false);
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
index b30fa59..8439c39 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
@@ -52,12 +52,15 @@ import java.io.IOException;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.TimeZone;
+import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEditor;
@@ -87,6 +90,8 @@ import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefWriter;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.TagBuilder;
+import org.eclipse.jgit.merge.MergeStrategy;
+import org.eclipse.jgit.merge.ThreeWayMerger;
import org.eclipse.jgit.revwalk.ObjectWalk;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -96,6 +101,7 @@ import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
+import org.eclipse.jgit.util.ChangeIdUtil;
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.io.SafeBufferedOutputStream;
@@ -106,9 +112,9 @@ import org.eclipse.jgit.util.io.SafeBufferedOutputStream;
* type of Repository the test data is stored on.
*/
public class TestRepository<R extends Repository> {
- private static final PersonIdent author;
+ private static final PersonIdent defaultAuthor;
- private static final PersonIdent committer;
+ private static final PersonIdent defaultCommitter;
static {
final MockSystemReader m = new MockSystemReader();
@@ -117,20 +123,22 @@ public class TestRepository<R extends Repository> {
final String an = "J. Author";
final String ae = "jauthor at example.com";
- author = new PersonIdent(an, ae, now, tz);
+ defaultAuthor = new PersonIdent(an, ae, now, tz);
final String cn = "J. Committer";
final String ce = "jcommitter at example.com";
- committer = new PersonIdent(cn, ce, now, tz);
+ defaultCommitter = new PersonIdent(cn, ce, now, tz);
}
private final R db;
+ private final Git git;
+
private final RevWalk pool;
private final ObjectInserter inserter;
- private long now;
+ private final MockSystemReader mockSystemReader;
/**
* Wrap a repository with test building tools.
@@ -140,7 +148,7 @@ public class TestRepository<R extends Repository> {
* @throws IOException
*/
public TestRepository(R db) throws IOException {
- this(db, new RevWalk(db));
+ this(db, new RevWalk(db), new MockSystemReader());
}
/**
@@ -153,10 +161,29 @@ public class TestRepository<R extends Repository> {
* @throws IOException
*/
public TestRepository(R db, RevWalk rw) throws IOException {
+ this(db, rw, new MockSystemReader());
+ }
+
+ /**
+ * Wrap a repository with test building tools.
+ *
+ * @param db
+ * the test repository to write into.
+ * @param rw
+ * the RevObject pool to use for object lookup.
+ * @param reader
+ * the MockSystemReader to use for clock and other system
+ * operations.
+ * @throws IOException
+ * @since 4.2
+ */
+ public TestRepository(R db, RevWalk rw, MockSystemReader reader)
+ throws IOException {
this.db = db;
+ this.git = Git.wrap(db);
this.pool = rw;
this.inserter = db.newObjectInserter();
- this.now = 1236977987000L;
+ this.mockSystemReader = reader;
}
/** @return the repository this helper class operates against. */
@@ -169,9 +196,36 @@ public class TestRepository<R extends Repository> {
return pool;
}
- /** @return current time adjusted by {@link #tick(int)}. */
+ /**
+ * @return an API wrapper for the underlying repository. This wrapper does
+ * not allocate any new resources and need not be closed (but closing
+ * it is harmless). */
+ public Git git() {
+ return git;
+ }
+
+ /**
+ * @return current date.
+ * @since 4.2
+ */
+ public Date getDate() {
+ return new Date(mockSystemReader.getCurrentTime());
+ }
+
+ /**
+ * @return current date.
+ *
+ * @deprecated Use {@link #getDate()} instead.
+ */
+ @Deprecated
public Date getClock() {
- return new Date(now);
+ // Remove once Gitiles and Gerrit are using the updated JGit.
+ return getDate();
+ }
+
+ /** @return timezone used for default identities. */
+ public TimeZone getTimeZone() {
+ return mockSystemReader.getTimeZone();
}
/**
@@ -181,18 +235,18 @@ public class TestRepository<R extends Repository> {
* number of seconds to add to the current time.
*/
public void tick(final int secDelta) {
- now += secDelta * 1000L;
+ mockSystemReader.tick(secDelta);
}
/**
- * Set the author and committer using {@link #getClock()}.
+ * Set the author and committer using {@link #getDate()}.
*
* @param c
* the commit builder to store.
*/
public void setAuthorAndCommitter(org.eclipse.jgit.lib.CommitBuilder c) {
- c.setAuthor(new PersonIdent(author, new Date(now)));
- c.setCommitter(new PersonIdent(committer, new Date(now)));
+ c.setAuthor(new PersonIdent(defaultAuthor, getDate()));
+ c.setCommitter(new PersonIdent(defaultCommitter, getDate()));
}
/**
@@ -217,11 +271,9 @@ public class TestRepository<R extends Repository> {
*/
public RevBlob blob(final byte[] content) throws Exception {
ObjectId id;
- try {
- id = inserter.insert(Constants.OBJ_BLOB, content);
- inserter.flush();
- } finally {
- inserter.release();
+ try (ObjectInserter ins = inserter) {
+ id = ins.insert(Constants.OBJ_BLOB, content);
+ ins.flush();
}
return pool.lookupBlob(id);
}
@@ -260,11 +312,9 @@ public class TestRepository<R extends Repository> {
b.add(e);
b.finish();
ObjectId root;
- try {
- root = dc.writeTree(inserter);
- inserter.flush();
- } finally {
- inserter.release();
+ try (ObjectInserter ins = inserter) {
+ root = dc.writeTree(ins);
+ ins.flush();
}
return pool.lookupTree(root);
}
@@ -281,18 +331,19 @@ public class TestRepository<R extends Repository> {
*/
public RevObject get(final RevTree tree, final String path)
throws Exception {
- final TreeWalk tw = new TreeWalk(pool.getObjectReader());
- tw.setFilter(PathFilterGroup.createFromStrings(Collections
- .singleton(path)));
- tw.reset(tree);
- while (tw.next()) {
- if (tw.isSubtree() && !path.equals(tw.getPathString())) {
- tw.enterSubtree();
- continue;
+ try (TreeWalk tw = new TreeWalk(pool.getObjectReader())) {
+ tw.setFilter(PathFilterGroup.createFromStrings(Collections
+ .singleton(path)));
+ tw.reset(tree);
+ while (tw.next()) {
+ if (tw.isSubtree() && !path.equals(tw.getPathString())) {
+ tw.enterSubtree();
+ continue;
+ }
+ final ObjectId entid = tw.getObjectId(0);
+ final FileMode entmode = tw.getFileMode(0);
+ return pool.lookupAny(entid, entmode.getObjectType());
}
- final ObjectId entid = tw.getObjectId(0);
- final FileMode entmode = tw.getFileMode(0);
- return pool.lookupAny(entid, entmode.getObjectType());
}
fail("Can't find " + path + " in tree " + tree.name());
return null; // never reached.
@@ -373,15 +424,13 @@ public class TestRepository<R extends Repository> {
c = new org.eclipse.jgit.lib.CommitBuilder();
c.setTreeId(tree);
c.setParentIds(parents);
- c.setAuthor(new PersonIdent(author, new Date(now)));
- c.setCommitter(new PersonIdent(committer, new Date(now)));
+ c.setAuthor(new PersonIdent(defaultAuthor, getDate()));
+ c.setCommitter(new PersonIdent(defaultCommitter, getDate()));
c.setMessage("");
ObjectId id;
- try {
- id = inserter.insert(c);
- inserter.flush();
- } finally {
- inserter.release();
+ try (ObjectInserter ins = inserter) {
+ id = ins.insert(c);
+ ins.flush();
}
return pool.lookupCommit(id);
}
@@ -411,14 +460,12 @@ public class TestRepository<R extends Repository> {
final TagBuilder t = new TagBuilder();
t.setObjectId(dst);
t.setTag(name);
- t.setTagger(new PersonIdent(committer, new Date(now)));
+ t.setTagger(new PersonIdent(defaultCommitter, getDate()));
t.setMessage("");
ObjectId id;
- try {
- id = inserter.insert(t);
- inserter.flush();
- } finally {
- inserter.release();
+ try (ObjectInserter ins = inserter) {
+ id = ins.insert(t);
+ ins.flush();
}
return (RevTag) pool.lookupAny(id, Constants.OBJ_TAG);
}
@@ -442,6 +489,72 @@ public class TestRepository<R extends Repository> {
}
/**
+ * Amend an existing ref.
+ *
+ * @param ref
+ * the name of the reference to amend, which must already exist.
+ * If {@code ref} does not start with {@code refs/} and is not the
+ * magic names {@code HEAD} {@code FETCH_HEAD} or {@code
+ * MERGE_HEAD}, then {@code refs/heads/} will be prefixed in front
+ * of the given name, thereby assuming it is a branch.
+ * @return commit builder that amends the branch on commit.
+ * @throws Exception
+ */
+ public CommitBuilder amendRef(String ref) throws Exception {
+ String name = normalizeRef(ref);
+ Ref r = db.getRef(name);
+ if (r == null)
+ throw new IOException("Not a ref: " + ref);
+ return amend(pool.parseCommit(r.getObjectId()), branch(name).commit());
+ }
+
+ /**
+ * Amend an existing commit.
+ *
+ * @param id
+ * the id of the commit to amend.
+ * @return commit builder.
+ * @throws Exception
+ */
+ public CommitBuilder amend(AnyObjectId id) throws Exception {
+ return amend(pool.parseCommit(id), commit());
+ }
+
+ private CommitBuilder amend(RevCommit old, CommitBuilder b) throws Exception {
+ pool.parseBody(old);
+ b.author(old.getAuthorIdent());
+ b.committer(old.getCommitterIdent());
+ b.message(old.getFullMessage());
+ // Use the committer name from the old commit, but update it after ticking
+ // the clock in CommitBuilder#create().
+ b.updateCommitterTime = true;
+
+ // Reset parents to original parents.
+ b.noParents();
+ for (int i = 0; i < old.getParentCount(); i++)
+ b.parent(old.getParent(i));
+
+ // Reset tree to original tree; resetting parents reset tree contents to the
+ // first parent.
+ b.tree.clear();
+ try (TreeWalk tw = new TreeWalk(db)) {
+ tw.reset(old.getTree());
+ tw.setRecursive(true);
+ while (tw.next()) {
+ b.edit(new PathEdit(tw.getPathString()) {
+ @Override
+ public void apply(DirCacheEntry ent) {
+ ent.setFileMode(tw.getFileMode(0));
+ ent.setObjectId(tw.getObjectId(0));
+ }
+ });
+ }
+ }
+
+ return b;
+ }
+
+ /**
* Update a reference to point to an object.
*
* @param <T>
@@ -458,17 +571,7 @@ public class TestRepository<R extends Repository> {
* @throws Exception
*/
public <T extends AnyObjectId> T update(String ref, T obj) throws Exception {
- if (Constants.HEAD.equals(ref)) {
- // nothing
- } else if ("FETCH_HEAD".equals(ref)) {
- // nothing
- } else if ("MERGE_HEAD".equals(ref)) {
- // nothing
- } else if (ref.startsWith(Constants.R_REFS)) {
- // nothing
- } else
- ref = Constants.R_HEADS + ref;
-
+ ref = normalizeRef(ref);
RefUpdate u = db.updateRef(ref);
u.setNewObjectId(obj);
switch (u.forceUpdate()) {
@@ -484,6 +587,128 @@ public class TestRepository<R extends Repository> {
}
}
+ private static String normalizeRef(String ref) {
+ if (Constants.HEAD.equals(ref)) {
+ // nothing
+ } else if ("FETCH_HEAD".equals(ref)) {
+ // nothing
+ } else if ("MERGE_HEAD".equals(ref)) {
+ // nothing
+ } else if (ref.startsWith(Constants.R_REFS)) {
+ // nothing
+ } else
+ ref = Constants.R_HEADS + ref;
+ return ref;
+ }
+
+ /**
+ * Soft-reset HEAD to a detached state.
+ * <p>
+ * @param id
+ * ID of detached head.
+ * @throws Exception
+ * @see #reset(String)
+ */
+ public void reset(AnyObjectId id) throws Exception {
+ RefUpdate ru = db.updateRef(Constants.HEAD, true);
+ ru.setNewObjectId(id);
+ RefUpdate.Result result = ru.forceUpdate();
+ switch (result) {
+ case FAST_FORWARD:
+ case FORCED:
+ case NEW:
+ case NO_CHANGE:
+ break;
+ default:
+ throw new IOException(String.format(
+ "Checkout \"%s\" failed: %s", id.name(), result));
+ }
+ }
+
+ /**
+ * Soft-reset HEAD to a different commit.
+ * <p>
+ * This is equivalent to {@code git reset --soft} in that it modifies HEAD but
+ * not the index or the working tree of a non-bare repository.
+ *
+ * @param name
+ * revision string; either an existing ref name, or something that
+ * can be parsed to an object ID.
+ * @throws Exception
+ */
+ public void reset(String name) throws Exception {
+ RefUpdate.Result result;
+ ObjectId id = db.resolve(name);
+ if (id == null)
+ throw new IOException("Not a revision: " + name);
+ RefUpdate ru = db.updateRef(Constants.HEAD, false);
+ ru.setNewObjectId(id);
+ result = ru.forceUpdate();
+ switch (result) {
+ case FAST_FORWARD:
+ case FORCED:
+ case NEW:
+ case NO_CHANGE:
+ break;
+ default:
+ throw new IOException(String.format(
+ "Checkout \"%s\" failed: %s", name, result));
+ }
+ }
+
+ /**
+ * Cherry-pick a commit onto HEAD.
+ * <p>
+ * This differs from {@code git cherry-pick} in that it works in a bare
+ * repository. As a result, any merge failure results in an exception, as
+ * there is no way to recover.
+ *
+ * @param id
+ * commit-ish to cherry-pick.
+ * @return newly created commit, or null if no work was done due to the
+ * resulting tree being identical.
+ * @throws Exception
+ */
+ public RevCommit cherryPick(AnyObjectId id) throws Exception {
+ RevCommit commit = pool.parseCommit(id);
+ pool.parseBody(commit);
+ if (commit.getParentCount() != 1)
+ throw new IOException(String.format(
+ "Expected 1 parent for %s, found: %s",
+ id.name(), Arrays.asList(commit.getParents())));
+ RevCommit parent = commit.getParent(0);
+ pool.parseHeaders(parent);
+
+ Ref headRef = db.getRef(Constants.HEAD);
+ if (headRef == null)
+ throw new IOException("Missing HEAD");
+ RevCommit head = pool.parseCommit(headRef.getObjectId());
+
+ ThreeWayMerger merger = MergeStrategy.RECURSIVE.newMerger(db, true);
+ merger.setBase(parent.getTree());
+ if (merger.merge(head, commit)) {
+ if (AnyObjectId.equals(head.getTree(), merger.getResultTreeId()))
+ return null;
+ tick(1);
+ org.eclipse.jgit.lib.CommitBuilder b =
+ new org.eclipse.jgit.lib.CommitBuilder();
+ b.setParentId(head);
+ b.setTreeId(merger.getResultTreeId());
+ b.setAuthor(commit.getAuthorIdent());
+ b.setCommitter(new PersonIdent(defaultCommitter, getDate()));
+ b.setMessage(commit.getFullMessage());
+ ObjectId result;
+ try (ObjectInserter ins = inserter) {
+ result = ins.insert(b);
+ ins.flush();
+ }
+ update(Constants.HEAD, result);
+ return pool.parseCommit(result);
+ } else {
+ throw new IOException("Merge conflict");
+ }
+ }
+
/**
* Update the dumb client server info files.
*
@@ -581,34 +806,35 @@ public class TestRepository<R extends Repository> {
*/
public void fsck(RevObject... tips) throws MissingObjectException,
IncorrectObjectTypeException, IOException {
- ObjectWalk ow = new ObjectWalk(db);
- if (tips.length != 0) {
- for (RevObject o : tips)
- ow.markStart(ow.parseAny(o));
- } else {
- for (Ref r : db.getAllRefs().values())
- ow.markStart(ow.parseAny(r.getObjectId()));
- }
+ try (ObjectWalk ow = new ObjectWalk(db)) {
+ if (tips.length != 0) {
+ for (RevObject o : tips)
+ ow.markStart(ow.parseAny(o));
+ } else {
+ for (Ref r : db.getAllRefs().values())
+ ow.markStart(ow.parseAny(r.getObjectId()));
+ }
- ObjectChecker oc = new ObjectChecker();
- for (;;) {
- final RevCommit o = ow.next();
- if (o == null)
- break;
+ ObjectChecker oc = new ObjectChecker();
+ for (;;) {
+ final RevCommit o = ow.next();
+ if (o == null)
+ break;
- final byte[] bin = db.open(o, o.getType()).getCachedBytes();
- oc.checkCommit(bin);
- assertHash(o, bin);
- }
+ final byte[] bin = db.open(o, o.getType()).getCachedBytes();
+ oc.checkCommit(o, bin);
+ assertHash(o, bin);
+ }
- for (;;) {
- final RevObject o = ow.nextObject();
- if (o == null)
- break;
+ for (;;) {
+ final RevObject o = ow.nextObject();
+ if (o == null)
+ break;
- final byte[] bin = db.open(o, o.getType()).getCachedBytes();
- oc.check(o.getType(), bin);
- assertHash(o, bin);
+ final byte[] bin = db.open(o, o.getType()).getCachedBytes();
+ oc.check(o, o.getType(), bin);
+ assertHash(o, bin);
+ }
}
}
@@ -636,35 +862,27 @@ public class TestRepository<R extends Repository> {
NullProgressMonitor m = NullProgressMonitor.INSTANCE;
final File pack, idx;
- PackWriter pw = new PackWriter(db);
- try {
+ try (PackWriter pw = new PackWriter(db)) {
Set<ObjectId> all = new HashSet<ObjectId>();
for (Ref r : db.getAllRefs().values())
all.add(r.getObjectId());
- pw.preparePack(m, all, Collections.<ObjectId> emptySet());
+ pw.preparePack(m, all, PackWriter.NONE);
final ObjectId name = pw.computeName();
- OutputStream out;
pack = nameFor(odb, name, ".pack");
- out = new SafeBufferedOutputStream(new FileOutputStream(pack));
- try {
+ try (OutputStream out =
+ new SafeBufferedOutputStream(new FileOutputStream(pack))) {
pw.writePack(m, m, out);
- } finally {
- out.close();
}
pack.setReadOnly();
idx = nameFor(odb, name, ".idx");
- out = new SafeBufferedOutputStream(new FileOutputStream(idx));
- try {
+ try (OutputStream out =
+ new SafeBufferedOutputStream(new FileOutputStream(idx))) {
pw.writeIndex(out);
- } finally {
- out.close();
}
idx.setReadOnly();
- } finally {
- pw.release();
}
odb.openPack(pack);
@@ -760,6 +978,13 @@ public class TestRepository<R extends Repository> {
private RevCommit self;
+ private PersonIdent author;
+ private PersonIdent committer;
+
+ private String changeId;
+
+ private boolean updateCommitterTime;
+
CommitBuilder() {
branch = null;
}
@@ -768,9 +993,8 @@ public class TestRepository<R extends Repository> {
branch = b;
Ref ref = db.getRef(branch.ref);
- if (ref != null) {
+ if (ref != null && ref.getObjectId() != null)
parent(pool.parseCommit(ref.getObjectId()));
- }
}
CommitBuilder(CommitBuilder prior) throws Exception {
@@ -796,6 +1020,10 @@ public class TestRepository<R extends Repository> {
return this;
}
+ public List<RevCommit> parents() {
+ return Collections.unmodifiableList(parents);
+ }
+
public CommitBuilder noParents() {
parents.clear();
return this;
@@ -846,11 +1074,51 @@ public class TestRepository<R extends Repository> {
return this;
}
+ public String message() {
+ return message;
+ }
+
public CommitBuilder tick(int secs) {
tick = secs;
return this;
}
+ public CommitBuilder ident(PersonIdent ident) {
+ author = ident;
+ committer = ident;
+ return this;
+ }
+
+ public CommitBuilder author(PersonIdent a) {
+ author = a;
+ return this;
+ }
+
+ public PersonIdent author() {
+ return author;
+ }
+
+ public CommitBuilder committer(PersonIdent c) {
+ committer = c;
+ return this;
+ }
+
+ public PersonIdent committer() {
+ return committer;
+ }
+
+ public CommitBuilder insertChangeId() {
+ changeId = "";
+ return this;
+ }
+
+ public CommitBuilder insertChangeId(String c) {
+ // Validate, but store as a string so we can use "" as a sentinel.
+ ObjectId.fromString(c);
+ changeId = c;
+ return this;
+ }
+
public RevCommit create() throws Exception {
if (self == null) {
TestRepository.this.tick(tick);
@@ -860,18 +1128,24 @@ public class TestRepository<R extends Repository> {
c = new org.eclipse.jgit.lib.CommitBuilder();
c.setParentIds(parents);
setAuthorAndCommitter(c);
- c.setMessage(message);
+ if (author != null)
+ c.setAuthor(author);
+ if (committer != null) {
+ if (updateCommitterTime)
+ committer = new PersonIdent(committer, getDate());
+ c.setCommitter(committer);
+ }
ObjectId commitId;
- try {
+ try (ObjectInserter ins = inserter) {
if (topLevelTree != null)
c.setTreeId(topLevelTree);
else
- c.setTreeId(tree.writeTree(inserter));
- commitId = inserter.insert(c);
- inserter.flush();
- } finally {
- inserter.release();
+ c.setTreeId(tree.writeTree(ins));
+ insertChangeId(c);
+ c.setMessage(message);
+ commitId = ins.insert(c);
+ ins.flush();
}
self = pool.lookupCommit(commitId);
@@ -881,6 +1155,30 @@ public class TestRepository<R extends Repository> {
return self;
}
+ private void insertChangeId(org.eclipse.jgit.lib.CommitBuilder c) {
+ if (changeId == null)
+ return;
+ int idx = ChangeIdUtil.indexOfChangeId(message, "\n");
+ if (idx >= 0)
+ return;
+
+ ObjectId firstParentId = null;
+ if (!parents.isEmpty())
+ firstParentId = parents.get(0);
+
+ ObjectId cid;
+ if (changeId.equals(""))
+ cid = ChangeIdUtil.computeChangeId(c.getTreeId(), firstParentId,
+ c.getAuthor(), c.getCommitter(), message);
+ else
+ cid = ObjectId.fromString(changeId);
+ message = ChangeIdUtil.insertId(message, cid);
+ if (cid != null)
+ message = message.replaceAll("\nChange-Id: I" //$NON-NLS-1$
+ + ObjectId.zeroId().getName() + "\n", "\nChange-Id: I" //$NON-NLS-1$ //$NON-NLS-2$
+ + cid.getName() + "\n"); //$NON-NLS-1$
+ }
+
public CommitBuilder child() throws Exception {
return new CommitBuilder(this);
}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
index 209702e..911adf0 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit"
label="%featureName"
- version="3.7.1.201504261725-r"
+ version="4.2.0.201601211800-r"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
@@ -22,12 +22,6 @@
<discovery label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/>
</url>
- <includes
- id="org.eclipse.jgit.java7"
- version="0.0.0"
- name="Java implementation of Git - optional Java 7 libraries"
- optional="true"/>
-
<plugin
id="org.eclipse.jgit"
download-size="0"
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
index 9e9156b..689bcc1 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>3.7.1.201504261725-r</version>
+ <version>4.2.0.201601211800-r</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
@@ -65,11 +65,6 @@
<artifactId>org.eclipse.jgit</artifactId>
<version>${project.version}</version>
</dependency>
- <dependency>
- <groupId>org.eclipse.jgit</groupId>
- <artifactId>org.eclipse.jgit.java7</artifactId>
- <version>${project.version}</version>
- </dependency>
</dependencies>
</project>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml
index def0ac0..296b71a 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.http.apache"
label="%featureName"
- version="3.7.1.201504261725-r"
+ version="4.2.0.201601211800-r"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml
index c7cb431..0ee2e74 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.http.apache.feature/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>3.7.1.201504261725-r</version>
+ <version>4.2.0.201601211800-r</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.gitignore b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.gitignore
deleted file mode 100644
index 2f7896d..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-target/
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.project b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.project
deleted file mode 100644
index 318860e..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>org.eclipse.jgit.java7.feature</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>org.eclipse.pde.FeatureBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>org.eclipse.pde.FeatureNature</nature>
- </natures>
-</projectDescription>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.settings/org.eclipse.core.resources.prefs
deleted file mode 100644
index 14bdc2c..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.settings/org.eclipse.core.resources.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Fri Jun 18 23:33:45 CEST 2010
-eclipse.preferences.version=1
-encoding/<project>=UTF-8
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.settings/org.eclipse.core.runtime.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.settings/org.eclipse.core.runtime.prefs
deleted file mode 100644
index 898252b..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.settings/org.eclipse.core.runtime.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Fri Jun 18 23:33:45 CEST 2010
-eclipse.preferences.version=1
-line.separator=\n
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs
deleted file mode 100644
index 823c0f5..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.settings/org.eclipse.mylyn.tasks.ui.prefs
+++ /dev/null
@@ -1,4 +0,0 @@
-#Tue Jul 19 20:11:28 CEST 2011
-eclipse.preferences.version=1
-project.repository.kind=bugzilla
-project.repository.url=https\://bugs.eclipse.org/bugs
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.settings/org.eclipse.mylyn.team.ui.prefs b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.settings/org.eclipse.mylyn.team.ui.prefs
deleted file mode 100644
index 0cba949..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/.settings/org.eclipse.mylyn.team.ui.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Tue Jul 19 20:11:28 CEST 2011
-commit.comment.template=${task.description} \n\nBug\: ${task.key}
-eclipse.preferences.version=1
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/build.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/build.properties
deleted file mode 100644
index b4a8dde..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/build.properties
+++ /dev/null
@@ -1,4 +0,0 @@
-bin.includes = feature.xml,\
- edl-v10.html,\
- feature.properties,\
- license.html
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/edl-v10.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/edl-v10.html
deleted file mode 100644
index 1826b47..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/edl-v10.html
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1" ?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
-<title>Eclipse Distribution License - Version 1.0</title>
-<style type="text/css">
- body {
- size: 8.5in 11.0in;
- margin: 0.25in 0.5in 0.25in 0.5in;
- tab-interval: 0.5in;
- }
- p {
- margin-left: auto;
- margin-top: 0.5em;
- margin-bottom: 0.5em;
- }
- p.list {
- margin-left: 0.5in;
- margin-top: 0.05em;
- margin-bottom: 0.05em;
- }
- </style>
-
-</head>
-
-<body lang="EN-US">
-
-<p><b>Eclipse Distribution License - v 1.0</b></p>
-
-<p>Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. </p>
-
-<p>All rights reserved.</p>
-<p>Redistribution and use in source and binary forms, with or without modification,
- are permitted provided that the following conditions are met:
-<ul><li>Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.</li>
-<li>Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.</li>
-<li>Neither the name of the Eclipse Foundation, Inc. nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.</li></ul>
-</p>
-<p>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.</p>
-
-</body>
-
-</html>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/feature.properties
deleted file mode 100644
index c29f299..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/feature.properties
+++ /dev/null
@@ -1,159 +0,0 @@
-###############################################################################
-# Copyright (c) 2000, 2010 IBM Corporation and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Eclipse Public License v1.0
-# which accompanies this distribution, and is available at
-# http://www.eclipse.org/legal/epl-v10.html
-#
-###############################################################################
-
-featureName=Java implementation of Git - optional Java 7 libraries
-providerName=Eclipse JGit
-
-updateSiteName=Eclipse JGit Update Site
-
-# description property - text of the "Feature Description"
-description=\
-Optional Java 7 libraries for JGit.\n
-################ end of description property ##################################
-
-# "copyright" property - text of the "Feature Update Copyright"
-copyright=\
-Copyright (c) 2005, 2013 Shawn Pearce, Robin Rosenberg, et.al.\n\
-All rights reserved. This program and the accompanying materials\n\
-are made available under the terms of the Eclipse Distribution License v1.0\n\
-which accompanies this distribution, and is available at\n\
-http://www.eclipse.org/org/documents/edl-v10.html\n
-################ end of copyright property ####################################
-
-# "licenseURL" property - URL of the "Feature License"
-# do not translate value - just change to point to a locale-specific HTML page
-licenseURL=license.html
-
-# "license" property - text of the "Feature Update License"
-# should be plain text version of license agreement pointed to be "licenseURL"
-license=\
-Eclipse Foundation Software User Agreement\n\
-April 9, 2014\n\
-\n\
-Usage Of Content\n\
-\n\
-THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR\n\
-OTHER MATERIALS FOR OPEN SOURCE PROJECTS (COLLECTIVELY "CONTENT").\n\
-USE OF THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS\n\
-AGREEMENT AND/OR THE TERMS AND CONDITIONS OF LICENSE AGREEMENTS OR\n\
-NOTICES INDICATED OR REFERENCED BELOW. BY USING THE CONTENT, YOU\n\
-AGREE THAT YOUR USE OF THE CONTENT IS GOVERNED BY THIS AGREEMENT\n\
-AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS\n\
-OR NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE\n\
-TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND CONDITIONS\n\
-OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED\n\
-BELOW, THEN YOU MAY NOT USE THE CONTENT.\n\
-\n\
-Applicable Licenses\n\
-\n\
-Unless otherwise indicated, all Content made available by the\n\
-Eclipse Foundation is provided to you under the terms and conditions of\n\
-the Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is\n\
-provided with this Content and is also available at http://www.eclipse.org/legal/epl-v10.html.\n\
-For purposes of the EPL, "Program" will mean the Content.\n\
-\n\
-Content includes, but is not limited to, source code, object code,\n\
-documentation and other files maintained in the Eclipse Foundation source code\n\
-repository ("Repository") in software modules ("Modules") and made available\n\
-as downloadable archives ("Downloads").\n\
-\n\
- - Content may be structured and packaged into modules to facilitate delivering,\n\
- extending, and upgrading the Content. Typical modules may include plug-ins ("Plug-ins"),\n\
- plug-in fragments ("Fragments"), and features ("Features").\n\
- - Each Plug-in or Fragment may be packaged as a sub-directory or JAR (Java(TM) ARchive)\n\
- in a directory named "plugins".\n\
- - A Feature is a bundle of one or more Plug-ins and/or Fragments and associated material.\n\
- Each Feature may be packaged as a sub-directory in a directory named "features".\n\
- Within a Feature, files named "feature.xml" may contain a list of the names and version\n\
- numbers of the Plug-ins and/or Fragments associated with that Feature.\n\
- - Features may also include other Features ("Included Features"). Within a Feature, files\n\
- named "feature.xml" may contain a list of the names and version numbers of Included Features.\n\
-\n\
-The terms and conditions governing Plug-ins and Fragments should be\n\
-contained in files named "about.html" ("Abouts"). The terms and\n\
-conditions governing Features and Included Features should be contained\n\
-in files named "license.html" ("Feature Licenses"). Abouts and Feature\n\
-Licenses may be located in any directory of a Download or Module\n\
-including, but not limited to the following locations:\n\
-\n\
- - The top-level (root) directory\n\
- - Plug-in and Fragment directories\n\
- - Inside Plug-ins and Fragments packaged as JARs\n\
- - Sub-directories of the directory named "src" of certain Plug-ins\n\
- - Feature directories\n\
-\n\
-Note: if a Feature made available by the Eclipse Foundation is installed using the\n\
-Provisioning Technology (as defined below), you must agree to a license ("Feature \n\
-Update License") during the installation process. If the Feature contains\n\
-Included Features, the Feature Update License should either provide you\n\
-with the terms and conditions governing the Included Features or inform\n\
-you where you can locate them. Feature Update Licenses may be found in\n\
-the "license" property of files named "feature.properties" found within a Feature.\n\
-Such Abouts, Feature Licenses, and Feature Update Licenses contain the\n\
-terms and conditions (or references to such terms and conditions) that\n\
-govern your use of the associated Content in that directory.\n\
-\n\
-THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER\n\
-TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS.\n\
-SOME OF THESE OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):\n\
-\n\
- - Eclipse Distribution License Version 1.0 (available at http://www.eclipse.org/licenses/edl-v1.0.html)\n\
- - Common Public License Version 1.0 (available at http://www.eclipse.org/legal/cpl-v10.html)\n\
- - Apache Software License 1.1 (available at http://www.apache.org/licenses/LICENSE)\n\
- - Apache Software License 2.0 (available at http://www.apache.org/licenses/LICENSE-2.0)\n\
- - Mozilla Public License Version 1.1 (available at http://www.mozilla.org/MPL/MPL-1.1.html)\n\
-\n\
-IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR\n\
-TO USE OF THE CONTENT. If no About, Feature License, or Feature Update License\n\
-is provided, please contact the Eclipse Foundation to determine what terms and conditions\n\
-govern that particular Content.\n\
-\n\
-\n\Use of Provisioning Technology\n\
-\n\
-The Eclipse Foundation makes available provisioning software, examples of which include,\n\
-but are not limited to, p2 and the Eclipse Update Manager ("Provisioning Technology") for\n\
-the purpose of allowing users to install software, documentation, information and/or\n\
-other materials (collectively "Installable Software"). This capability is provided with\n\
-the intent of allowing such users to install, extend and update Eclipse-based products.\n\
-Information about packaging Installable Software is available at\n\
-http://eclipse.org/equinox/p2/repository_packaging.html ("Specification").\n\
-\n\
-You may use Provisioning Technology to allow other parties to install Installable Software.\n\
-You shall be responsible for enabling the applicable license agreements relating to the\n\
-Installable Software to be presented to, and accepted by, the users of the Provisioning Technology\n\
-in accordance with the Specification. By using Provisioning Technology in such a manner and\n\
-making it available in accordance with the Specification, you further acknowledge your\n\
-agreement to, and the acquisition of all necessary rights to permit the following:\n\
-\n\
- 1. A series of actions may occur ("Provisioning Process") in which a user may execute\n\
- the Provisioning Technology on a machine ("Target Machine") with the intent of installing,\n\
- extending or updating the functionality of an Eclipse-based product.\n\
- 2. During the Provisioning Process, the Provisioning Technology may cause third party\n\
- Installable Software or a portion thereof to be accessed and copied to the Target Machine.\n\
- 3. Pursuant to the Specification, you will provide to the user the terms and conditions that\n\
- govern the use of the Installable Software ("Installable Software Agreement") and such\n\
- Installable Software Agreement shall be accessed from the Target Machine in accordance\n\
- with the Specification. Such Installable Software Agreement must inform the user of the\n\
- terms and conditions that govern the Installable Software and must solicit acceptance by\n\
- the end user in the manner prescribed in such Installable Software Agreement. Upon such\n\
- indication of agreement by the user, the provisioning Technology will complete installation\n\
- of the Installable Software.\n\
-\n\
-Cryptography\n\
-\n\
-Content may contain encryption software. The country in which you are\n\
-currently may have restrictions on the import, possession, and use,\n\
-and/or re-export to another country, of encryption software. BEFORE\n\
-using any encryption software, please check the country's laws,\n\
-regulations and policies concerning the import, possession, or use, and\n\
-re-export of encryption software, to see if this is permitted.\n\
-\n\
-Java and all Java-based trademarks are trademarks of Oracle Corporation in the United States, other countries, or both.\n
-########### end of license property ##########################################
\ No newline at end of file
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/feature.xml
deleted file mode 100644
index af3de05..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/feature.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<feature
- id="org.eclipse.jgit.java7"
- label="%featureName"
- version="3.7.1.201504261725-r"
- provider-name="%providerName">
-
- <description url="http://www.eclipse.org/jgit/">
- %description
- </description>
-
- <copyright>
- %copyright
- </copyright>
-
- <license url="%licenseURL">
- %license
- </license>
-
- <url>
- <update label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/>
- <discovery label="%updateSiteName" url="http://download.eclipse.org/egit/updates"/>
- </url>
-
- <plugin
- id="org.eclipse.jgit.java7"
- download-size="0"
- install-size="0"
- version="0.0.0"
- fragment="true"
- unpack="false"/>
-
-</feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/license.html b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/license.html
deleted file mode 100644
index 95ad95e..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/license.html
+++ /dev/null
@@ -1,106 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<!-- saved from url=(0044)http://www.eclipse.org/legal/epl/notice.html -->
-<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
-
-<title>Eclipse Foundation Software User Agreement</title>
-</head>
-
-<body lang="EN-US">
-<h2>Eclipse Foundation Software User Agreement</h2>
-<p>April 9, 2014</p>
-
-<h3>Usage Of Content</h3>
-
-<p>THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS
- (COLLECTIVELY "CONTENT"). USE OF THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE TERMS AND
- CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE
- OF THE CONTENT IS GOVERNED BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR
- NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND
- CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU MAY NOT USE THE CONTENT.</p>
-
-<h3>Applicable Licenses</h3>
-
-<p>Unless otherwise indicated, all Content made available by the Eclipse Foundation is provided to you under the terms and conditions of the Eclipse Public License Version 1.0
- ("EPL"). A copy of the EPL is provided with this Content and is also available at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
- For purposes of the EPL, "Program" will mean the Content.</p>
-
-<p>Content includes, but is not limited to, source code, object code, documentation and other files maintained in the Eclipse Foundation source code
- repository ("Repository") in software modules ("Modules") and made available as downloadable archives ("Downloads").</p>
-
-<ul>
- <li>Content may be structured and packaged into modules to facilitate delivering, extending, and upgrading the Content. Typical modules may include plug-ins ("Plug-ins"), plug-in fragments ("Fragments"), and features ("Features").</li>
- <li>Each Plug-in or Fragment may be packaged as a sub-directory or JAR (Java� ARchive) in a directory named "plugins".</li>
- <li>A Feature is a bundle of one or more Plug-ins and/or Fragments and associated material. Each Feature may be packaged as a sub-directory in a directory named "features". Within a Feature, files named "feature.xml" may contain a list of the names and version numbers of the Plug-ins
- and/or Fragments associated with that Feature.</li>
- <li>Features may also include other Features ("Included Features"). Within a Feature, files named "feature.xml" may contain a list of the names and version numbers of Included Features.</li>
-</ul>
-
-<p>The terms and conditions governing Plug-ins and Fragments should be contained in files named "about.html" ("Abouts"). The terms and conditions governing Features and
-Included Features should be contained in files named "license.html" ("Feature Licenses"). Abouts and Feature Licenses may be located in any directory of a Download or Module
-including, but not limited to the following locations:</p>
-
-<ul>
- <li>The top-level (root) directory</li>
- <li>Plug-in and Fragment directories</li>
- <li>Inside Plug-ins and Fragments packaged as JARs</li>
- <li>Sub-directories of the directory named "src" of certain Plug-ins</li>
- <li>Feature directories</li>
-</ul>
-
-<p>Note: if a Feature made available by the Eclipse Foundation is installed using the Provisioning Technology (as defined below), you must agree to a license ("Feature Update License") during the
-installation process. If the Feature contains Included Features, the Feature Update License should either provide you with the terms and conditions governing the Included Features or
-inform you where you can locate them. Feature Update Licenses may be found in the "license" property of files named "feature.properties" found within a Feature.
-Such Abouts, Feature Licenses, and Feature Update Licenses contain the terms and conditions (or references to such terms and conditions) that govern your use of the associated Content in
-that directory.</p>
-
-<p>THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS. SOME OF THESE
-OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):</p>
-
-<ul>
- <li>Eclipse Distribution License Version 1.0 (available at <a href="http://www.eclipse.org/licenses/edl-v10.html">http://www.eclipse.org/licenses/edl-v1.0.html</a>)</li>
- <li>Common Public License Version 1.0 (available at <a href="http://www.eclipse.org/legal/cpl-v10.html">http://www.eclipse.org/legal/cpl-v10.html</a>)</li>
- <li>Apache Software License 1.1 (available at <a href="http://www.apache.org/licenses/LICENSE">http://www.apache.org/licenses/LICENSE</a>)</li>
- <li>Apache Software License 2.0 (available at <a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>)</li>
- <li>Mozilla Public License Version 1.1 (available at <a href="http://www.mozilla.org/MPL/MPL-1.1.html">http://www.mozilla.org/MPL/MPL-1.1.html</a>)</li>
-</ul>
-
-<p>IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR TO USE OF THE CONTENT. If no About, Feature License, or Feature Update License is provided, please
-contact the Eclipse Foundation to determine what terms and conditions govern that particular Content.</p>
-
-
-<h3>Use of Provisioning Technology</h3>
-
-<p>The Eclipse Foundation makes available provisioning software, examples of which include, but are not limited to, p2 and the Eclipse
- Update Manager ("Provisioning Technology") for the purpose of allowing users to install software, documentation, information and/or
- other materials (collectively "Installable Software"). This capability is provided with the intent of allowing such users to
- install, extend and update Eclipse-based products. Information about packaging Installable Software is available at <a href="http://eclipse.org/equinox/p2/repository_packaging.html">http://eclipse.org/equinox/p2/repository_packaging.html</a>
- ("Specification").</p>
-
-<p>You may use Provisioning Technology to allow other parties to install Installable Software. You shall be responsible for enabling the
- applicable license agreements relating to the Installable Software to be presented to, and accepted by, the users of the Provisioning Technology
- in accordance with the Specification. By using Provisioning Technology in such a manner and making it available in accordance with the
- Specification, you further acknowledge your agreement to, and the acquisition of all necessary rights to permit the following:</p>
-
-<ol>
- <li>A series of actions may occur ("Provisioning Process") in which a user may execute the Provisioning Technology
- on a machine ("Target Machine") with the intent of installing, extending or updating the functionality of an Eclipse-based
- product.</li>
- <li>During the Provisioning Process, the Provisioning Technology may cause third party Installable Software or a portion thereof to be
- accessed and copied to the Target Machine.</li>
- <li>Pursuant to the Specification, you will provide to the user the terms and conditions that govern the use of the Installable
- Software ("Installable Software Agreement") and such Installable Software Agreement shall be accessed from the Target
- Machine in accordance with the Specification. Such Installable Software Agreement must inform the user of the terms and conditions that govern
- the Installable Software and must solicit acceptance by the end user in the manner prescribed in such Installable Software Agreement. Upon such
- indication of agreement by the user, the provisioning Technology will complete installation of the Installable Software.</li>
-</ol>
-
-<h3>Cryptography</h3>
-
-<p>Content may contain encryption software. The country in which you are currently may have restrictions on the import, possession, and use, and/or re-export to
- another country, of encryption software. BEFORE using any encryption software, please check the country's laws, regulations and policies concerning the import,
- possession, or use, and re-export of encryption software, to see if this is permitted.</p>
-
-<p><small>Java and all Java-based trademarks are trademarks of Oracle Corporation in the United States, other countries, or both.</small></p>
-
-
-</body></html>
\ No newline at end of file
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/pom.xml
deleted file mode 100644
index 35f8243..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/pom.xml
+++ /dev/null
@@ -1,77 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2009-2012, Matthias Sohn <matthias.sohn at sap.com>
- and other copyright owners as documented in the project's IP log.
-
- This program and the accompanying materials are made available
- under the terms of the Eclipse Distribution License v1.0 which
- accompanies this distribution, is reproduced below, and is
- available at http://www.eclipse.org/org/documents/edl-v10.php
-
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or
- without modification, are permitted provided that the following
- conditions are met:
-
- - Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- - Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- - Neither the name of the Eclipse Foundation, Inc. nor the
- names of its contributors may be used to endorse or promote
- products derived from this software without specific prior
- written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <groupId>org.eclipse.jgit</groupId>
- <artifactId>jgit.tycho.parent</artifactId>
- <version>3.7.1.201504261725-r</version>
- </parent>
-
- <groupId>org.eclipse.jgit.feature</groupId>
- <artifactId>org.eclipse.jgit.java7</artifactId>
- <packaging>eclipse-feature</packaging>
-
- <name>JGit Optional Java 7 Feature</name>
- <dependencies>
-
- <dependency>
- <groupId>org.eclipse.jgit</groupId>
- <artifactId>org.eclipse.jgit</artifactId>
- <version>${project.version}</version>
- </dependency>
-
- <dependency>
- <groupId>org.eclipse.jgit</groupId>
- <artifactId>org.eclipse.jgit.java7</artifactId>
- <version>${project.version}</version>
- </dependency>
-
- </dependencies>
-
-</project>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml
index 9647562..28af509 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.junit"
label="%featureName"
- version="3.7.1.201504261725-r"
+ version="4.2.0.201601211800-r"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml
index 8281549..da2fb72 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>3.7.1.201504261725-r</version>
+ <version>4.2.0.201601211800-r</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml
index 0069f36..5f811cb 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.pgm"
label="%featureName"
- version="3.7.1.201504261725-r"
+ version="4.2.0.201601211800-r"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
@@ -27,7 +27,7 @@
version="0.0.0"/>
<requires>
- <import feature="org.eclipse.jgit" version="3.7.0" match="equivalent"/>
+ <import feature="org.eclipse.jgit" version="4.2.0" match="equivalent"/>
</requires>
<plugin
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
index fc2415f..d0d87ea 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>3.7.1.201504261725-r</version>
+ <version>4.2.0.201601211800-r</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
@@ -69,12 +69,6 @@
<dependency>
<groupId>org.eclipse.jgit</groupId>
- <artifactId>org.eclipse.jgit.java7</artifactId>
- <version>${project.version}</version>
- </dependency>
-
- <dependency>
- <groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.pgm</artifactId>
<version>${project.version}</version>
</dependency>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml
index b69b0b0..1446fe9 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.pgm.source"
label="%featureName"
- version="3.7.1.201504261725-r"
+ version="4.2.0.201601211800-r"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml
index e7ee324..e1bccc0 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>3.7.1.201504261725-r</version>
+ <version>4.2.0.201601211800-r</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
index b1c8164..2186ad9 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/category.xml
@@ -18,9 +18,6 @@
<feature url="features/org.eclipse.jgit.junit_0.0.0.qualifier.jar" id="org.eclipse.jgit.junit" version="0.0.0" patch="true">
<category name="JGit"/>
</feature>
- <feature url="features/org.eclipse.jgit.java7_0.0.0.qualifier.jar" id="org.eclipse.jgit.java7" version="0.0.0" patch="true">
- <category name="JGit"/>
- </feature>
<feature url="features/org.eclipse.jgit.http.apache_0.0.0.qualifier.jar" id="org.eclipse.jgit.http.apache" version="0.0.0" patch="true">
<category name="JGit"/>
</feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
index 80c5bc5..c9d492f 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.repository/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>3.7.1.201504261725-r</version>
+ <version>4.2.0.201601211800-r</version>
</parent>
<artifactId>org.eclipse.jgit.repository</artifactId>
@@ -69,11 +69,6 @@
<artifactId>org.eclipse.jgit.http.apache</artifactId>
<version>${project.version}</version>
</dependency>
- <dependency>
- <groupId>org.eclipse.jgit</groupId>
- <artifactId>org.eclipse.jgit.java7</artifactId>
- <version>${project.version}</version>
- </dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.pgm</artifactId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
index b569812..070b452 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.xml
@@ -2,7 +2,7 @@
<feature
id="org.eclipse.jgit.source"
label="%featureName"
- version="3.7.1.201504261725-r"
+ version="4.2.0.201601211800-r"
provider-name="%providerName">
<description url="http://www.eclipse.org/jgit/">
@@ -28,10 +28,4 @@
install-size="0"
version="0.0.0"
unpack="false"/>
- <plugin
- id="org.eclipse.jgit.java7.source"
- download-size="0"
- install-size="0"
- version="0.0.0"
- unpack="false"/>
</feature>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml
index 9111229..6de1ce2 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>3.7.1.201504261725-r</version>
+ <version>4.2.0.201601211800-r</version>
</parent>
<groupId>org.eclipse.jgit.feature</groupId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/.classpath b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/.classpath
index 64c5e31..098194c 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/.classpath
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/.classpath
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF
index 5aa3c33..9196587 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/META-INF/MANIFEST.MF
@@ -2,4 +2,4 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: JGit Target Platform Bundle
Bundle-SymbolicName: org.eclipse.jgit.target
-Bundle-Version: 3.7.1.201504261725-r
+Bundle-Version: 4.2.0.201601211800-r
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.3.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.3.target
index 44be1e5..e8670e7 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.3.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.3.target
@@ -1,26 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
<!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform -->
-<target name="jgit-4.3" sequenceNumber="1424129720">
+<target name="jgit-4.3" sequenceNumber="1440024094">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.jetty.client" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.client.source" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.continuation" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.continuation.source" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.http" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.http.source" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.io" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.io.source" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.security" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.security.source" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.server" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.server.source" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.servlet" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.servlet.source" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.util" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.util.source" version="7.6.14.v20131031"/>
- <repository id="jetty-7.6.14" location="http://download.eclipse.org/jetty/updates/jetty-bundles-7.x/7.6.14.v20131031/"/>
+ <unit id="org.eclipse.jetty.client" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.client.source" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.continuation" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.continuation.source" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.http" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.http.source" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.io" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.io.source" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.security" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.security.source" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.server" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.server.source" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.servlet" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.servlet.source" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.util" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.util.source" version="9.2.13.v20150730"/>
+ <repository id="jetty-9.2.13" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.2.13.v20150730/"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.apache.ant" version="1.9.2.v201404171502"/>
@@ -29,10 +29,10 @@
<unit id="org.apache.commons.compress.source" version="1.6.0.v201310281400"/>
<unit id="org.apache.commons.logging" version="1.1.1.v201101211721"/>
<unit id="org.apache.commons.logging.source" version="1.1.1.v201101211721"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.1.4.v201203221030"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.1.4.v201203221030"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.1.3.v201209201135"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.1.3.v201209201135"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.3.3.v201411290715"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.3.3.v201411290715"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.3.6.v201411290715"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.3.6.v201411290715"/>
<unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
<unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/>
<unit id="org.kohsuke.args4j" version="2.0.21.v201301150030"/>
@@ -49,8 +49,8 @@
<unit id="com.jcraft.jsch.source" version="0.1.51.v201410302000"/>
<unit id="org.junit" version="4.11.0.v201303080030"/>
<unit id="org.junit.source" version="4.11.0.v201303080030"/>
- <unit id="javax.servlet" version="2.5.0.v201103041518"/>
- <unit id="javax.servlet.source" version="2.5.0.v201103041518"/>
+ <unit id="javax.servlet" version="3.1.0.v20140303-1611"/>
+ <unit id="javax.servlet.source" version="3.1.0.v20140303-1611"/>
<unit id="org.tukaani.xz" version="1.3.0.v201308270617"/>
<unit id="org.tukaani.xz.source" version="1.3.0.v201308270617"/>
<unit id="org.slf4j.api" version="1.7.2.v20121108-1250"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.3.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.3.tpd
index 1d16481..062e930 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.3.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.3.tpd
@@ -1,6 +1,6 @@
target "jgit-4.3" with source configurePhase
-include "projects/jetty-7.6.14.tpd"
+include "projects/jetty-9.2.13.tpd"
include "orbit/R20150124073747-Luna-SR2.tpd"
location "http://download.eclipse.org/releases/kepler/" {
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.4.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.4.target
index 38e25a4..0b85d75 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.4.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.4.target
@@ -1,26 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
<!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform -->
-<target name="jgit-4.4" sequenceNumber="1424129605">
+<target name="jgit-4.4" sequenceNumber="1440024079">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.jetty.client" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.client.source" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.continuation" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.continuation.source" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.http" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.http.source" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.io" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.io.source" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.security" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.security.source" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.server" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.server.source" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.servlet" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.servlet.source" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.util" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.util.source" version="7.6.14.v20131031"/>
- <repository id="jetty-7.6.14" location="http://download.eclipse.org/jetty/updates/jetty-bundles-7.x/7.6.14.v20131031/"/>
+ <unit id="org.eclipse.jetty.client" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.client.source" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.continuation" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.continuation.source" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.http" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.http.source" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.io" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.io.source" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.security" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.security.source" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.server" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.server.source" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.servlet" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.servlet.source" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.util" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.util.source" version="9.2.13.v20150730"/>
+ <repository id="jetty-9.2.13" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.2.13.v20150730/"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.apache.ant" version="1.9.2.v201404171502"/>
@@ -29,10 +29,10 @@
<unit id="org.apache.commons.compress.source" version="1.6.0.v201310281400"/>
<unit id="org.apache.commons.logging" version="1.1.1.v201101211721"/>
<unit id="org.apache.commons.logging.source" version="1.1.1.v201101211721"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.1.4.v201203221030"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.1.4.v201203221030"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.1.3.v201209201135"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.1.3.v201209201135"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.3.3.v201411290715"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.3.3.v201411290715"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.3.6.v201411290715"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.3.6.v201411290715"/>
<unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
<unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/>
<unit id="org.kohsuke.args4j" version="2.0.21.v201301150030"/>
@@ -49,8 +49,8 @@
<unit id="com.jcraft.jsch.source" version="0.1.51.v201410302000"/>
<unit id="org.junit" version="4.11.0.v201303080030"/>
<unit id="org.junit.source" version="4.11.0.v201303080030"/>
- <unit id="javax.servlet" version="2.5.0.v201103041518"/>
- <unit id="javax.servlet.source" version="2.5.0.v201103041518"/>
+ <unit id="javax.servlet" version="3.1.0.v20140303-1611"/>
+ <unit id="javax.servlet.source" version="3.1.0.v20140303-1611"/>
<unit id="org.tukaani.xz" version="1.3.0.v201308270617"/>
<unit id="org.tukaani.xz.source" version="1.3.0.v201308270617"/>
<unit id="org.slf4j.api" version="1.7.2.v20121108-1250"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.4.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.4.tpd
index afbe9c9..9b9558f 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.4.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.4.tpd
@@ -1,6 +1,6 @@
target "jgit-4.4" with source configurePhase
-include "projects/jetty-7.6.14.tpd"
+include "projects/jetty-9.2.13.tpd"
include "orbit/R20150124073747-Luna-SR2.tpd"
location "http://download.eclipse.org/releases/luna/" {
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target
index e5d1433..2067482 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.target
@@ -1,38 +1,38 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde?>
<!-- generated with https://github.com/mbarbero/fr.obeo.releng.targetplatform -->
-<target name="jgit-4.5" sequenceNumber="1424129587">
+<target name="jgit-4.5" sequenceNumber="1440022750">
<locations>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.eclipse.jetty.client" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.client.source" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.continuation" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.continuation.source" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.http" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.http.source" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.io" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.io.source" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.security" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.security.source" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.server" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.server.source" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.servlet" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.servlet.source" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.util" version="7.6.14.v20131031"/>
- <unit id="org.eclipse.jetty.util.source" version="7.6.14.v20131031"/>
- <repository id="jetty-7.6.14" location="http://download.eclipse.org/jetty/updates/jetty-bundles-7.x/7.6.14.v20131031/"/>
+ <unit id="org.eclipse.jetty.client" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.client.source" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.continuation" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.continuation.source" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.http" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.http.source" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.io" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.io.source" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.security" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.security.source" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.server" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.server.source" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.servlet" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.servlet.source" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.util" version="9.2.13.v20150730"/>
+ <unit id="org.eclipse.jetty.util.source" version="9.2.13.v20150730"/>
+ <repository id="jetty-9.2.13" location="http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.2.13.v20150730/"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
- <unit id="org.apache.ant" version="1.9.4.v201410062020"/>
- <unit id="org.apache.ant.source" version="1.9.4.v201410062020"/>
+ <unit id="org.apache.ant" version="1.9.4.v201504302020"/>
+ <unit id="org.apache.ant.source" version="1.9.4.v201504302020"/>
<unit id="org.apache.commons.compress" version="1.6.0.v201310281400"/>
<unit id="org.apache.commons.compress.source" version="1.6.0.v201310281400"/>
<unit id="org.apache.commons.logging" version="1.1.1.v201101211721"/>
<unit id="org.apache.commons.logging.source" version="1.1.1.v201101211721"/>
- <unit id="org.apache.httpcomponents.httpcore" version="4.1.4.v201203221030"/>
- <unit id="org.apache.httpcomponents.httpcore.source" version="4.1.4.v201203221030"/>
- <unit id="org.apache.httpcomponents.httpclient" version="4.1.3.v201209201135"/>
- <unit id="org.apache.httpcomponents.httpclient.source" version="4.1.3.v201209201135"/>
+ <unit id="org.apache.httpcomponents.httpcore" version="4.3.3.v201411290715"/>
+ <unit id="org.apache.httpcomponents.httpcore.source" version="4.3.3.v201411290715"/>
+ <unit id="org.apache.httpcomponents.httpclient" version="4.3.6.v201411290715"/>
+ <unit id="org.apache.httpcomponents.httpclient.source" version="4.3.6.v201411290715"/>
<unit id="org.apache.log4j" version="1.2.15.v201012070815"/>
<unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/>
<unit id="org.kohsuke.args4j" version="2.0.21.v201301150030"/>
@@ -41,23 +41,23 @@
<unit id="org.hamcrest.core.source" version="1.3.0.v201303031735"/>
<unit id="javaewah" version="0.7.9.v201401101600"/>
<unit id="javaewah.source" version="0.7.9.v201401101600"/>
- <unit id="org.objenesis" version="1.0.0.v201105211943"/>
- <unit id="org.objenesis.source" version="1.0.0.v201105211943"/>
+ <unit id="org.objenesis" version="1.0.0.v201505121915"/>
+ <unit id="org.objenesis.source" version="1.0.0.v201505121915"/>
<unit id="org.mockito" version="1.8.4.v201303031500"/>
<unit id="org.mockito.source" version="1.8.4.v201303031500"/>
- <unit id="com.jcraft.jsch" version="0.1.51.v201410302000"/>
- <unit id="com.jcraft.jsch.source" version="0.1.51.v201410302000"/>
+ <unit id="com.jcraft.jsch" version="0.1.53.v201508180515"/>
+ <unit id="com.jcraft.jsch.source" version="0.1.53.v201508180515"/>
<unit id="org.junit" version="4.11.0.v201303080030"/>
<unit id="org.junit.source" version="4.11.0.v201303080030"/>
- <unit id="javax.servlet" version="2.5.0.v201103041518"/>
- <unit id="javax.servlet.source" version="2.5.0.v201103041518"/>
+ <unit id="javax.servlet" version="3.1.0.v201410161800"/>
+ <unit id="javax.servlet.source" version="3.1.0.v201410161800"/>
<unit id="org.tukaani.xz" version="1.3.0.v201308270617"/>
<unit id="org.tukaani.xz.source" version="1.3.0.v201308270617"/>
<unit id="org.slf4j.api" version="1.7.2.v20121108-1250"/>
<unit id="org.slf4j.api.source" version="1.7.2.v20121108-1250"/>
<unit id="org.slf4j.impl.log4j12" version="1.7.2.v20131105-2200"/>
<unit id="org.slf4j.impl.log4j12.source" version="1.7.2.v20131105-2200"/>
- <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/S20150202203538/repository/"/>
+ <repository location="http://download.eclipse.org/tools/orbit/downloads/drops/R20150821153341/repository/"/>
</location>
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
<unit id="org.eclipse.osgi" version="0.0.0"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd
index 33b088a..cdf24b5 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.5.tpd
@@ -1,7 +1,7 @@
target "jgit-4.5" with source configurePhase
-include "projects/jetty-7.6.14.tpd"
-include "orbit/S20150202203538-Mars-M5.tpd"
+include "projects/jetty-9.2.13.tpd"
+include "orbit/R20150821153341-Mars.tpd"
location "http://download.eclipse.org/releases/mars/" {
org.eclipse.osgi lazy
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20150124073747-Luna-SR2.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20150124073747-Luna-SR2.tpd
index d79b41a..38c9297 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20150124073747-Luna-SR2.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20150124073747-Luna-SR2.tpd
@@ -8,10 +8,10 @@ location "http://download.eclipse.org/tools/orbit/downloads/drops/R2015012407374
org.apache.commons.compress.source [1.6.0.v201310281400,1.6.0.v201310281400]
org.apache.commons.logging [1.1.1.v201101211721,1.1.1.v201101211721]
org.apache.commons.logging.source [1.1.1.v201101211721,1.1.1.v201101211721]
- org.apache.httpcomponents.httpcore [4.1.4.v201203221030,4.1.4.v201203221030]
- org.apache.httpcomponents.httpcore.source [4.1.4.v201203221030,4.1.4.v201203221030]
- org.apache.httpcomponents.httpclient [4.1.3.v201209201135,4.1.3.v201209201135]
- org.apache.httpcomponents.httpclient.source [4.1.3.v201209201135,4.1.3.v201209201135]
+ org.apache.httpcomponents.httpcore [4.3.3.v201411290715,4.3.3.v201411290715]
+ org.apache.httpcomponents.httpcore.source [4.3.3.v201411290715,4.3.3.v201411290715]
+ org.apache.httpcomponents.httpclient [4.3.6.v201411290715,4.3.6.v201411290715]
+ org.apache.httpcomponents.httpclient.source [4.3.6.v201411290715,4.3.6.v201411290715]
org.apache.log4j [1.2.15.v201012070815,1.2.15.v201012070815]
org.apache.log4j.source [1.2.15.v201012070815,1.2.15.v201012070815]
org.kohsuke.args4j [2.0.21.v201301150030,2.0.21.v201301150030]
@@ -28,8 +28,8 @@ location "http://download.eclipse.org/tools/orbit/downloads/drops/R2015012407374
com.jcraft.jsch.source [0.1.51.v201410302000,0.1.51.v201410302000]
org.junit [4.11.0.v201303080030,4.11.0.v201303080030]
org.junit.source [4.11.0.v201303080030,4.11.0.v201303080030]
- javax.servlet [2.5.0.v201103041518,2.5.0.v201103041518]
- javax.servlet.source [2.5.0.v201103041518,2.5.0.v201103041518]
+ javax.servlet [3.1.0.v20140303-1611,3.1.0.v20140303-1611]
+ javax.servlet.source [3.1.0.v20140303-1611,3.1.0.v20140303-1611]
org.tukaani.xz [1.3.0.v201308270617,1.3.0.v201308270617]
org.tukaani.xz.source [1.3.0.v201308270617,1.3.0.v201308270617]
org.slf4j.api [1.7.2.v20121108-1250,1.7.2.v20121108-1250]
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20150202203538-Mars-M5.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20150821153341-Mars.tpd
similarity index 62%
rename from org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20150202203538-Mars-M5.tpd
rename to org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20150821153341-Mars.tpd
index 3ec1001..cb63807 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/S20150202203538-Mars-M5.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/orbit/R20150821153341-Mars.tpd
@@ -1,17 +1,17 @@
-target "S20141023165154-Mars-M3" with source configurePhase
+target "R20150821153341-Mars" with source configurePhase
// see http://download.eclipse.org/tools/orbit/downloads/
-location "http://download.eclipse.org/tools/orbit/downloads/drops/S20150202203538/repository/" {
- org.apache.ant [1.9.4.v201410062020,1.9.4.v201410062020]
- org.apache.ant.source [1.9.4.v201410062020,1.9.4.v201410062020]
+location "http://download.eclipse.org/tools/orbit/downloads/drops/R20150821153341/repository/" {
+ org.apache.ant [1.9.4.v201504302020,1.9.4.v201504302020]
+ org.apache.ant.source [1.9.4.v201504302020,1.9.4.v201504302020]
org.apache.commons.compress [1.6.0.v201310281400,1.6.0.v201310281400]
org.apache.commons.compress.source [1.6.0.v201310281400,1.6.0.v201310281400]
org.apache.commons.logging [1.1.1.v201101211721,1.1.1.v201101211721]
org.apache.commons.logging.source [1.1.1.v201101211721,1.1.1.v201101211721]
- org.apache.httpcomponents.httpcore [4.1.4.v201203221030,4.1.4.v201203221030]
- org.apache.httpcomponents.httpcore.source [4.1.4.v201203221030,4.1.4.v201203221030]
- org.apache.httpcomponents.httpclient [4.1.3.v201209201135,4.1.3.v201209201135]
- org.apache.httpcomponents.httpclient.source [4.1.3.v201209201135,4.1.3.v201209201135]
+ org.apache.httpcomponents.httpcore [4.3.3.v201411290715,4.3.3.v201411290715]
+ org.apache.httpcomponents.httpcore.source [4.3.3.v201411290715,4.3.3.v201411290715]
+ org.apache.httpcomponents.httpclient [4.3.6.v201411290715,4.3.6.v201411290715]
+ org.apache.httpcomponents.httpclient.source [4.3.6.v201411290715,4.3.6.v201411290715]
org.apache.log4j [1.2.15.v201012070815,1.2.15.v201012070815]
org.apache.log4j.source [1.2.15.v201012070815,1.2.15.v201012070815]
org.kohsuke.args4j [2.0.21.v201301150030,2.0.21.v201301150030]
@@ -20,16 +20,16 @@ location "http://download.eclipse.org/tools/orbit/downloads/drops/S2015020220353
org.hamcrest.core.source [1.3.0.v201303031735,1.3.0.v201303031735]
javaewah [0.7.9.v201401101600,0.7.9.v201401101600]
javaewah.source [0.7.9.v201401101600,0.7.9.v201401101600]
- org.objenesis [1.0.0.v201105211943,1.0.0.v201105211943]
- org.objenesis.source [1.0.0.v201105211943,1.0.0.v201105211943]
+ org.objenesis [1.0.0.v201505121915,1.0.0.v201505121915]
+ org.objenesis.source [1.0.0.v201505121915,1.0.0.v201505121915]
org.mockito [1.8.4.v201303031500,1.8.4.v201303031500]
org.mockito.source [1.8.4.v201303031500,1.8.4.v201303031500]
- com.jcraft.jsch [0.1.51.v201410302000,0.1.51.v201410302000]
- com.jcraft.jsch.source [0.1.51.v201410302000,0.1.51.v201410302000]
+ com.jcraft.jsch [0.1.53.v201508180515,0.1.53.v201508180515]
+ com.jcraft.jsch.source [0.1.53.v201508180515,0.1.53.v201508180515]
org.junit [4.11.0.v201303080030,4.11.0.v201303080030]
org.junit.source [4.11.0.v201303080030,4.11.0.v201303080030]
- javax.servlet [2.5.0.v201103041518,2.5.0.v201103041518]
- javax.servlet.source [2.5.0.v201103041518,2.5.0.v201103041518]
+ javax.servlet [3.1.0.v201410161800,3.1.0.v201410161800]
+ javax.servlet.source [3.1.0.v201410161800,3.1.0.v201410161800]
org.tukaani.xz [1.3.0.v201308270617,1.3.0.v201308270617]
org.tukaani.xz.source [1.3.0.v201308270617,1.3.0.v201308270617]
org.slf4j.api [1.7.2.v20121108-1250,1.7.2.v20121108-1250]
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml
index 64a2d31..01ff977 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/pom.xml
@@ -49,7 +49,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>3.7.1.201504261725-r</version>
+ <version>4.2.0.201601211800-r</version>
</parent>
<artifactId>org.eclipse.jgit.target</artifactId>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-7.6.14.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-7.6.14.tpd
deleted file mode 100644
index 2e338d6..0000000
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-7.6.14.tpd
+++ /dev/null
@@ -1,20 +0,0 @@
-target "jetty-7.6.14" with source configurePhase
-
-location jetty-7.6.14 "http://download.eclipse.org/jetty/updates/jetty-bundles-7.x/7.6.14.v20131031/" {
- org.eclipse.jetty.client [7.6.14.v20131031,7.6.14.v20131031]
- org.eclipse.jetty.client.source [7.6.14.v20131031,7.6.14.v20131031]
- org.eclipse.jetty.continuation [7.6.14.v20131031,7.6.14.v20131031]
- org.eclipse.jetty.continuation.source [7.6.14.v20131031,7.6.14.v20131031]
- org.eclipse.jetty.http [7.6.14.v20131031,7.6.14.v20131031]
- org.eclipse.jetty.http.source [7.6.14.v20131031,7.6.14.v20131031]
- org.eclipse.jetty.io [7.6.14.v20131031,7.6.14.v20131031]
- org.eclipse.jetty.io.source [7.6.14.v20131031,7.6.14.v20131031]
- org.eclipse.jetty.security [7.6.14.v20131031,7.6.14.v20131031]
- org.eclipse.jetty.security.source [7.6.14.v20131031,7.6.14.v20131031]
- org.eclipse.jetty.server [7.6.14.v20131031,7.6.14.v20131031]
- org.eclipse.jetty.server.source [7.6.14.v20131031,7.6.14.v20131031]
- org.eclipse.jetty.servlet [7.6.14.v20131031,7.6.14.v20131031]
- org.eclipse.jetty.servlet.source [7.6.14.v20131031,7.6.14.v20131031]
- org.eclipse.jetty.util [7.6.14.v20131031,7.6.14.v20131031]
- org.eclipse.jetty.util.source [7.6.14.v20131031,7.6.14.v20131031]
-}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.2.13.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.2.13.tpd
new file mode 100644
index 0000000..289a73d
--- /dev/null
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.2.13.tpd
@@ -0,0 +1,20 @@
+target "jetty-9.2.13" with source configurePhase
+
+location jetty-9.2.13 "http://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.2.13.v20150730/" {
+ org.eclipse.jetty.client [9.2.13.v20150730,9.2.13.v20150730]
+ org.eclipse.jetty.client.source [9.2.13.v20150730,9.2.13.v20150730]
+ org.eclipse.jetty.continuation [9.2.13.v20150730,9.2.13.v20150730]
+ org.eclipse.jetty.continuation.source [9.2.13.v20150730,9.2.13.v20150730]
+ org.eclipse.jetty.http [9.2.13.v20150730,9.2.13.v20150730]
+ org.eclipse.jetty.http.source [9.2.13.v20150730,9.2.13.v20150730]
+ org.eclipse.jetty.io [9.2.13.v20150730,9.2.13.v20150730]
+ org.eclipse.jetty.io.source [9.2.13.v20150730,9.2.13.v20150730]
+ org.eclipse.jetty.security [9.2.13.v20150730,9.2.13.v20150730]
+ org.eclipse.jetty.security.source [9.2.13.v20150730,9.2.13.v20150730]
+ org.eclipse.jetty.server [9.2.13.v20150730,9.2.13.v20150730]
+ org.eclipse.jetty.server.source [9.2.13.v20150730,9.2.13.v20150730]
+ org.eclipse.jetty.servlet [9.2.13.v20150730,9.2.13.v20150730]
+ org.eclipse.jetty.servlet.source [9.2.13.v20150730,9.2.13.v20150730]
+ org.eclipse.jetty.util [9.2.13.v20150730,9.2.13.v20150730]
+ org.eclipse.jetty.util.source [9.2.13.v20150730,9.2.13.v20150730]
+}
diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml
index 5b0c3b2..563b547 100644
--- a/org.eclipse.jgit.packaging/pom.xml
+++ b/org.eclipse.jgit.packaging/pom.xml
@@ -53,13 +53,13 @@
<groupId>org.eclipse.jgit</groupId>
<artifactId>jgit.tycho.parent</artifactId>
- <version>3.7.1.201504261725-r</version>
+ <version>4.2.0.201601211800-r</version>
<packaging>pom</packaging>
<name>JGit Tycho Parent</name>
<properties>
- <tycho-version>0.22.0</tycho-version>
+ <tycho-version>0.23.0</tycho-version>
<tycho-extras-version>${tycho-version}</tycho-extras-version>
</properties>
@@ -68,13 +68,16 @@
<id>repo.eclipse.org.cbi-releases</id>
<url>https://repo.eclipse.org/content/repositories/cbi-releases/</url>
</pluginRepository>
+ <pluginRepository>
+ <id>repo.eclipse.org.cbi-snapshots</id>
+ <url>https://repo.eclipse.org/content/repositories/cbi-snapshots/</url>
+ </pluginRepository>
</pluginRepositories>
<modules>
<module>org.eclipse.jgit.target</module>
<module>org.eclipse.jgit.feature</module>
<module>org.eclipse.jgit.http.apache.feature</module>
- <module>org.eclipse.jgit.java7.feature</module>
<module>org.eclipse.jgit.pgm.feature</module>
<module>org.eclipse.jgit.source.feature</module>
<module>org.eclipse.jgit.pgm.source.feature</module>
@@ -106,12 +109,6 @@
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
- <artifactId>org.eclipse.jgit.java7</artifactId>
- <version>${project.version}</version>
- <classifier>sources</classifier>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.pgm</artifactId>
<version>${project.version}</version>
<classifier>sources</classifier>
@@ -143,6 +140,8 @@
<version>${tycho-version}</version>
<configuration>
<encoding>UTF-8</encoding>
+ <source>1.7</source>
+ <target>1.7</target>
</configuration>
</plugin>
<plugin>
@@ -215,7 +214,7 @@
<plugin>
<groupId>org.eclipse.cbi.maven.plugins</groupId>
<artifactId>eclipse-jarsigner-plugin</artifactId>
- <version>1.1.1</version>
+ <version>1.1.2</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
@@ -230,7 +229,6 @@
<profile>
<id>platform-kepler</id>
<activation>
- <activeByDefault>true</activeByDefault>
<property>
<name>platform-version-name</name>
<value>kepler</value>
@@ -255,6 +253,7 @@
<profile>
<id>platform-mars</id>
<activation>
+ <activeByDefault>true</activeByDefault>
<property>
<name>platform-version-name</name>
<value>mars</value>
diff --git a/org.eclipse.jgit.pgm.test/.classpath b/org.eclipse.jgit.pgm.test/.classpath
index a940818..30d83d8 100644
--- a/org.eclipse.jgit.pgm.test/.classpath
+++ b/org.eclipse.jgit.pgm.test/.classpath
@@ -2,7 +2,7 @@
<classpath>
<classpathentry kind="src" path="tst"/>
<classpathentry kind="src" path="src"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs
index 5fb6234..dcc0d3a 100644
--- a/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.pgm.test/.settings/org.eclipse.jdt.core.prefs
@@ -7,9 +7,9 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
@@ -111,7 +111,7 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
-org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.compiler.source=1.7
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
diff --git a/org.eclipse.jgit.pgm.test/BUCK b/org.eclipse.jgit.pgm.test/BUCK
new file mode 100644
index 0000000..a3859c9
--- /dev/null
+++ b/org.eclipse.jgit.pgm.test/BUCK
@@ -0,0 +1,38 @@
+TESTS = glob(['tst/**/*.java'])
+
+for t in TESTS:
+ n = t[len('tst/'):len(t)-len('.java')].replace('/', '.')
+ java_test(
+ name = n,
+ labels = ['pgm'],
+ srcs = [t],
+ deps = [
+ ':helpers',
+ '//org.eclipse.jgit:jgit',
+ '//org.eclipse.jgit.archive:jgit-archive',
+ '//org.eclipse.jgit.junit:junit',
+ '//org.eclipse.jgit.pgm:pgm',
+ '//lib:hamcrest-core',
+ '//lib:hamcrest-library',
+ '//lib:javaewah',
+ '//lib:junit',
+ '//lib:slf4j-api',
+ '//lib:slf4j-simple',
+ '//lib:commons-compress',
+ '//lib:tukaani-xz',
+ ],
+ source_under_test = ['//org.eclipse.jgit.pgm:pgm'],
+ vm_args = ['-Xmx256m', '-Dfile.encoding=UTF-8'],
+ )
+
+java_library(
+ name = 'helpers',
+ srcs = glob(['src/**/*.java']),
+ deps = [
+ '//org.eclipse.jgit:jgit',
+ '//org.eclipse.jgit.pgm:pgm',
+ '//org.eclipse.jgit.junit:junit',
+ '//lib:args4j',
+ '//lib:junit',
+ ],
+)
diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
index 7624c09..2996996 100644
--- a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
@@ -2,27 +2,28 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Bundle-SymbolicName: org.eclipse.jgit.pgm.test
-Bundle-Version: 3.7.1.201504261725-r
+Bundle-Version: 4.2.0.201601211800-r
Bundle-Vendor: %provider_name
Bundle-Localization: plugin
Bundle-ActivationPolicy: lazy
-Bundle-RequiredExecutionEnvironment: J2SE-1.5
-Import-Package: org.eclipse.jgit.api;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.api.errors;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.diff;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.dircache;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.junit;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.lib;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.merge;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.pgm;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.pgm.internal;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.pgm.opt;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.revwalk;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.storage.file;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.transport;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.treewalk;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.util;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.util.io;version="[3.7.1,3.8.0)",
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
+Import-Package: org.eclipse.jgit.api;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.api.errors;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.diff;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.dircache;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.internal.storage.file;version="4.2.0",
+ org.eclipse.jgit.junit;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.lib;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.merge;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.pgm;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.pgm.internal;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.pgm.opt;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.revwalk;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.storage.file;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.transport;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.treewalk;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.util;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.util.io;version="[4.2.0,4.3.0)",
org.hamcrest.core;bundle-version="[1.1.0,2.0.0)",
org.junit;version="[4.4.0,5.0.0)",
org.kohsuke.args4j;version="[2.0.12,2.1.0)"
diff --git a/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java7).launch b/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java7).launch
index 600ce7b..3df0dcb 100644
--- a/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java7).launch
+++ b/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java7).launch
@@ -15,12 +15,6 @@
<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
-<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
-<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7" path="1" type="4"/>
"/>
-<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath">
<memento exportedEntriesOnly="false" project="org.eclipse.jgit.pgm.test"/>
</runtimeClasspathEntry>
"/>
-<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry path="3" projectName="org.eclipse.jgit.java7" type="1"/>
"/>
-</listAttribute>
-<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.jgit.pgm.test"/>
diff --git a/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java8).launch b/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java8) (de).launch
similarity index 90%
copy from org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java8).launch
copy to org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java8) (de).launch
index 1b61d3d..5c137f2 100644
--- a/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java8).launch
+++ b/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java8) (de).launch
@@ -7,6 +7,9 @@
<listEntry value="2"/>
</listAttribute>
<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+<mapAttribute key="org.eclipse.debug.core.environmentVariables">
+<mapEntry key="LANG" value="de_DE.UTF-8"/>
+</mapAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
@@ -19,7 +22,6 @@
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7" path="1" type="4"/>
"/>
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath">
<memento exportedEntriesOnly="false" project="org.eclipse.jgit.pgm.test"/>
</runtimeClasspathEntry>
"/>
-<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry path="3" projectName="org.eclipse.jgit.java7" type="1"/>
"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
diff --git a/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java8).launch b/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java8).launch
index 1b61d3d..ce473ed 100644
--- a/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java8).launch
+++ b/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java8).launch
@@ -19,7 +19,6 @@
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7" path="1" type="4"/>
"/>
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath">
<memento exportedEntriesOnly="false" project="org.eclipse.jgit.pgm.test"/>
</runtimeClasspathEntry>
"/>
-<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry path="3" projectName="org.eclipse.jgit.java7" type="1"/>
"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
diff --git a/org.eclipse.jgit.pgm.test/pom.xml b/org.eclipse.jgit.pgm.test/pom.xml
index 891488e..04453bf 100644
--- a/org.eclipse.jgit.pgm.test/pom.xml
+++ b/org.eclipse.jgit.pgm.test/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>3.7.1.201504261725-r</version>
+ <version>4.2.0.201601211800-r</version>
</parent>
<artifactId>org.eclipse.jgit.pgm.test</artifactId>
@@ -60,23 +60,6 @@
Tests for command line client tools built on top of JGit.
</description>
- <profiles>
- <profile>
- <id>jgit.java7</id>
- <activation>
- <jdk>[1.7,)</jdk>
- </activation>
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jgit</groupId>
- <artifactId>org.eclipse.jgit.java7</artifactId>
- <version>${project.version}</version>
- <optional>true</optional>
- </dependency>
- </dependencies>
- </profile>
- </profiles>
-
<dependencies>
<dependency>
<groupId>junit</groupId>
diff --git a/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/lib/CLIRepositoryTestCase.java b/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/lib/CLIRepositoryTestCase.java
index 50ddfe0..a6af077 100644
--- a/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/lib/CLIRepositoryTestCase.java
+++ b/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/lib/CLIRepositoryTestCase.java
@@ -46,12 +46,16 @@ import static org.junit.Assert.assertEquals;
import java.io.File;
import java.io.IOException;
+import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
import org.eclipse.jgit.pgm.CLIGitCommand;
+import org.eclipse.jgit.pgm.CLIGitCommand.Result;
+import org.eclipse.jgit.pgm.TextBuiltin.TerminatedByHelpException;
import org.junit.Before;
public class CLIRepositoryTestCase extends LocalDiskRepositoryTestCase {
@@ -69,13 +73,59 @@ public class CLIRepositoryTestCase extends LocalDiskRepositoryTestCase {
trash = db.getWorkTree();
}
+ /**
+ * Executes specified git commands (with arguments)
+ *
+ * @param cmds
+ * each string argument must be a valid git command line, e.g.
+ * "git branch -h"
+ * @return command output
+ * @throws Exception
+ */
+ protected String[] executeUnchecked(String... cmds) throws Exception {
+ List<String> result = new ArrayList<String>(cmds.length);
+ for (String cmd : cmds) {
+ result.addAll(CLIGitCommand.executeUnchecked(cmd, db));
+ }
+ return result.toArray(new String[0]);
+ }
+
+ /**
+ * Executes specified git commands (with arguments), throws exception and
+ * stops execution on first command which output contains a 'fatal:' error
+ *
+ * @param cmds
+ * each string argument must be a valid git command line, e.g.
+ * "git branch -h"
+ * @return command output
+ * @throws Exception
+ */
protected String[] execute(String... cmds) throws Exception {
List<String> result = new ArrayList<String>(cmds.length);
- for (String cmd : cmds)
- result.addAll(CLIGitCommand.execute(cmd, db));
+ for (String cmd : cmds) {
+ Result r = CLIGitCommand.executeRaw(cmd, db);
+ if (r.ex instanceof TerminatedByHelpException) {
+ result.addAll(r.errLines());
+ } else if (r.ex != null) {
+ throw r.ex;
+ }
+ result.addAll(r.outLines());
+ }
return result.toArray(new String[0]);
}
+ /**
+ * @param link
+ * the path of the symbolic link to create
+ * @param target
+ * the target of the symbolic link
+ * @return the path to the symbolic link
+ * @throws Exception
+ */
+ protected Path writeLink(String link, String target) throws Exception {
+ return JGitTestUtil.writeLink(db, link, target);
+ }
+
protected File writeTrashFile(final String name, final String data)
throws IOException {
return JGitTestUtil.writeTrashFile(db, name, data);
@@ -164,16 +214,45 @@ public class CLIRepositoryTestCase extends LocalDiskRepositoryTestCase {
.replaceAll("\t", "\\\\t");
}
+ protected void assertStringArrayEquals(String expected, String[] actual) {
+ // if there is more than one line, ignore last one if empty
+ assertEquals(1,
+ actual.length > 1 && actual[actual.length - 1].equals("")
+ ? actual.length - 1 : actual.length);
+ assertEquals(expected, actual[0]);
+ }
+
protected void assertArrayOfLinesEquals(String[] expected, String[] actual) {
- assertEquals(toText(expected), toText(actual));
+ assertEquals(toString(expected), toString(actual));
+ }
+
+ public static String toString(String... lines) {
+ return toString(Arrays.asList(lines));
}
- private static String toText(String[] lines) {
+ public static String toString(List<String> lines) {
StringBuilder b = new StringBuilder();
for (String s : lines) {
- b.append(s);
- b.append('\n');
+ // trim indentation, to simplify tests
+ s = s.trim();
+ if (s != null && !s.isEmpty()) {
+ b.append(s);
+ b.append('\n');
+ }
+ }
+ // delete last line break to allow simpler tests with one line compare
+ if (b.length() > 0 && b.charAt(b.length() - 1) == '\n') {
+ b.deleteCharAt(b.length() - 1);
}
return b.toString();
}
+
+ public static boolean contains(List<String> lines, String str) {
+ for (String s : lines) {
+ if (s.contains(str)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/pgm/CLIGitCommand.java b/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/pgm/CLIGitCommand.java
index d77b150..3f39656 100644
--- a/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/pgm/CLIGitCommand.java
+++ b/org.eclipse.jgit.pgm.test/src/org/eclipse/jgit/pgm/CLIGitCommand.java
@@ -42,71 +42,140 @@
*/
package org.eclipse.jgit.pgm;
+import static org.junit.Assert.assertNull;
+
import java.io.ByteArrayOutputStream;
-import java.text.MessageFormat;
+import java.io.File;
+
+import java.io.IOException;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.pgm.internal.CLIText;
-import org.eclipse.jgit.pgm.opt.CmdLineParser;
-import org.eclipse.jgit.pgm.opt.SubcommandHandler;
+import org.eclipse.jgit.pgm.TextBuiltin.TerminatedByHelpException;
import org.eclipse.jgit.util.IO;
-import org.kohsuke.args4j.Argument;
-public class CLIGitCommand {
- @Argument(index = 0, metaVar = "metaVar_command", required = true, handler = SubcommandHandler.class)
- private TextBuiltin subcommand;
+public class CLIGitCommand extends Main {
- @Argument(index = 1, metaVar = "metaVar_arg")
- private List<String> arguments = new ArrayList<String>();
+ private final Result result;
- public TextBuiltin getSubcommand() {
- return subcommand;
+ private final Repository db;
+
+ public CLIGitCommand(Repository db) {
+ super();
+ this.db = db;
+ result = new Result();
}
- public List<String> getArguments() {
- return arguments;
+ /**
+ * Executes git commands (with arguments) specified on the command line. The
+ * git repository (same for all commands) can be specified via system
+ * property "-Dgit_work_tree=path_to_work_tree". If the property is not set,
+ * current directory is used.
+ *
+ * @param args
+ * each element in the array must be a valid git command line,
+ * e.g. "git branch -h"
+ * @throws Exception
+ */
+ public static void main(String[] args) throws Exception {
+ String workDir = System.getProperty("git_work_tree");
+ if (workDir == null) {
+ workDir = ".";
+ System.out.println(
+ "System property 'git_work_tree' not specified, using current directory: "
+ + new File(workDir).getAbsolutePath());
+ }
+ try (Repository db = new FileRepository(workDir + "/.git")) {
+ for (String cmd : args) {
+ List<String> result = execute(cmd, db);
+ for (String line : result) {
+ System.out.println(line);
+ }
+ }
+ }
}
public static List<String> execute(String str, Repository db)
throws Exception {
+ Result result = executeRaw(str, db);
+ return getOutput(result);
+ }
+
+ public static Result executeRaw(String str, Repository db)
+ throws Exception {
+ CLIGitCommand cmd = new CLIGitCommand(db);
+ cmd.run(str);
+ return cmd.result;
+ }
+
+ public static List<String> executeUnchecked(String str, Repository db)
+ throws Exception {
+ CLIGitCommand cmd = new CLIGitCommand(db);
+ try {
+ cmd.run(str);
+ return getOutput(cmd.result);
+ } catch (Throwable e) {
+ return cmd.result.errLines();
+ }
+ }
+
+ private static List<String> getOutput(Result result) {
+ if (result.ex instanceof TerminatedByHelpException) {
+ return result.errLines();
+ }
+ return result.outLines();
+ }
+
+ private void run(String commandLine) throws Exception {
+ String[] argv = convertToMainArgs(commandLine);
try {
- return IO.readLines(new String(rawExecute(str, db)));
- } catch (Die e) {
- return IO.readLines(MessageFormat.format(CLIText.get().fatalError,
- e.getMessage()));
+ super.run(argv);
+ } catch (TerminatedByHelpException e) {
+ // this is not a failure, super called exit() on help
+ } finally {
+ writer.flush();
}
}
- public static byte[] rawExecute(String str, Repository db)
+ private static String[] convertToMainArgs(String str)
throws Exception {
String[] args = split(str);
- if (!args[0].equalsIgnoreCase("git") || args.length < 2)
+ if (!args[0].equalsIgnoreCase("git") || args.length < 2) {
throw new IllegalArgumentException(
"Expected 'git <command> [<args>]', was:" + str);
+ }
String[] argv = new String[args.length - 1];
System.arraycopy(args, 1, argv, 0, args.length - 1);
+ return argv;
+ }
- CLIGitCommand bean = new CLIGitCommand();
- final CmdLineParser clp = new CmdLineParser(bean);
- clp.parseArgument(argv);
-
- final TextBuiltin cmd = bean.getSubcommand();
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- cmd.outs = baos;
- if (cmd.requiresRepository())
- cmd.init(db, null);
- else
- cmd.init(null, null);
- try {
- cmd.execute(bean.getArguments().toArray(
- new String[bean.getArguments().size()]));
- } finally {
- if (cmd.outw != null)
- cmd.outw.flush();
+ @Override
+ PrintWriter createErrorWriter() {
+ return new PrintWriter(result.err);
+ }
+
+ void init(final TextBuiltin cmd) throws IOException {
+ cmd.outs = result.out;
+ cmd.errs = result.err;
+ super.init(cmd);
+ }
+
+ @Override
+ protected Repository openGitDir(String aGitdir) throws IOException {
+ assertNull(aGitdir);
+ return db;
+ }
+
+ @Override
+ void exit(int status, Exception t) throws Exception {
+ if (t == null) {
+ t = new IllegalStateException(Integer.toString(status));
}
- return baos.toByteArray();
+ result.ex = t;
+ throw t;
}
/**
@@ -164,4 +233,36 @@ public class CLIGitCommand {
return list.toArray(new String[list.size()]);
}
+ public static class Result {
+ public final ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ public final ByteArrayOutputStream err = new ByteArrayOutputStream();
+
+ public Exception ex;
+
+ public byte[] outBytes() {
+ return out.toByteArray();
+ }
+
+ public byte[] errBytes() {
+ return err.toByteArray();
+ }
+
+ public String outString() {
+ return out.toString();
+ }
+
+ public List<String> outLines() {
+ return IO.readLines(out.toString());
+ }
+
+ public String errString() {
+ return err.toString();
+ }
+
+ public List<String> errLines() {
+ return IO.readLines(err.toString());
+ }
+ }
+
}
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/AddTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/AddTest.java
index 4253080..3edd9b8 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/AddTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/AddTest.java
@@ -45,15 +45,12 @@ package org.eclipse.jgit.pgm;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-
-import java.lang.Exception;
-import java.lang.String;
+import static org.junit.Assert.fail;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.lib.CLIRepositoryTestCase;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
public class AddTest extends CLIRepositoryTestCase {
@@ -66,14 +63,16 @@ public class AddTest extends CLIRepositoryTestCase {
git = new Git(db);
}
- @Ignore("args4j exit()s on error instead of throwing, JVM goes down")
@Test
public void testAddNothing() throws Exception {
- assertEquals("fatal: Argument \"filepattern\" is required", //
- execute("git add")[0]);
+ try {
+ execute("git add");
+ fail("Must die");
+ } catch (Die e) {
+ // expected, requires argument
+ }
}
- @Ignore("args4j exit()s for --help, too")
@Test
public void testAddUsage() throws Exception {
execute("git add --help");
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java
index 6ce092d..a503ffd 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ArchiveTest.java
@@ -52,17 +52,15 @@ import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
-import java.io.InputStreamReader;
import java.io.IOException;
+import java.io.InputStreamReader;
import java.io.OutputStream;
-import java.lang.Object;
-import java.lang.String;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
-import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
@@ -71,9 +69,7 @@ import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.lib.CLIRepositoryTestCase;
import org.eclipse.jgit.lib.FileMode;
-import org.eclipse.jgit.pgm.CLIGitCommand;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
public class ArchiveTest extends CLIRepositoryTestCase {
@@ -89,25 +85,26 @@ public class ArchiveTest extends CLIRepositoryTestCase {
emptyTree = db.resolve("HEAD^{tree}").abbreviate(12).name();
}
- @Ignore("Some versions of java.util.zip refuse to write an empty ZIP")
@Test
public void testEmptyArchive() throws Exception {
- final byte[] result = CLIGitCommand.rawExecute( //
- "git archive --format=zip " + emptyTree, db);
+ byte[] result = CLIGitCommand.executeRaw(
+ "git archive --format=zip " + emptyTree, db).outBytes();
assertArrayEquals(new String[0], listZipEntries(result));
}
@Test
public void testEmptyTar() throws Exception {
- final byte[] result = CLIGitCommand.rawExecute( //
- "git archive --format=tar " + emptyTree, db);
+ byte[] result = CLIGitCommand.executeRaw(
+ "git archive --format=tar " + emptyTree, db).outBytes();
assertArrayEquals(new String[0], listTarEntries(result));
}
@Test
public void testUnrecognizedFormat() throws Exception {
- final String[] expect = new String[] { "fatal: Unknown archive format 'nonsense'" };
- final String[] actual = execute("git archive --format=nonsense " + emptyTree);
+ String[] expect = new String[] {
+ "fatal: Unknown archive format 'nonsense'", "" };
+ String[] actual = executeUnchecked(
+ "git archive --format=nonsense " + emptyTree);
assertArrayEquals(expect, actual);
}
@@ -120,9 +117,9 @@ public class ArchiveTest extends CLIRepositoryTestCase {
git.add().addFilepattern("c").call();
git.commit().setMessage("populate toplevel").call();
- final byte[] result = CLIGitCommand.rawExecute( //
- "git archive --format=zip HEAD", db);
- assertArrayEquals(new String[] { "a", "c" }, //
+ byte[] result = CLIGitCommand.executeRaw(
+ "git archive --format=zip HEAD", db).outBytes();
+ assertArrayEquals(new String[] { "a", "c" },
listZipEntries(result));
}
@@ -135,9 +132,9 @@ public class ArchiveTest extends CLIRepositoryTestCase {
@Test
public void testDefaultFormatIsTar() throws Exception {
commitGreeting();
- final byte[] result = CLIGitCommand.rawExecute( //
- "git archive HEAD", db);
- assertArrayEquals(new String[] { "greeting" }, //
+ byte[] result = CLIGitCommand.executeRaw(
+ "git archive HEAD", db).outBytes();
+ assertArrayEquals(new String[] { "greeting" },
listTarEntries(result));
}
@@ -147,8 +144,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
@Test
public void testFormatOverridesFilename() throws Exception {
- final File archive = new File(db.getWorkTree(), "format-overrides-name.tar");
- final String path = archive.getAbsolutePath();
+ File archive = new File(db.getWorkTree(), "format-overrides-name.tar");
+ String path = archive.getAbsolutePath();
commitGreeting();
assertArrayEquals(new String[] { "" },
@@ -162,8 +159,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
@Test
public void testUnrecognizedExtensionMeansTar() throws Exception {
- final File archive = new File(db.getWorkTree(), "example.txt");
- final String path = archive.getAbsolutePath();
+ File archive = new File(db.getWorkTree(), "example.txt");
+ String path = archive.getAbsolutePath();
commitGreeting();
assertArrayEquals(new String[] { "" },
@@ -176,8 +173,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
@Test
public void testNoExtensionMeansTar() throws Exception {
- final File archive = new File(db.getWorkTree(), "example");
- final String path = archive.getAbsolutePath();
+ File archive = new File(db.getWorkTree(), "example");
+ String path = archive.getAbsolutePath();
commitGreeting();
assertArrayEquals(new String[] { "" },
@@ -189,8 +186,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
@Test
public void testExtensionMatchIsAnchored() throws Exception {
- final File archive = new File(db.getWorkTree(), "two-extensions.zip.bak");
- final String path = archive.getAbsolutePath();
+ File archive = new File(db.getWorkTree(), "two-extensions.zip.bak");
+ String path = archive.getAbsolutePath();
commitGreeting();
assertArrayEquals(new String[] { "" },
@@ -202,8 +199,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
@Test
public void testZipExtension() throws Exception {
- final File archiveWithDot = new File(db.getWorkTree(), "greeting.zip");
- final File archiveNoDot = new File(db.getWorkTree(), "greetingzip");
+ File archiveWithDot = new File(db.getWorkTree(), "greeting.zip");
+ File archiveNoDot = new File(db.getWorkTree(), "greetingzip");
commitGreeting();
execute("git archive " +
@@ -218,8 +215,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
@Test
public void testTarExtension() throws Exception {
- final File archive = new File(db.getWorkTree(), "tarball.tar");
- final String path = archive.getAbsolutePath();
+ File archive = new File(db.getWorkTree(), "tarball.tar");
+ String path = archive.getAbsolutePath();
commitGreeting();
assertArrayEquals(new String[] { "" },
@@ -234,8 +231,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
commitGreeting();
for (String ext : Arrays.asList("tar.gz", "tgz")) {
- final File archiveWithDot = new File(db.getWorkTree(), "tarball." + ext);
- final File archiveNoDot = new File(db.getWorkTree(), "tarball" + ext);
+ File archiveWithDot = new File(db.getWorkTree(), "tarball." + ext);
+ File archiveNoDot = new File(db.getWorkTree(), "tarball" + ext);
execute("git archive " +
shellQuote("--output=" + archiveWithDot.getAbsolutePath()) + " " +
@@ -253,8 +250,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
commitGreeting();
for (String ext : Arrays.asList("tar.bz2", "tbz", "tbz2")) {
- final File archiveWithDot = new File(db.getWorkTree(), "tarball." + ext);
- final File archiveNoDot = new File(db.getWorkTree(), "tarball" + ext);
+ File archiveWithDot = new File(db.getWorkTree(), "tarball." + ext);
+ File archiveNoDot = new File(db.getWorkTree(), "tarball" + ext);
execute("git archive " +
shellQuote("--output=" + archiveWithDot.getAbsolutePath()) + " " +
@@ -272,8 +269,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
commitGreeting();
for (String ext : Arrays.asList("tar.xz", "txz")) {
- final File archiveWithDot = new File(db.getWorkTree(), "tarball." + ext);
- final File archiveNoDot = new File(db.getWorkTree(), "tarball" + ext);
+ File archiveWithDot = new File(db.getWorkTree(), "tarball." + ext);
+ File archiveNoDot = new File(db.getWorkTree(), "tarball" + ext);
execute("git archive " +
shellQuote("--output=" + archiveWithDot.getAbsolutePath()) + " " +
@@ -302,8 +299,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
git.add().addFilepattern("b").call();
git.commit().setMessage("add subdir").call();
- final byte[] result = CLIGitCommand.rawExecute( //
- "git archive --format=zip master", db);
+ byte[] result = CLIGitCommand.executeRaw(
+ "git archive --format=zip master", db).outBytes();
String[] expect = { "a", "b.c", "b0c", "b/", "b/a", "b/b", "c" };
String[] actual = listZipEntries(result);
@@ -328,8 +325,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
git.add().addFilepattern("b").call();
git.commit().setMessage("add subdir").call();
- final byte[] result = CLIGitCommand.rawExecute( //
- "git archive --format=tar master", db);
+ byte[] result = CLIGitCommand.executeRaw(
+ "git archive --format=tar master", db).outBytes();
String[] expect = { "a", "b.c", "b0c", "b/", "b/a", "b/b", "c" };
String[] actual = listTarEntries(result);
@@ -349,8 +346,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
@Test
public void testArchivePrefixOption() throws Exception {
commitBazAndFooSlashBar();
- byte[] result = CLIGitCommand.rawExecute(
- "git archive --prefix=x/ --format=zip master", db);
+ byte[] result = CLIGitCommand.executeRaw(
+ "git archive --prefix=x/ --format=zip master", db).outBytes();
String[] expect = { "x/baz", "x/foo/", "x/foo/bar" };
String[] actual = listZipEntries(result);
@@ -362,8 +359,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
@Test
public void testTarPrefixOption() throws Exception {
commitBazAndFooSlashBar();
- byte[] result = CLIGitCommand.rawExecute(
- "git archive --prefix=x/ --format=tar master", db);
+ byte[] result = CLIGitCommand.executeRaw(
+ "git archive --prefix=x/ --format=tar master", db).outBytes();
String[] expect = { "x/baz", "x/foo/", "x/foo/bar" };
String[] actual = listTarEntries(result);
@@ -381,8 +378,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
@Test
public void testPrefixDoesNotNormalizeDoubleSlash() throws Exception {
commitFoo();
- byte[] result = CLIGitCommand.rawExecute(
- "git archive --prefix=x// --format=zip master", db);
+ byte[] result = CLIGitCommand.executeRaw(
+ "git archive --prefix=x// --format=zip master", db).outBytes();
String[] expect = { "x//foo" };
assertArrayEquals(expect, listZipEntries(result));
}
@@ -390,8 +387,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
@Test
public void testPrefixDoesNotNormalizeDoubleSlashInTar() throws Exception {
commitFoo();
- final byte[] result = CLIGitCommand.rawExecute( //
- "git archive --prefix=x// --format=tar master", db);
+ byte[] result = CLIGitCommand.executeRaw(
+ "git archive --prefix=x// --format=tar master", db).outBytes();
String[] expect = { "x//foo" };
assertArrayEquals(expect, listTarEntries(result));
}
@@ -408,8 +405,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
@Test
public void testPrefixWithoutTrailingSlash() throws Exception {
commitBazAndFooSlashBar();
- byte[] result = CLIGitCommand.rawExecute(
- "git archive --prefix=my- --format=zip master", db);
+ byte[] result = CLIGitCommand.executeRaw(
+ "git archive --prefix=my- --format=zip master", db).outBytes();
String[] expect = { "my-baz", "my-foo/", "my-foo/bar" };
String[] actual = listZipEntries(result);
@@ -421,8 +418,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
@Test
public void testTarPrefixWithoutTrailingSlash() throws Exception {
commitBazAndFooSlashBar();
- final byte[] result = CLIGitCommand.rawExecute( //
- "git archive --prefix=my- --format=tar master", db);
+ byte[] result = CLIGitCommand.executeRaw(
+ "git archive --prefix=my- --format=tar master", db).outBytes();
String[] expect = { "my-baz", "my-foo/", "my-foo/bar" };
String[] actual = listTarEntries(result);
@@ -441,8 +438,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
git.submoduleAdd().setURI("./.").setPath("b").call().close();
git.commit().setMessage("add submodule").call();
- final byte[] result = CLIGitCommand.rawExecute( //
- "git archive --format=zip master", db);
+ byte[] result = CLIGitCommand.executeRaw(
+ "git archive --format=zip master", db).outBytes();
String[] expect = { ".gitmodules", "a", "b/", "c" };
String[] actual = listZipEntries(result);
@@ -461,8 +458,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
git.submoduleAdd().setURI("./.").setPath("b").call().close();
git.commit().setMessage("add submodule").call();
- final byte[] result = CLIGitCommand.rawExecute( //
- "git archive --format=tar master", db);
+ byte[] result = CLIGitCommand.executeRaw(
+ "git archive --format=tar master", db).outBytes();
String[] expect = { ".gitmodules", "a", "b/", "c" };
String[] actual = listTarEntries(result);
@@ -491,8 +488,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
git.commit().setMessage("three files with different modes").call();
- final byte[] zipData = CLIGitCommand.rawExecute( //
- "git archive --format=zip master", db);
+ byte[] zipData = CLIGitCommand.executeRaw(
+ "git archive --format=zip master", db).outBytes();
writeRaw("zip-with-modes.zip", zipData);
assertContainsEntryWithMode("zip-with-modes.zip", "-rw-", "plain");
assertContainsEntryWithMode("zip-with-modes.zip", "-rwx", "executable");
@@ -520,8 +517,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
git.commit().setMessage("three files with different modes").call();
- final byte[] archive = CLIGitCommand.rawExecute( //
- "git archive --format=tar master", db);
+ byte[] archive = CLIGitCommand.executeRaw(
+ "git archive --format=tar master", db).outBytes();
writeRaw("with-modes.tar", archive);
assertTarContainsEntry("with-modes.tar", "-rw-r--r--", "plain");
assertTarContainsEntry("with-modes.tar", "-rwxr-xr-x", "executable");
@@ -532,7 +529,7 @@ public class ArchiveTest extends CLIRepositoryTestCase {
@Test
public void testArchiveWithLongFilename() throws Exception {
String filename = "";
- final List<String> l = new ArrayList<String>();
+ List<String> l = new ArrayList<String>();
for (int i = 0; i < 20; i++) {
filename = filename + "1234567890/";
l.add(filename);
@@ -543,8 +540,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
git.add().addFilepattern("1234567890").call();
git.commit().setMessage("file with long name").call();
- final byte[] result = CLIGitCommand.rawExecute( //
- "git archive --format=zip HEAD", db);
+ byte[] result = CLIGitCommand.executeRaw(
+ "git archive --format=zip HEAD", db).outBytes();
assertArrayEquals(l.toArray(new String[l.size()]),
listZipEntries(result));
}
@@ -552,7 +549,7 @@ public class ArchiveTest extends CLIRepositoryTestCase {
@Test
public void testTarWithLongFilename() throws Exception {
String filename = "";
- final List<String> l = new ArrayList<String>();
+ List<String> l = new ArrayList<String>();
for (int i = 0; i < 20; i++) {
filename = filename + "1234567890/";
l.add(filename);
@@ -563,42 +560,42 @@ public class ArchiveTest extends CLIRepositoryTestCase {
git.add().addFilepattern("1234567890").call();
git.commit().setMessage("file with long name").call();
- final byte[] result = CLIGitCommand.rawExecute( //
- "git archive --format=tar HEAD", db);
+ byte[] result = CLIGitCommand.executeRaw(
+ "git archive --format=tar HEAD", db).outBytes();
assertArrayEquals(l.toArray(new String[l.size()]),
listTarEntries(result));
}
@Test
public void testArchivePreservesContent() throws Exception {
- final String payload = "“The quick brown fox jumps over the lazy dog!”";
+ String payload = "“The quick brown fox jumps over the lazy dog!”";
writeTrashFile("xyzzy", payload);
git.add().addFilepattern("xyzzy").call();
git.commit().setMessage("add file with content").call();
- final byte[] result = CLIGitCommand.rawExecute( //
- "git archive --format=zip HEAD", db);
- assertArrayEquals(new String[] { payload }, //
+ byte[] result = CLIGitCommand.executeRaw(
+ "git archive --format=zip HEAD", db).outBytes();
+ assertArrayEquals(new String[] { payload },
zipEntryContent(result, "xyzzy"));
}
@Test
public void testTarPreservesContent() throws Exception {
- final String payload = "“The quick brown fox jumps over the lazy dog!”";
+ String payload = "“The quick brown fox jumps over the lazy dog!”";
writeTrashFile("xyzzy", payload);
git.add().addFilepattern("xyzzy").call();
git.commit().setMessage("add file with content").call();
- final byte[] result = CLIGitCommand.rawExecute( //
- "git archive --format=tar HEAD", db);
- assertArrayEquals(new String[] { payload }, //
+ byte[] result = CLIGitCommand.executeRaw(
+ "git archive --format=tar HEAD", db).outBytes();
+ assertArrayEquals(new String[] { payload },
tarEntryContent(result, "xyzzy"));
}
private Process spawnAssumingCommandPresent(String... cmdline) {
- final File cwd = db.getWorkTree();
- final ProcessBuilder procBuilder = new ProcessBuilder(cmdline) //
- .directory(cwd) //
+ File cwd = db.getWorkTree();
+ ProcessBuilder procBuilder = new ProcessBuilder(cmdline)
+ .directory(cwd)
.redirectErrorStream(true);
Process proc = null;
try {
@@ -612,15 +609,15 @@ public class ArchiveTest extends CLIRepositoryTestCase {
}
private BufferedReader readFromProcess(Process proc) throws Exception {
- return new BufferedReader( //
+ return new BufferedReader(
new InputStreamReader(proc.getInputStream(), "UTF-8"));
}
- private void grepForEntry(String name, String mode, String... cmdline) //
+ private void grepForEntry(String name, String mode, String... cmdline)
throws Exception {
- final Process proc = spawnAssumingCommandPresent(cmdline);
+ Process proc = spawnAssumingCommandPresent(cmdline);
proc.getOutputStream().close();
- final BufferedReader reader = readFromProcess(proc);
+ BufferedReader reader = readFromProcess(proc);
try {
String line;
while ((line = reader.readLine()) != null)
@@ -672,20 +669,20 @@ public class ArchiveTest extends CLIRepositoryTestCase {
assertMagic(new byte[] { (byte) 0xfd, '7', 'z', 'X', 'Z', 0 }, file);
}
- private void assertContainsEntryWithMode(String zipFilename, String mode, String name) //
+ private void assertContainsEntryWithMode(String zipFilename, String mode, String name)
throws Exception {
grepForEntry(name, mode, "zipinfo", zipFilename);
}
- private void assertTarContainsEntry(String tarfile, String mode, String name) //
+ private void assertTarContainsEntry(String tarfile, String mode, String name)
throws Exception {
grepForEntry(name, mode, "tar", "tvf", tarfile);
}
- private void writeRaw(String filename, byte[] data) //
+ private void writeRaw(String filename, byte[] data)
throws IOException {
- final File path = new File(db.getWorkTree(), filename);
- final OutputStream out = new FileOutputStream(path);
+ File path = new File(db.getWorkTree(), filename);
+ OutputStream out = new FileOutputStream(path);
try {
out.write(data);
} finally {
@@ -694,8 +691,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
}
private static String[] listZipEntries(byte[] zipData) throws IOException {
- final List<String> l = new ArrayList<String>();
- final ZipInputStream in = new ZipInputStream( //
+ List<String> l = new ArrayList<String>();
+ ZipInputStream in = new ZipInputStream(
new ByteArrayInputStream(zipData));
ZipEntry e;
@@ -706,9 +703,9 @@ public class ArchiveTest extends CLIRepositoryTestCase {
}
private static Future<Object> writeAsync(final OutputStream stream, final byte[] data) {
- final ExecutorService executor = Executors.newSingleThreadExecutor();
+ ExecutorService executor = Executors.newSingleThreadExecutor();
- return executor.submit(new Callable<Object>() { //
+ return executor.submit(new Callable<Object>() {
public Object call() throws IOException {
try {
stream.write(data);
@@ -721,13 +718,13 @@ public class ArchiveTest extends CLIRepositoryTestCase {
}
private String[] listTarEntries(byte[] tarData) throws Exception {
- final List<String> l = new ArrayList<String>();
- final Process proc = spawnAssumingCommandPresent("tar", "tf", "-");
- final BufferedReader reader = readFromProcess(proc);
- final OutputStream out = proc.getOutputStream();
+ List<String> l = new ArrayList<String>();
+ Process proc = spawnAssumingCommandPresent("tar", "tf", "-");
+ BufferedReader reader = readFromProcess(proc);
+ OutputStream out = proc.getOutputStream();
// Dump tarball to tar stdin in background
- final Future<?> writing = writeAsync(out, tarData);
+ Future<?> writing = writeAsync(out, tarData);
try {
String line;
@@ -742,9 +739,9 @@ public class ArchiveTest extends CLIRepositoryTestCase {
}
}
- private static String[] zipEntryContent(byte[] zipData, String path) //
+ private static String[] zipEntryContent(byte[] zipData, String path)
throws IOException {
- final ZipInputStream in = new ZipInputStream( //
+ ZipInputStream in = new ZipInputStream(
new ByteArrayInputStream(zipData));
ZipEntry e;
while ((e = in.getNextEntry()) != null) {
@@ -752,8 +749,8 @@ public class ArchiveTest extends CLIRepositoryTestCase {
continue;
// found!
- final List<String> l = new ArrayList<String>();
- final BufferedReader reader = new BufferedReader( //
+ List<String> l = new ArrayList<String>();
+ BufferedReader reader = new BufferedReader(
new InputStreamReader(in, "UTF-8"));
String line;
while ((line = reader.readLine()) != null)
@@ -765,13 +762,13 @@ public class ArchiveTest extends CLIRepositoryTestCase {
return null;
}
- private String[] tarEntryContent(byte[] tarData, String path) //
+ private String[] tarEntryContent(byte[] tarData, String path)
throws Exception {
- final List<String> l = new ArrayList<String>();
- final Process proc = spawnAssumingCommandPresent("tar", "Oxf", "-", path);
- final BufferedReader reader = readFromProcess(proc);
- final OutputStream out = proc.getOutputStream();
- final Future<?> writing = writeAsync(out, tarData);
+ List<String> l = new ArrayList<String>();
+ Process proc = spawnAssumingCommandPresent("tar", "Oxf", "-", path);
+ BufferedReader reader = readFromProcess(proc);
+ OutputStream out = proc.getOutputStream();
+ Future<?> writing = writeAsync(out, tarData);
try {
String line;
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/BranchTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/BranchTest.java
index 4200cd0..55f4d8b 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/BranchTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/BranchTest.java
@@ -43,11 +43,17 @@
package org.eclipse.jgit.pgm;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.CLIRepositoryTestCase;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.pgm.internal.CLIText;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Before;
import org.junit.Test;
@@ -57,13 +63,25 @@ public class BranchTest extends CLIRepositoryTestCase {
@Before
public void setUp() throws Exception {
super.setUp();
- new Git(db).commit().setMessage("initial commit").call();
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("initial commit").call();
+ }
+ }
+
+ @Test
+ public void testHelpAfterDelete() throws Exception {
+ String err = toString(executeUnchecked("git branch -d"));
+ String help = toString(executeUnchecked("git branch -h"));
+ String errAndHelp = toString(executeUnchecked("git branch -d -h"));
+ assertEquals(CLIText.fatalError(CLIText.get().branchNameRequired), err);
+ assertEquals(toString(err, help), errAndHelp);
}
@Test
public void testList() throws Exception {
+ assertEquals("* master", toString(execute("git branch")));
assertEquals("* master 6fd41be initial commit",
- execute("git branch -v")[0]);
+ toString(execute("git branch -v")));
}
@Test
@@ -71,24 +89,188 @@ public class BranchTest extends CLIRepositoryTestCase {
RefUpdate updateRef = db.updateRef(Constants.HEAD, true);
updateRef.setNewObjectId(db.resolve("6fd41be"));
updateRef.update();
- assertEquals("* (no branch) 6fd41be initial commit",
- execute("git branch -v")[0]);
+ assertEquals(
+ toString("* (no branch) 6fd41be initial commit",
+ "master 6fd41be initial commit"),
+ toString(execute("git branch -v")));
}
@Test
public void testListContains() throws Exception {
- new Git(db).branchCreate().setName("initial").call();
- RevCommit second = new Git(db).commit().setMessage("second commit")
- .call();
- assertArrayOfLinesEquals(new String[] { " initial", "* master", "" },
- execute("git branch --contains 6fd41be"));
- assertArrayOfLinesEquals(new String[] { "* master", "" },
- execute("git branch --contains " + second.name()));
+ try (Git git = new Git(db)) {
+ git.branchCreate().setName("initial").call();
+ RevCommit second = git.commit().setMessage("second commit")
+ .call();
+ assertEquals(toString(" initial", "* master"),
+ toString(execute("git branch --contains 6fd41be")));
+ assertEquals("* master",
+ toString(execute("git branch --contains " + second.name())));
+ }
}
@Test
public void testExistingBranch() throws Exception {
assertEquals("fatal: A branch named 'master' already exists.",
- execute("git branch master")[0]);
+ toString(executeUnchecked("git branch master")));
+ }
+
+ @Test
+ public void testRenameSingleArg() throws Exception {
+ try {
+ toString(execute("git branch -m"));
+ fail("Must die");
+ } catch (Die e) {
+ // expected, requires argument
+ }
+ String result = toString(execute("git branch -m slave"));
+ assertEquals("", result);
+ result = toString(execute("git branch -a"));
+ assertEquals("* slave", result);
+ }
+
+ @Test
+ public void testRenameTwoArgs() throws Exception {
+ String result = toString(execute("git branch -m master slave"));
+ assertEquals("", result);
+ result = toString(execute("git branch -a"));
+ assertEquals("* slave", result);
+ }
+
+ @Test
+ public void testCreate() throws Exception {
+ try {
+ toString(execute("git branch a b"));
+ fail("Must die");
+ } catch (Die e) {
+ // expected, too many arguments
+ }
+ String result = toString(execute("git branch second"));
+ assertEquals("", result);
+ result = toString(execute("git branch"));
+ assertEquals(toString("* master", "second"), result);
+ result = toString(execute("git branch -v"));
+ assertEquals(toString("* master 6fd41be initial commit",
+ "second 6fd41be initial commit"), result);
+ }
+
+ @Test
+ public void testDelete() throws Exception {
+ try {
+ toString(execute("git branch -d"));
+ fail("Must die");
+ } catch (Die e) {
+ // expected, requires argument
+ }
+ String result = toString(execute("git branch second"));
+ assertEquals("", result);
+ result = toString(execute("git branch -d second"));
+ assertEquals("", result);
+ result = toString(execute("git branch"));
+ assertEquals("* master", result);
+ }
+
+ @Test
+ public void testDeleteMultiple() throws Exception {
+ String result = toString(execute("git branch second",
+ "git branch third", "git branch fourth"));
+ assertEquals("", result);
+ result = toString(execute("git branch -d second third fourth"));
+ assertEquals("", result);
+ result = toString(execute("git branch"));
+ assertEquals("* master", result);
+ }
+
+ @Test
+ public void testDeleteForce() throws Exception {
+ try {
+ toString(execute("git branch -D"));
+ fail("Must die");
+ } catch (Die e) {
+ // expected, requires argument
+ }
+ String result = toString(execute("git branch second"));
+ assertEquals("", result);
+ result = toString(execute("git checkout second"));
+ assertEquals("Switched to branch 'second'", result);
+
+ File a = writeTrashFile("a", "a");
+ assertTrue(a.exists());
+ execute("git add a", "git commit -m 'added a'");
+
+ result = toString(execute("git checkout master"));
+ assertEquals("Switched to branch 'master'", result);
+
+ result = toString(execute("git branch"));
+ assertEquals(toString("* master", "second"), result);
+
+ try {
+ toString(execute("git branch -d second"));
+ fail("Must die");
+ } catch (Die e) {
+ // expected, the current HEAD is on second and not merged to master
+ }
+ result = toString(execute("git branch -D second"));
+ assertEquals("", result);
+
+ result = toString(execute("git branch"));
+ assertEquals("* master", result);
+ }
+
+ @Test
+ public void testDeleteForceMultiple() throws Exception {
+ String result = toString(execute("git branch second",
+ "git branch third", "git branch fourth"));
+
+ assertEquals("", result);
+ result = toString(execute("git checkout second"));
+ assertEquals("Switched to branch 'second'", result);
+
+ File a = writeTrashFile("a", "a");
+ assertTrue(a.exists());
+ execute("git add a", "git commit -m 'added a'");
+
+ result = toString(execute("git checkout master"));
+ assertEquals("Switched to branch 'master'", result);
+
+ result = toString(execute("git branch"));
+ assertEquals(toString("fourth", "* master", "second", "third"), result);
+
+ try {
+ toString(execute("git branch -d second third fourth"));
+ fail("Must die");
+ } catch (Die e) {
+ // expected, the current HEAD is on second and not merged to master
+ }
+ result = toString(execute("git branch"));
+ assertEquals(toString("fourth", "* master", "second", "third"), result);
+
+ result = toString(execute("git branch -D second third fourth"));
+ assertEquals("", result);
+
+ result = toString(execute("git branch"));
+ assertEquals("* master", result);
+ }
+
+ @Test
+ public void testCreateFromOldCommit() throws Exception {
+ File a = writeTrashFile("a", "a");
+ assertTrue(a.exists());
+ execute("git add a", "git commit -m 'added a'");
+ File b = writeTrashFile("b", "b");
+ assertTrue(b.exists());
+ execute("git add b", "git commit -m 'added b'");
+ String result = toString(execute("git log -n 1 --reverse"));
+ String firstCommitId = result.substring("commit ".length(),
+ result.indexOf('\n'));
+
+ result = toString(execute("git branch -f second " + firstCommitId));
+ assertEquals("", result);
+
+ result = toString(execute("git branch"));
+ assertEquals(toString("* master", "second"), result);
+
+ result = toString(execute("git checkout second"));
+ assertEquals("Switched to branch 'second'", result);
+ assertFalse(b.exists());
}
}
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java
index cef9b9e..e690ad6 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java
@@ -43,9 +43,15 @@
package org.eclipse.jgit.pgm;
import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
import java.util.List;
import org.eclipse.jgit.api.Git;
@@ -58,91 +64,106 @@ import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.FileTreeIterator.FileEntry;
import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils;
-import org.junit.Assert;
+import org.junit.Assume;
import org.junit.Test;
public class CheckoutTest extends CLIRepositoryTestCase {
@Test
public void testCheckoutSelf() throws Exception {
- new Git(db).commit().setMessage("initial commit").call();
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("initial commit").call();
- assertEquals("Already on 'master'", execute("git checkout master"));
+ assertStringArrayEquals("Already on 'master'",
+ execute("git checkout master"));
+ }
}
@Test
public void testCheckoutBranch() throws Exception {
- new Git(db).commit().setMessage("initial commit").call();
- new Git(db).branchCreate().setName("side").call();
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("initial commit").call();
+ git.branchCreate().setName("side").call();
- assertEquals("Switched to branch 'side'", execute("git checkout side"));
+ assertStringArrayEquals("Switched to branch 'side'",
+ execute("git checkout side"));
+ }
}
@Test
public void testCheckoutNewBranch() throws Exception {
- new Git(db).commit().setMessage("initial commit").call();
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("initial commit").call();
- assertEquals("Switched to a new branch 'side'",
- execute("git checkout -b side"));
+ assertStringArrayEquals("Switched to a new branch 'side'",
+ execute("git checkout -b side"));
+ }
}
@Test
public void testCheckoutNonExistingBranch() throws Exception {
- assertEquals(
+ assertStringArrayEquals(
"error: pathspec 'side' did not match any file(s) known to git.",
execute("git checkout side"));
}
@Test
public void testCheckoutNewBranchThatAlreadyExists() throws Exception {
- new Git(db).commit().setMessage("initial commit").call();
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("initial commit").call();
- assertEquals("fatal: A branch named 'master' already exists.",
- execute("git checkout -b master"));
+ assertStringArrayEquals(
+ "fatal: A branch named 'master' already exists.",
+ executeUnchecked("git checkout -b master"));
+ }
}
@Test
public void testCheckoutNewBranchOnBranchToBeBorn() throws Exception {
- assertEquals("fatal: You are on a branch yet to be born",
- execute("git checkout -b side"));
+ assertStringArrayEquals("fatal: You are on a branch yet to be born",
+ executeUnchecked("git checkout -b side"));
}
@Test
public void testCheckoutUnresolvedHead() throws Exception {
- assertEquals(
+ assertStringArrayEquals(
"error: pathspec 'HEAD' did not match any file(s) known to git.",
execute("git checkout HEAD"));
}
@Test
public void testCheckoutHead() throws Exception {
- new Git(db).commit().setMessage("initial commit").call();
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("initial commit").call();
- assertEquals("", execute("git checkout HEAD"));
+ assertStringArrayEquals("", execute("git checkout HEAD"));
+ }
}
@Test
public void testCheckoutExistingBranchWithConflict() throws Exception {
- Git git = new Git(db);
- writeTrashFile("a", "Hello world a");
- git.add().addFilepattern(".").call();
- git.commit().setMessage("commit file a").call();
- git.branchCreate().setName("branch_1").call();
- git.rm().addFilepattern("a").call();
- FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
- writeTrashFile("a/b", "Hello world b");
- git.add().addFilepattern("a/b").call();
- git.commit().setMessage("commit folder a").call();
- git.rm().addFilepattern("a").call();
- writeTrashFile("a", "New Hello world a");
- git.add().addFilepattern(".").call();
-
- String[] execute = execute("git checkout branch_1");
- Assert.assertEquals(
- "error: Your local changes to the following files would be overwritten by checkout:",
- execute[0]);
- Assert.assertEquals("\ta", execute[1]);
+ try (Git git = new Git(db)) {
+ writeTrashFile("a", "Hello world a");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("commit file a").call();
+ git.branchCreate().setName("branch_1").call();
+ git.rm().addFilepattern("a").call();
+ FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
+ writeTrashFile("a/b", "Hello world b");
+ git.add().addFilepattern("a/b").call();
+ git.commit().setMessage("commit folder a").call();
+ git.rm().addFilepattern("a").call();
+ writeTrashFile("a", "New Hello world a");
+ git.add().addFilepattern(".").call();
+
+ String[] execute = execute("git checkout branch_1");
+ assertEquals(
+ "error: Your local changes to the following files would be overwritten by checkout:",
+ execute[0]);
+ assertEquals("\ta", execute[1]);
+ }
}
/**
@@ -164,40 +185,43 @@ public class CheckoutTest extends CLIRepositoryTestCase {
*/
@Test
public void testCheckoutWithMissingWorkingTreeFile() throws Exception {
- Git git = new Git(db);
- File fileA = writeTrashFile("a", "Hello world a");
- writeTrashFile("b", "Hello world b");
- git.add().addFilepattern(".").call();
- git.commit().setMessage("add files a & b").call();
- Ref branch_1 = git.branchCreate().setName("branch_1").call();
- writeTrashFile("a", "b");
- git.add().addFilepattern("a").call();
- git.commit().setMessage("modify file a").call();
-
- FileEntry entry = new FileTreeIterator.FileEntry(new File(
- db.getWorkTree(), "a"), db.getFS());
- assertEquals(FileMode.REGULAR_FILE, entry.getMode());
-
- FileUtils.delete(fileA);
-
- git.checkout().setName(branch_1.getName()).call();
-
- entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
- db.getFS());
- assertEquals(FileMode.REGULAR_FILE, entry.getMode());
- assertEquals("Hello world a", read(fileA));
+ try (Git git = new Git(db)) {
+ File fileA = writeTrashFile("a", "Hello world a");
+ writeTrashFile("b", "Hello world b");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("add files a & b").call();
+ Ref branch_1 = git.branchCreate().setName("branch_1").call();
+ writeTrashFile("a", "b");
+ git.add().addFilepattern("a").call();
+ git.commit().setMessage("modify file a").call();
+
+ FileEntry entry = new FileTreeIterator.FileEntry(new File(
+ db.getWorkTree(), "a"), db.getFS());
+ assertEquals(FileMode.REGULAR_FILE, entry.getMode());
+
+ FileUtils.delete(fileA);
+
+ git.checkout().setName(branch_1.getName()).call();
+
+ entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
+ db.getFS());
+ assertEquals(FileMode.REGULAR_FILE, entry.getMode());
+ assertEquals("Hello world a", read(fileA));
+ }
}
@Test
public void testCheckoutOrphan() throws Exception {
- Git git = new Git(db);
- git.commit().setMessage("initial commit").call();
-
- assertEquals("Switched to a new branch 'new_branch'",
- execute("git checkout --orphan new_branch"));
- assertEquals("refs/heads/new_branch", db.getRef("HEAD").getTarget().getName());
- RevCommit commit = git.commit().setMessage("orphan commit").call();
- assertEquals(0, commit.getParentCount());
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("initial commit").call();
+
+ assertStringArrayEquals("Switched to a new branch 'new_branch'",
+ execute("git checkout --orphan new_branch"));
+ assertEquals("refs/heads/new_branch",
+ db.exactRef("HEAD").getTarget().getName());
+ RevCommit commit = git.commit().setMessage("orphan commit").call();
+ assertEquals(0, commit.getParentCount());
+ }
}
/**
@@ -220,33 +244,34 @@ public class CheckoutTest extends CLIRepositoryTestCase {
@Test
public void fileModeTestMissingThenFolderWithFileInWorkingTree()
throws Exception {
- Git git = new Git(db);
- writeTrashFile("b", "Hello world b");
- git.add().addFilepattern(".").call();
- git.commit().setMessage("add file b").call();
- Ref branch_1 = git.branchCreate().setName("branch_1").call();
- File folderA = new File(db.getWorkTree(), "a");
- FileUtils.mkdirs(folderA);
- writeTrashFile("a/c", "Hello world c");
- git.add().addFilepattern(".").call();
- git.commit().setMessage("add folder a").call();
-
- FileEntry entry = new FileTreeIterator.FileEntry(new File(
- db.getWorkTree(), "a"), db.getFS());
- assertEquals(FileMode.TREE, entry.getMode());
-
- FileUtils.delete(folderA, FileUtils.RECURSIVE);
- writeTrashFile("a", "b");
-
- entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
- db.getFS());
- assertEquals(FileMode.REGULAR_FILE, entry.getMode());
-
- git.checkout().setName(branch_1.getName()).call();
-
- entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
- db.getFS());
- assertEquals(FileMode.REGULAR_FILE, entry.getMode());
+ try (Git git = new Git(db)) {
+ writeTrashFile("b", "Hello world b");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("add file b").call();
+ Ref branch_1 = git.branchCreate().setName("branch_1").call();
+ File folderA = new File(db.getWorkTree(), "a");
+ FileUtils.mkdirs(folderA);
+ writeTrashFile("a/c", "Hello world c");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("add folder a").call();
+
+ FileEntry entry = new FileTreeIterator.FileEntry(new File(
+ db.getWorkTree(), "a"), db.getFS());
+ assertEquals(FileMode.TREE, entry.getMode());
+
+ FileUtils.delete(folderA, FileUtils.RECURSIVE);
+ writeTrashFile("a", "b");
+
+ entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
+ db.getFS());
+ assertEquals(FileMode.REGULAR_FILE, entry.getMode());
+
+ git.checkout().setName(branch_1.getName()).call();
+
+ entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
+ db.getFS());
+ assertEquals(FileMode.REGULAR_FILE, entry.getMode());
+ }
}
/**
@@ -268,30 +293,31 @@ public class CheckoutTest extends CLIRepositoryTestCase {
*/
@Test
public void fileModeTestFolderWithMissingInWorkingTree() throws Exception {
- Git git = new Git(db);
- writeTrashFile("b", "Hello world b");
- writeTrashFile("a", "b");
- git.add().addFilepattern(".").call();
- git.commit().setMessage("add file b & file a").call();
- Ref branch_1 = git.branchCreate().setName("branch_1").call();
- git.rm().addFilepattern("a").call();
- File folderA = new File(db.getWorkTree(), "a");
- FileUtils.mkdirs(folderA);
- writeTrashFile("a/c", "Hello world c");
- git.add().addFilepattern(".").call();
- git.commit().setMessage("add folder a").call();
-
- FileEntry entry = new FileTreeIterator.FileEntry(new File(
- db.getWorkTree(), "a"), db.getFS());
- assertEquals(FileMode.TREE, entry.getMode());
-
- FileUtils.delete(folderA, FileUtils.RECURSIVE);
-
- git.checkout().setName(branch_1.getName()).call();
-
- entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
- db.getFS());
- assertEquals(FileMode.REGULAR_FILE, entry.getMode());
+ try (Git git = new Git(db)) {
+ writeTrashFile("b", "Hello world b");
+ writeTrashFile("a", "b");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("add file b & file a").call();
+ Ref branch_1 = git.branchCreate().setName("branch_1").call();
+ git.rm().addFilepattern("a").call();
+ File folderA = new File(db.getWorkTree(), "a");
+ FileUtils.mkdirs(folderA);
+ writeTrashFile("a/c", "Hello world c");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("add folder a").call();
+
+ FileEntry entry = new FileTreeIterator.FileEntry(new File(
+ db.getWorkTree(), "a"), db.getFS());
+ assertEquals(FileMode.TREE, entry.getMode());
+
+ FileUtils.delete(folderA, FileUtils.RECURSIVE);
+
+ git.checkout().setName(branch_1.getName()).call();
+
+ entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
+ db.getFS());
+ assertEquals(FileMode.REGULAR_FILE, entry.getMode());
+ }
}
/**
@@ -313,32 +339,33 @@ public class CheckoutTest extends CLIRepositoryTestCase {
*/
@Test
public void fileModeTestMissingWithFolderInWorkingTree() throws Exception {
- Git git = new Git(db);
- writeTrashFile("b", "Hello world b");
- writeTrashFile("a", "b");
- git.add().addFilepattern(".").call();
- git.commit().setMessage("add file b & file a").call();
- Ref branch_1 = git.branchCreate().setName("branch_1").call();
- git.rm().addFilepattern("a").call();
- git.commit().setMessage("delete file a").call();
-
- FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
- writeTrashFile("a/c", "Hello world c");
-
- FileEntry entry = new FileTreeIterator.FileEntry(new File(
- db.getWorkTree(), "a"), db.getFS());
- assertEquals(FileMode.TREE, entry.getMode());
-
- CheckoutConflictException exception = null;
- try {
- git.checkout().setName(branch_1.getName()).call();
- } catch (CheckoutConflictException e) {
- exception = e;
+ try (Git git = new Git(db)) {
+ writeTrashFile("b", "Hello world b");
+ writeTrashFile("a", "b");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("add file b & file a").call();
+ Ref branch_1 = git.branchCreate().setName("branch_1").call();
+ git.rm().addFilepattern("a").call();
+ git.commit().setMessage("delete file a").call();
+
+ FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
+ writeTrashFile("a/c", "Hello world c");
+
+ FileEntry entry = new FileTreeIterator.FileEntry(new File(
+ db.getWorkTree(), "a"), db.getFS());
+ assertEquals(FileMode.TREE, entry.getMode());
+
+ CheckoutConflictException exception = null;
+ try {
+ git.checkout().setName(branch_1.getName()).call();
+ } catch (CheckoutConflictException e) {
+ exception = e;
+ }
+ assertNotNull(exception);
+ assertEquals(2, exception.getConflictingPaths().size());
+ assertEquals("a", exception.getConflictingPaths().get(0));
+ assertEquals("a/c", exception.getConflictingPaths().get(1));
}
- assertNotNull(exception);
- assertEquals(2, exception.getConflictingPaths().size());
- assertEquals("a", exception.getConflictingPaths().get(0));
- assertEquals("a/c", exception.getConflictingPaths().get(1));
}
/**
@@ -360,40 +387,41 @@ public class CheckoutTest extends CLIRepositoryTestCase {
@Test
public void fileModeTestFolderThenMissingWithFileInWorkingTree()
throws Exception {
- Git git = new Git(db);
- FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
- writeTrashFile("a/c", "Hello world c");
- writeTrashFile("b", "Hello world b");
- git.add().addFilepattern(".").call();
- RevCommit commit1 = git.commit().setMessage("add folder a & file b")
- .call();
- Ref branch_1 = git.branchCreate().setName("branch_1").call();
- git.rm().addFilepattern("a").call();
- RevCommit commit2 = git.commit().setMessage("delete folder a").call();
-
- TreeWalk tw = new TreeWalk(db);
- tw.addTree(commit1.getTree());
- tw.addTree(commit2.getTree());
- List<DiffEntry> scan = DiffEntry.scan(tw);
- assertEquals(1, scan.size());
- assertEquals(FileMode.MISSING, scan.get(0).getNewMode());
- assertEquals(FileMode.TREE, scan.get(0).getOldMode());
-
- writeTrashFile("a", "b");
-
- FileEntry entry = new FileTreeIterator.FileEntry(new File(
- db.getWorkTree(), "a"), db.getFS());
- assertEquals(FileMode.REGULAR_FILE, entry.getMode());
-
- CheckoutConflictException exception = null;
- try {
- git.checkout().setName(branch_1.getName()).call();
- } catch (CheckoutConflictException e) {
- exception = e;
+ try (Git git = new Git(db)) {
+ FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
+ writeTrashFile("a/c", "Hello world c");
+ writeTrashFile("b", "Hello world b");
+ git.add().addFilepattern(".").call();
+ RevCommit commit1 = git.commit().setMessage("add folder a & file b")
+ .call();
+ Ref branch_1 = git.branchCreate().setName("branch_1").call();
+ git.rm().addFilepattern("a").call();
+ RevCommit commit2 = git.commit().setMessage("delete folder a").call();
+
+ TreeWalk tw = new TreeWalk(db);
+ tw.addTree(commit1.getTree());
+ tw.addTree(commit2.getTree());
+ List<DiffEntry> scan = DiffEntry.scan(tw);
+ assertEquals(1, scan.size());
+ assertEquals(FileMode.MISSING, scan.get(0).getNewMode());
+ assertEquals(FileMode.TREE, scan.get(0).getOldMode());
+
+ writeTrashFile("a", "b");
+
+ FileEntry entry = new FileTreeIterator.FileEntry(new File(
+ db.getWorkTree(), "a"), db.getFS());
+ assertEquals(FileMode.REGULAR_FILE, entry.getMode());
+
+ CheckoutConflictException exception = null;
+ try {
+ git.checkout().setName(branch_1.getName()).call();
+ } catch (CheckoutConflictException e) {
+ exception = e;
+ }
+ assertNotNull(exception);
+ assertEquals(1, exception.getConflictingPaths().size());
+ assertEquals("a", exception.getConflictingPaths().get(0));
}
- assertNotNull(exception);
- assertEquals(1, exception.getConflictingPaths().size());
- assertEquals("a", exception.getConflictingPaths().get(0));
}
/**
@@ -416,30 +444,31 @@ public class CheckoutTest extends CLIRepositoryTestCase {
@Test
public void fileModeTestFolderThenFileWithMissingInWorkingTree()
throws Exception {
- Git git = new Git(db);
- FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
- writeTrashFile("a/c", "Hello world c");
- writeTrashFile("b", "Hello world b");
- git.add().addFilepattern(".").call();
- git.commit().setMessage("add folder a & file b").call();
- Ref branch_1 = git.branchCreate().setName("branch_1").call();
- git.rm().addFilepattern("a").call();
- File fileA = new File(db.getWorkTree(), "a");
- writeTrashFile("a", "b");
- git.add().addFilepattern("a").call();
- git.commit().setMessage("add file a").call();
-
- FileEntry entry = new FileTreeIterator.FileEntry(new File(
- db.getWorkTree(), "a"), db.getFS());
- assertEquals(FileMode.REGULAR_FILE, entry.getMode());
-
- FileUtils.delete(fileA);
-
- git.checkout().setName(branch_1.getName()).call();
-
- entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
- db.getFS());
- assertEquals(FileMode.TREE, entry.getMode());
+ try (Git git = new Git(db)) {
+ FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
+ writeTrashFile("a/c", "Hello world c");
+ writeTrashFile("b", "Hello world b");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("add folder a & file b").call();
+ Ref branch_1 = git.branchCreate().setName("branch_1").call();
+ git.rm().addFilepattern("a").call();
+ File fileA = new File(db.getWorkTree(), "a");
+ writeTrashFile("a", "b");
+ git.add().addFilepattern("a").call();
+ git.commit().setMessage("add file a").call();
+
+ FileEntry entry = new FileTreeIterator.FileEntry(new File(
+ db.getWorkTree(), "a"), db.getFS());
+ assertEquals(FileMode.REGULAR_FILE, entry.getMode());
+
+ FileUtils.delete(fileA);
+
+ git.checkout().setName(branch_1.getName()).call();
+
+ entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
+ db.getFS());
+ assertEquals(FileMode.TREE, entry.getMode());
+ }
}
/**
@@ -460,38 +489,39 @@ public class CheckoutTest extends CLIRepositoryTestCase {
*/
@Test
public void fileModeTestFileThenFileWithFolderInIndex() throws Exception {
- Git git = new Git(db);
- writeTrashFile("a", "Hello world a");
- writeTrashFile("b", "Hello world b");
- git.add().addFilepattern(".").call();
- git.commit().setMessage("add files a & b").call();
- Ref branch_1 = git.branchCreate().setName("branch_1").call();
- writeTrashFile("a", "b");
- git.add().addFilepattern("a").call();
- git.commit().setMessage("add file a").call();
-
- FileEntry entry = new FileTreeIterator.FileEntry(new File(
- db.getWorkTree(), "a"), db.getFS());
- assertEquals(FileMode.REGULAR_FILE, entry.getMode());
-
- git.rm().addFilepattern("a").call();
- FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
- writeTrashFile("a/c", "Hello world c");
- git.add().addFilepattern(".").call();
-
- entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
- db.getFS());
- assertEquals(FileMode.TREE, entry.getMode());
-
- CheckoutConflictException exception = null;
- try {
- git.checkout().setName(branch_1.getName()).call();
- } catch (CheckoutConflictException e) {
- exception = e;
+ try (Git git = new Git(db)) {
+ writeTrashFile("a", "Hello world a");
+ writeTrashFile("b", "Hello world b");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("add files a & b").call();
+ Ref branch_1 = git.branchCreate().setName("branch_1").call();
+ writeTrashFile("a", "b");
+ git.add().addFilepattern("a").call();
+ git.commit().setMessage("add file a").call();
+
+ FileEntry entry = new FileTreeIterator.FileEntry(new File(
+ db.getWorkTree(), "a"), db.getFS());
+ assertEquals(FileMode.REGULAR_FILE, entry.getMode());
+
+ git.rm().addFilepattern("a").call();
+ FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
+ writeTrashFile("a/c", "Hello world c");
+ git.add().addFilepattern(".").call();
+
+ entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
+ db.getFS());
+ assertEquals(FileMode.TREE, entry.getMode());
+
+ CheckoutConflictException exception = null;
+ try {
+ git.checkout().setName(branch_1.getName()).call();
+ } catch (CheckoutConflictException e) {
+ exception = e;
+ }
+ assertNotNull(exception);
+ assertEquals(1, exception.getConflictingPaths().size());
+ assertEquals("a", exception.getConflictingPaths().get(0));
}
- assertNotNull(exception);
- assertEquals(1, exception.getConflictingPaths().size());
- assertEquals("a", exception.getConflictingPaths().get(0));
}
/**
@@ -513,78 +543,97 @@ public class CheckoutTest extends CLIRepositoryTestCase {
*/
@Test
public void fileModeTestFileWithFolderInIndex() throws Exception {
- Git git = new Git(db);
- writeTrashFile("b", "Hello world b");
- writeTrashFile("a", "b");
- git.add().addFilepattern(".").call();
- git.commit().setMessage("add file b & file a").call();
- Ref branch_1 = git.branchCreate().setName("branch_1").call();
- git.rm().addFilepattern("a").call();
- writeTrashFile("a", "Hello world a");
- git.add().addFilepattern("a").call();
- git.commit().setMessage("add file a").call();
-
- FileEntry entry = new FileTreeIterator.FileEntry(new File(
- db.getWorkTree(), "a"), db.getFS());
- assertEquals(FileMode.REGULAR_FILE, entry.getMode());
-
- git.rm().addFilepattern("a").call();
- FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
- writeTrashFile("a/c", "Hello world c");
- git.add().addFilepattern(".").call();
-
- entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
- db.getFS());
- assertEquals(FileMode.TREE, entry.getMode());
-
- CheckoutConflictException exception = null;
- try {
- git.checkout().setName(branch_1.getName()).call();
- } catch (CheckoutConflictException e) {
- exception = e;
+ try (Git git = new Git(db)) {
+ writeTrashFile("b", "Hello world b");
+ writeTrashFile("a", "b");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("add file b & file a").call();
+ Ref branch_1 = git.branchCreate().setName("branch_1").call();
+ git.rm().addFilepattern("a").call();
+ writeTrashFile("a", "Hello world a");
+ git.add().addFilepattern("a").call();
+ git.commit().setMessage("add file a").call();
+
+ FileEntry entry = new FileTreeIterator.FileEntry(new File(
+ db.getWorkTree(), "a"), db.getFS());
+ assertEquals(FileMode.REGULAR_FILE, entry.getMode());
+
+ git.rm().addFilepattern("a").call();
+ FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
+ writeTrashFile("a/c", "Hello world c");
+ git.add().addFilepattern(".").call();
+
+ entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
+ db.getFS());
+ assertEquals(FileMode.TREE, entry.getMode());
+
+ CheckoutConflictException exception = null;
+ try {
+ git.checkout().setName(branch_1.getName()).call();
+ } catch (CheckoutConflictException e) {
+ exception = e;
+ }
+ assertNotNull(exception);
+ assertEquals(1, exception.getConflictingPaths().size());
+ assertEquals("a", exception.getConflictingPaths().get(0));
+
+ // TODO: ideally we'd like to get two paths from this exception
+ // assertEquals(2, exception.getConflictingPaths().size());
+ // assertEquals("a", exception.getConflictingPaths().get(0));
+ // assertEquals("a/c", exception.getConflictingPaths().get(1));
}
- assertNotNull(exception);
- assertEquals(1, exception.getConflictingPaths().size());
- assertEquals("a", exception.getConflictingPaths().get(0));
-
- // TODO: ideally we'd like to get two paths from this exception
- // assertEquals(2, exception.getConflictingPaths().size());
- // assertEquals("a", exception.getConflictingPaths().get(0));
- // assertEquals("a/c", exception.getConflictingPaths().get(1));
}
- static private void assertEquals(Object expected, Object actual) {
- Assert.assertEquals(expected, actual);
+ @Test
+ public void testCheckoutPath() throws Exception {
+ try (Git git = new Git(db)) {
+ writeTrashFile("a", "Hello world a");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("commit file a").call();
+ git.branchCreate().setName("branch_1").call();
+ git.checkout().setName("branch_1").call();
+ File b = writeTrashFile("b", "Hello world b");
+ git.add().addFilepattern("b").call();
+ git.commit().setMessage("commit file b").call();
+ File a = writeTrashFile("a", "New Hello world a");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("modified a").call();
+ assertArrayEquals(new String[] { "" },
+ execute("git checkout HEAD~2 -- a"));
+ assertEquals("Hello world a", read(a));
+ assertArrayEquals(new String[] { "* branch_1", " master", "" },
+ execute("git branch"));
+ assertEquals("Hello world b", read(b));
+ }
}
- static private void assertEquals(String expected, String[] actual) {
- // if there is more than one line, ignore last one if empty
- Assert.assertEquals(
- 1,
- actual.length > 1 && actual[actual.length - 1].equals("") ? actual.length - 1
- : actual.length);
- Assert.assertEquals(expected, actual[0]);
+ @Test
+ public void testCheckouSingleFile() throws Exception {
+ try (Git git = new Git(db)) {
+ File a = writeTrashFile("a", "file a");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("commit file a").call();
+ writeTrashFile("a", "b");
+ assertEquals("b", read(a));
+ assertEquals("[]", Arrays.toString(execute("git checkout -- a")));
+ assertEquals("file a", read(a));
+ }
}
@Test
- public void testCheckoutPath() throws Exception {
- Git git = new Git(db);
- writeTrashFile("a", "Hello world a");
- git.add().addFilepattern(".").call();
- git.commit().setMessage("commit file a").call();
- git.branchCreate().setName("branch_1").call();
- git.checkout().setName("branch_1").call();
- File b = writeTrashFile("b", "Hello world b");
- git.add().addFilepattern("b").call();
- git.commit().setMessage("commit file b").call();
- File a = writeTrashFile("a", "New Hello world a");
- git.add().addFilepattern(".").call();
- git.commit().setMessage("modified a").call();
- assertArrayEquals(new String[] { "" },
- execute("git checkout HEAD~2 -- a"));
- assertEquals("Hello world a", read(a));
- assertArrayEquals(new String[] { "* branch_1", " master", "" },
- execute("git branch"));
- assertEquals("Hello world b", read(b));
+ public void testCheckoutLink() throws Exception {
+ Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
+ try (Git git = new Git(db)) {
+ Path path = writeLink("a", "link_a");
+ assertTrue(Files.isSymbolicLink(path));
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("commit link a").call();
+ deleteTrashFile("a");
+ writeTrashFile("a", "Hello world a");
+ assertFalse(Files.isSymbolicLink(path));
+ assertEquals("[]", Arrays.toString(execute("git checkout -- a")));
+ assertEquals("link_a", FileUtils.readSymLink(path.toFile()));
+ assertTrue(Files.isSymbolicLink(path));
+ }
}
}
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/BranchTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CommitTest.java
similarity index 56%
copy from org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/BranchTest.java
copy to org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CommitTest.java
index 4200cd0..6bccb6d 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/BranchTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CommitTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012, 2014 IBM Corporation and others.
+ * Copyright (C) 2015, Andrey Loskutov <loskutov at gmx.de>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -44,51 +44,57 @@ package org.eclipse.jgit.pgm;
import static org.junit.Assert.assertEquals;
-import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.CLIRepositoryTestCase;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.junit.Before;
import org.junit.Test;
-public class BranchTest extends CLIRepositoryTestCase {
- @Override
- @Before
- public void setUp() throws Exception {
- super.setUp();
- new Git(db).commit().setMessage("initial commit").call();
- }
+public class CommitTest extends CLIRepositoryTestCase {
@Test
- public void testList() throws Exception {
- assertEquals("* master 6fd41be initial commit",
- execute("git branch -v")[0]);
- }
+ public void testCommitPath() throws Exception {
+ writeTrashFile("a", "a");
+ writeTrashFile("b", "a");
+ String result = toString(execute("git add a"));
+ assertEquals("", result);
- @Test
- public void testListDetached() throws Exception {
- RefUpdate updateRef = db.updateRef(Constants.HEAD, true);
- updateRef.setNewObjectId(db.resolve("6fd41be"));
- updateRef.update();
- assertEquals("* (no branch) 6fd41be initial commit",
- execute("git branch -v")[0]);
- }
+ result = toString(execute("git status -- a"));
+ assertEquals(toString("On branch master", "Changes to be committed:",
+ "new file: a"), result);
- @Test
- public void testListContains() throws Exception {
- new Git(db).branchCreate().setName("initial").call();
- RevCommit second = new Git(db).commit().setMessage("second commit")
- .call();
- assertArrayOfLinesEquals(new String[] { " initial", "* master", "" },
- execute("git branch --contains 6fd41be"));
- assertArrayOfLinesEquals(new String[] { "* master", "" },
- execute("git branch --contains " + second.name()));
+ result = toString(execute("git status -- b"));
+ assertEquals(toString("On branch master", "Untracked files:", "b"),
+ result);
+
+ result = toString(execute("git commit a -m 'added a'"));
+ assertEquals(
+ "[master 8cb3ef7e5171aaee1792df6302a5a0cd30425f7a] added a",
+ result);
+
+ result = toString(execute("git status -- a"));
+ assertEquals("On branch master", result);
+
+ result = toString(execute("git status -- b"));
+ assertEquals(toString("On branch master", "Untracked files:", "b"),
+ result);
}
@Test
- public void testExistingBranch() throws Exception {
- assertEquals("fatal: A branch named 'master' already exists.",
- execute("git branch master")[0]);
+ public void testCommitAll() throws Exception {
+ writeTrashFile("a", "a");
+ writeTrashFile("b", "a");
+ String result = toString(execute("git add a b"));
+ assertEquals("", result);
+
+ result = toString(execute("git status -- a b"));
+ assertEquals(toString("On branch master", "Changes to be committed:",
+ "new file: a", "new file: b"), result);
+
+ result = toString(execute("git commit -m 'added a b'"));
+ assertEquals(
+ "[master 3c93fa8e3a28ee26690498be78016edcb3a38c73] added a b",
+ result);
+
+ result = toString(execute("git status -- a b"));
+ assertEquals("On branch master", result);
}
+
}
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ConfigTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ConfigTest.java
index aefdff1..23aa97e 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ConfigTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ConfigTest.java
@@ -60,7 +60,9 @@ public class ConfigTest extends CLIRepositoryTestCase {
@Before
public void setUp() throws Exception {
super.setUp();
- new Git(db).commit().setMessage("initial commit").call();
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("initial commit").call();
+ }
}
@Test
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DescribeTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DescribeTest.java
index 4ab849b..086e72e 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DescribeTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/DescribeTest.java
@@ -43,9 +43,15 @@
package org.eclipse.jgit.pgm;
import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.CLIRepositoryTestCase;
+import org.eclipse.jgit.pgm.internal.CLIText;
import org.junit.Before;
import org.junit.Test;
@@ -67,17 +73,15 @@ public class DescribeTest extends CLIRepositoryTestCase {
@Test
public void testNoHead() throws Exception {
- assertArrayEquals(
- new String[] { "fatal: No names found, cannot describe anything." },
- execute("git describe"));
+ assertEquals(CLIText.fatalError(CLIText.get().noNamesFound),
+ toString(executeUnchecked("git describe")));
}
@Test
public void testHeadNoTag() throws Exception {
git.commit().setMessage("initial commit").call();
- assertArrayEquals(
- new String[] { "fatal: No names found, cannot describe anything." },
- execute("git describe"));
+ assertEquals(CLIText.fatalError(CLIText.get().noNamesFound),
+ toString(executeUnchecked("git describe")));
}
@Test
@@ -96,4 +100,29 @@ public class DescribeTest extends CLIRepositoryTestCase {
assertArrayEquals(new String[] { "v1.0-1-g56f6ceb", "" },
execute("git describe"));
}
+
+ @Test
+ public void testDescribeTagLong() throws Exception {
+ initialCommitAndTag();
+ assertArrayEquals(new String[] { "v1.0-0-g6fd41be", "" },
+ execute("git describe --long HEAD"));
+ }
+
+ @Test
+ public void testHelpArgumentBeforeUnknown() throws Exception {
+ String[] output = execute("git describe -h -XYZ");
+ String all = Arrays.toString(output);
+ assertTrue("Unexpected help output: " + all,
+ all.contains("jgit describe"));
+ assertFalse("Unexpected help output: " + all, all.contains("fatal"));
+ }
+
+ @Test
+ public void testHelpArgumentAfterUnknown() throws Exception {
+ String[] output = executeUnchecked("git describe -XYZ -h");
+ String all = Arrays.toString(output);
+ assertTrue("Unexpected help output: " + all,
+ all.contains("jgit describe"));
+ assertTrue("Unexpected help output: " + all, all.contains("fatal"));
+ }
}
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeTest.java
index 975e8c4..4719901 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/MergeTest.java
@@ -50,6 +50,7 @@ import java.util.Iterator;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.CLIRepositoryTestCase;
import org.eclipse.jgit.merge.MergeStrategy;
+import org.eclipse.jgit.pgm.internal.CLIText;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Before;
import org.junit.Test;
@@ -194,8 +195,9 @@ public class MergeTest extends CLIRepositoryTestCase {
@Test
public void testNoFastForwardAndSquash() throws Exception {
- assertEquals("fatal: You cannot combine --squash with --no-ff.",
- execute("git merge master --no-ff --squash")[0]);
+ assertEquals(
+ CLIText.fatalError(CLIText.get().cannotCombineSquashWithNoff),
+ executeUnchecked("git merge master --no-ff --squash")[0]);
}
@Test
@@ -209,8 +211,8 @@ public class MergeTest extends CLIRepositoryTestCase {
git.add().addFilepattern("file").call();
git.commit().setMessage("commit#2").call();
- assertEquals("fatal: Not possible to fast-forward, aborting.",
- execute("git merge master --ff-only")[0]);
+ assertEquals(CLIText.fatalError(CLIText.get().ffNotPossibleAborting),
+ executeUnchecked("git merge master --ff-only")[0]);
}
@Test
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/RemoteTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/RemoteTest.java
new file mode 100644
index 0000000..58e0e19
--- /dev/null
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/RemoteTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r at zend.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.pgm;
+
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.lib.CLIRepositoryTestCase;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.URIish;
+import org.junit.Before;
+import org.junit.Test;
+
+public class RemoteTest extends CLIRepositoryTestCase {
+
+ private StoredConfig config;
+
+ private RemoteConfig remote;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ // create another repository
+ Repository remoteRepository = createWorkRepository();
+
+ // set it up as a remote to this repository
+ config = db.getConfig();
+ remote = new RemoteConfig(config, "test");
+ remote.addFetchRefSpec(
+ new RefSpec("+refs/heads/*:refs/remotes/test/*"));
+ URIish uri = new URIish(
+ remoteRepository.getDirectory().toURI().toURL());
+ remote.addURI(uri);
+ remote.update(config);
+ config.save();
+
+ Git.wrap(remoteRepository).commit().setMessage("initial commit").call();
+ }
+
+ @Test
+ public void testList() throws Exception {
+ assertArrayEquals(new String[] { remote.getName(), "" },
+ execute("git remote"));
+ }
+
+ @Test
+ public void testVerboseList() throws Exception {
+ assertArrayEquals(
+ new String[] {
+ String.format("%s\t%s (fetch)", remote.getName(),
+ remote.getURIs().get(0)),
+ String.format("%s\t%s (push)", remote.getName(),
+ remote.getURIs().get(0)),
+ "" },
+ execute("git remote -v"));
+ }
+
+ @Test
+ public void testAdd() throws Exception {
+ assertArrayEquals(new String[] { "" },
+ execute("git remote add second git://test.com/second"));
+
+ List<RemoteConfig> remotes = RemoteConfig.getAllRemoteConfigs(config);
+ assertEquals(2, remotes.size());
+ assertEquals("second", remotes.get(0).getName());
+ assertEquals("test", remotes.get(1).getName());
+ }
+
+ @Test
+ public void testRemove() throws Exception {
+ assertArrayEquals(new String[] { "" },
+ execute("git remote remove test"));
+
+ assertTrue(RemoteConfig.getAllRemoteConfigs(config).isEmpty());
+ }
+
+ @Test
+ public void testSetUrl() throws Exception {
+ assertArrayEquals(new String[] { "" },
+ execute("git remote set-url test git://test.com/test"));
+
+ RemoteConfig result = new RemoteConfig(config, "test");
+ assertEquals("test", result.getName());
+ assertArrayEquals(new URIish[] { new URIish("git://test.com/test") },
+ result.getURIs().toArray());
+ assertTrue(result.getPushURIs().isEmpty());
+ }
+
+ @Test
+ public void testSetUrlPush() throws Exception {
+ assertArrayEquals(new String[] { "" },
+ execute("git remote set-url --push test git://test.com/test"));
+
+ RemoteConfig result = new RemoteConfig(config, "test");
+ assertEquals("test", result.getName());
+ assertEquals(remote.getURIs(), result.getURIs());
+ assertArrayEquals(new URIish[] { new URIish("git://test.com/test") },
+ result.getPushURIs().toArray());
+ }
+
+ @Test
+ public void testUpdate() throws Exception {
+ assertArrayEquals(new String[] {
+ "From " + remote.getURIs().get(0).toString(),
+ " * [new branch] master -> test/master", "", "" },
+ execute("git remote update test"));
+ }
+
+}
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/RepoTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/RepoTest.java
index 90efae2..0eee771 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/RepoTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/RepoTest.java
@@ -44,8 +44,11 @@ package org.eclipse.jgit.pgm;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.io.File;
+import java.util.Arrays;
+
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.lib.CLIRepositoryTestCase;
@@ -98,6 +101,31 @@ public class RepoTest extends CLIRepositoryTestCase {
}
@Test
+ public void testMissingPath() throws Exception {
+ try {
+ execute("git repo");
+ fail("Must die");
+ } catch (Die e) {
+ // expected, requires argument
+ }
+ }
+
+ /**
+ * See bug 484951: "git repo -h" should not print unexpected values
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testZombieHelpArgument() throws Exception {
+ String[] output = execute("git repo -h");
+ String all = Arrays.toString(output);
+ assertTrue("Unexpected help output: " + all,
+ all.contains("jgit repo"));
+ assertFalse("Unexpected help output: " + all,
+ all.contains("jgit repo VAL"));
+ }
+
+ @Test
public void testAddRepoManifest() throws Exception {
StringBuilder xmlContent = new StringBuilder();
xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ResetTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ResetTest.java
new file mode 100644
index 0000000..16c5889
--- /dev/null
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ResetTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r at zend.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.pgm;
+
+import static org.junit.Assert.*;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.lib.CLIRepositoryTestCase;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class ResetTest extends CLIRepositoryTestCase {
+
+ private Git git;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ git = new Git(db);
+ }
+
+ @Test
+ public void testPathOptionHelp() throws Exception {
+ String[] result = execute("git reset -h");
+ assertTrue("Unexpected argument: " + result[1],
+ result[1].endsWith("[-- path ... ...]"));
+ }
+
+ @Test
+ public void testZombieArgument_Bug484951() throws Exception {
+ String[] result = execute("git reset -h");
+ assertFalse("Unexpected argument: " + result[0],
+ result[0].contains("[VAL ...]"));
+ }
+
+ @Test
+ public void testResetSelf() throws Exception {
+ RevCommit commit = git.commit().setMessage("initial commit").call();
+ assertStringArrayEquals("",
+ execute("git reset --hard " + commit.getId().name()));
+ assertEquals(commit.getId(),
+ git.getRepository().exactRef("HEAD").getObjectId());
+ }
+
+ @Test
+ public void testResetPrevious() throws Exception {
+ RevCommit commit = git.commit().setMessage("initial commit").call();
+ git.commit().setMessage("second commit").call();
+ assertStringArrayEquals("",
+ execute("git reset --hard " + commit.getId().name()));
+ assertEquals(commit.getId(),
+ git.getRepository().exactRef("HEAD").getObjectId());
+ }
+
+ @Test
+ public void testResetEmptyPath() throws Exception {
+ RevCommit commit = git.commit().setMessage("initial commit").call();
+ assertStringArrayEquals("",
+ execute("git reset --hard " + commit.getId().name() + " --"));
+ assertEquals(commit.getId(),
+ git.getRepository().exactRef("HEAD").getObjectId());
+ }
+
+ @Test
+ public void testResetPathDoubleDash() throws Exception {
+ resetPath(true, true);
+ }
+
+ @Test
+ public void testResetPathNoDoubleDash() throws Exception {
+ resetPath(false, true);
+ }
+
+ @Test
+ public void testResetPathDoubleDashNoRef() throws Exception {
+ resetPath(true, false);
+ }
+
+ @Ignore("Currently we cannote recognize if a name is a commit-ish or a path, "
+ + "so 'git reset a' will not work if 'a' is not a branch name but a file path")
+ @Test
+ public void testResetPathNoDoubleDashNoRef() throws Exception {
+ resetPath(false, false);
+ }
+
+ private void resetPath(boolean useDoubleDash, boolean supplyCommit)
+ throws Exception {
+ // create files a and b
+ writeTrashFile("a", "Hello world a");
+ writeTrashFile("b", "Hello world b");
+ // stage the files
+ git.add().addFilepattern(".").call();
+ // commit the files
+ RevCommit commit = git.commit().setMessage("files a & b").call();
+
+ // change both files a and b
+ writeTrashFile("a", "New Hello world a");
+ writeTrashFile("b", "New Hello world b");
+ // stage the files
+ git.add().addFilepattern(".").call();
+
+ // reset only file a
+ String cmd = String.format("git reset %s%s a",
+ supplyCommit ? commit.getId().name() : "",
+ useDoubleDash ? " --" : "");
+ assertStringArrayEquals("", execute(cmd));
+ assertEquals(commit.getId(),
+ git.getRepository().exactRef("HEAD").getObjectId());
+
+ org.eclipse.jgit.api.Status status = git.status().call();
+ // assert that file a is unstaged
+ assertArrayEquals(new String[] { "a" },
+ status.getModified().toArray());
+ // assert that file b is still staged
+ assertArrayEquals(new String[] { "b" },
+ status.getChanged().toArray());
+ }
+
+}
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/StatusTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/StatusTest.java
index 7ffcae5..368047c 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/StatusTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/StatusTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012, 2013 François Rey <eclipse.org_ at _francois_._rey_._name>
+ * Copyright (C) 2012, 2015 François Rey <eclipse.org_ at _francois_._rey_._name>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -42,18 +42,154 @@
*/
package org.eclipse.jgit.pgm;
+import static org.eclipse.jgit.lib.Constants.MASTER;
+import static org.eclipse.jgit.lib.Constants.R_HEADS;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.CLIRepositoryTestCase;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Test;
public class StatusTest extends CLIRepositoryTestCase {
+
+ @Test
+ public void testPathOptionHelp() throws Exception {
+ String[] result = execute("git status -h");
+ assertTrue("Unexpected argument: " + result[1],
+ result[1].endsWith("[-- path ... ...]"));
+ }
+
+ @Test
+ public void testStatusDefault() throws Exception {
+ executeTest("git status", false, true);
+ }
+
+ @Test
+ public void testStatusU() throws Exception {
+ executeTest("git status -u", false, true);
+ }
+
+ @Test
+ public void testStatusUno() throws Exception {
+ executeTest("git status -uno", false, false);
+ }
+
+ @Test
+ public void testStatusUall() throws Exception {
+ executeTest("git status -uall", false, true);
+ }
+
+ @Test
+ public void testStatusUntrackedFiles() throws Exception {
+ executeTest("git status --untracked-files", false, true);
+ }
+
+ @Test
+ public void testStatusUntrackedFilesNo() throws Exception {
+ executeTest("git status --untracked-files=no", false, false);
+ }
+
+ @Test
+ public void testStatusUntrackedFilesAll() throws Exception {
+ executeTest("git status --untracked-files=all", false, true);
+ }
+
+ @Test
+ public void testStatusPorcelain() throws Exception {
+ executeTest("git status --porcelain", true, true);
+ }
+
+ @Test
+ public void testStatusPorcelainU() throws Exception {
+ executeTest("git status --porcelain -u", true, true);
+ }
+
+ @Test
+ public void testStatusPorcelainUno() throws Exception {
+ executeTest("git status --porcelain -uno", true, false);
+ }
+
+ @Test
+ public void testStatusPorcelainUall() throws Exception {
+ executeTest("git status --porcelain -uall", true, true);
+ }
+
+ @Test
+ public void testStatusPorcelainUntrackedFiles() throws Exception {
+ executeTest("git status --porcelain --untracked-files", true, true);
+ }
+
+ @Test
+ public void testStatusPorcelainUntrackedFilesNo() throws Exception {
+ executeTest("git status --porcelain --untracked-files=no", true, false);
+ }
+
@Test
- public void testStatus() throws Exception {
+ public void testStatusPorcelainUntrackedFilesAll() throws Exception {
+ executeTest("git status --porcelain --untracked-files=all", true, true);
+ }
+
+ /**
+ * Executes the test sequence.
+ *
+ * @param command
+ * full git command and parameters to be used
+ * @param porcelain
+ * indicates that porcelain format is expected in the output
+ * @param untrackedFiles
+ * indicates that untracked files are expected in the output
+ *
+ * @throws Exception
+ * if error during test execution
+ */
+ private void executeTest(String command, boolean porcelain,
+ boolean untrackedFiles) throws Exception {
Git git = new Git(db);
// Write all files
+ writeAllFiles();
+ // Test untracked
+ assertUntrackedFiles(command, porcelain, untrackedFiles);
+ // Add to index
+ addFilesToIndex(git);
+ // Test staged count
+ assertStagedFiles(command, porcelain, untrackedFiles);
+ // Commit
+ makeInitialCommit(git);
+ assertAfterInitialCommit(command, porcelain, untrackedFiles);
+ // Make some changes and stage them
+ makeSomeChangesAndStageThem(git);
+ // Test staged/not-staged status
+ assertStagedStatus(command, porcelain, untrackedFiles);
+ // Create unmerged file
+ createUnmergedFile(git);
+ // Commit pending changes
+ commitPendingChanges(git);
+ assertUntracked(command, porcelain, untrackedFiles, "master");
+ // Checkout new branch
+ checkoutTestBranch(git);
+ // Test branch status
+ assertUntracked(command, porcelain, untrackedFiles, "test");
+ // Commit change and checkout master again
+ RevCommit testBranch = commitChangesInTestBranch(git);
+ assertUntracked(command, porcelain, untrackedFiles, "test");
+ checkoutMasterBranch(git);
+ // Change the same file and commit
+ changeUnmergedFileAndCommit(git);
+ assertUntracked(command, porcelain, untrackedFiles, "master");
+ // Merge test branch into master
+ mergeTestBranchInMaster(git, testBranch);
+ // Test unmerged status
+ assertUntrackedAndUnmerged(command, porcelain, untrackedFiles, "master");
+ // Test detached head
+ detachHead(git);
+ assertUntrackedAndUnmerged(command, porcelain, untrackedFiles, null);
+ }
+
+ private void writeAllFiles() throws IOException {
writeTrashFile("tracked", "tracked");
writeTrashFile("stagedNew", "stagedNew");
writeTrashFile("stagedModified", "stagedModified");
@@ -61,55 +197,22 @@ public class StatusTest extends CLIRepositoryTestCase {
writeTrashFile("trackedModified", "trackedModified");
writeTrashFile("trackedDeleted", "trackedDeleted");
writeTrashFile("untracked", "untracked");
- // Test untracked
- assertArrayOfLinesEquals(new String[] { // git status output
- "On branch master", //
- "Untracked files:", //
- "",//
- "\tstagedDeleted", //
- "\tstagedModified", //
- "\tstagedNew", //
- "\ttracked", //
- "\ttrackedDeleted", //
- "\ttrackedModified", //
- "\tuntracked", //
- "" //
- }, execute("git status")); //
- // Add to index
+ }
+
+ private void addFilesToIndex(Git git) throws GitAPIException {
git.add().addFilepattern("tracked").call();
git.add().addFilepattern("stagedModified").call();
git.add().addFilepattern("stagedDeleted").call();
git.add().addFilepattern("trackedModified").call();
git.add().addFilepattern("trackedDeleted").call();
- // Test staged count
- assertArrayOfLinesEquals(new String[] { // git status output
- "On branch master", //
- "Changes to be committed:", //
- "", //
- "\tnew file: stagedDeleted", //
- "\tnew file: stagedModified", //
- "\tnew file: tracked", //
- "\tnew file: trackedDeleted", //
- "\tnew file: trackedModified", //
- "", //
- "Untracked files:", //
- "", //
- "\tstagedNew", //
- "\tuntracked", //
- "" //
- }, execute("git status")); //
- // Commit
- git.commit().setMessage("initial commit")
- .call();
- assertArrayOfLinesEquals(new String[] { // git status output
- "On branch master", //
- "Untracked files:", //
- "", //
- "\tstagedNew", //
- "\tuntracked", //
- "" //
- }, execute("git status")); //
- // Make some changes and stage them
+ }
+
+ private void makeInitialCommit(Git git) throws GitAPIException {
+ git.commit().setMessage("initial commit").call();
+ }
+
+ private void makeSomeChangesAndStageThem(Git git) throws IOException,
+ GitAPIException {
writeTrashFile("stagedModified", "stagedModified modified");
deleteTrashFile("stagedDeleted");
writeTrashFile("trackedModified", "trackedModified modified");
@@ -117,116 +220,60 @@ public class StatusTest extends CLIRepositoryTestCase {
git.add().addFilepattern("stagedModified").call();
git.rm().addFilepattern("stagedDeleted").call();
git.add().addFilepattern("stagedNew").call();
- // Test staged/not-staged status
- assertArrayOfLinesEquals(new String[] { // git status output
- "On branch master", //
- "Changes to be committed:", //
- "", //
- "\tdeleted: stagedDeleted", //
- "\tmodified: stagedModified", //
- "\tnew file: stagedNew", //
- "", //
- "Changes not staged for commit:", //
- "", //
- "\tdeleted: trackedDeleted", //
- "\tmodified: trackedModified", //
- "", //
- "Untracked files:", //
- "", //
- "\tuntracked", //
- "" //
- }, execute("git status")); //
- // Create unmerged file
+ }
+
+ private void createUnmergedFile(Git git) throws IOException,
+ GitAPIException {
writeTrashFile("unmerged", "unmerged");
git.add().addFilepattern("unmerged").call();
- // Commit pending changes
+ }
+
+ private void commitPendingChanges(Git git) throws GitAPIException {
git.add().addFilepattern("trackedModified").call();
git.rm().addFilepattern("trackedDeleted").call();
git.commit().setMessage("commit before branching").call();
- assertArrayOfLinesEquals(new String[] { // git status output
- "On branch master", //
- "Untracked files:", //
- "", //
- "\tuntracked", //
- "" //
- }, execute("git status")); //
- // Checkout new branch
+ }
+
+ private void checkoutTestBranch(Git git) throws GitAPIException {
git.checkout().setCreateBranch(true).setName("test").call();
- // Test branch status
- assertArrayOfLinesEquals(new String[] { // git status output
- "On branch test", //
- "Untracked files:", //
- "", //
- "\tuntracked", //
- "" //
- }, execute("git status")); //
- // Commit change and checkout master again
+ }
+
+ private RevCommit commitChangesInTestBranch(Git git) throws IOException,
+ GitAPIException {
writeTrashFile("unmerged", "changed in test branch");
git.add().addFilepattern("unmerged").call();
- RevCommit testBranch = git.commit()
+ return git.commit()
.setMessage("changed unmerged in test branch").call();
- assertArrayOfLinesEquals(new String[] { // git status output
- "On branch test", //
- "Untracked files:", //
- "", //
- "\tuntracked", //
- "" //
- }, execute("git status")); //
+ }
+
+ private void checkoutMasterBranch(Git git) throws GitAPIException {
git.checkout().setName("master").call();
- // Change the same file and commit
+ }
+
+ private void changeUnmergedFileAndCommit(Git git) throws IOException,
+ GitAPIException {
writeTrashFile("unmerged", "changed in master branch");
git.add().addFilepattern("unmerged").call();
git.commit().setMessage("changed unmerged in master branch").call();
- assertArrayOfLinesEquals(new String[] { // git status output
- "On branch master", //
- "Untracked files:", //
- "", //
- "\tuntracked", //
- "" //
- }, execute("git status")); //
- // Merge test branch into master
- git.merge().include(testBranch.getId()).call();
- // Test unmerged status
- assertArrayOfLinesEquals(new String[] { // git status output
- "On branch master", //
- "Unmerged paths:", //
- "", //
- "\tboth modified: unmerged", //
- "", //
- "Untracked files:", //
- "", //
- "\tuntracked", //
- "" //
- }, execute("git status")); //
- // Test detached head
- String commitId = db.getRef(Constants.MASTER).getObjectId().name();
+ }
+
+ private void mergeTestBranchInMaster(Git git, RevCommit aCommit)
+ throws GitAPIException {
+ git.merge().include(aCommit.getId()).call();
+ }
+
+ private void detachHead(Git git) throws IOException, GitAPIException {
+ String commitId = db.exactRef(R_HEADS + MASTER).getObjectId().name();
git.checkout().setName(commitId).call();
- assertArrayOfLinesEquals(new String[] { // git status output
- "Not currently on any branch.", //
- "Unmerged paths:", //
- "", //
- "\tboth modified: unmerged", //
- "", //
- "Untracked files:", //
- "", //
- "\tuntracked", //
- "" //
- }, execute("git status")); //
}
- @Test
- public void testStatusPorcelain() throws Exception {
- Git git = new Git(db);
- // Write all files
- writeTrashFile("tracked", "tracked");
- writeTrashFile("stagedNew", "stagedNew");
- writeTrashFile("stagedModified", "stagedModified");
- writeTrashFile("stagedDeleted", "stagedDeleted");
- writeTrashFile("trackedModified", "trackedModified");
- writeTrashFile("trackedDeleted", "trackedDeleted");
- writeTrashFile("untracked", "untracked");
- // Test untracked
- assertArrayOfLinesEquals(new String[] { // git status output
+ private void assertUntrackedFiles(String command, boolean porcelain,
+ boolean untrackedFiles) throws Exception {
+ String[] output = new String[0];
+
+ if (porcelain) {
+ if (untrackedFiles) {
+ output = new String[] { //
"?? stagedDeleted", //
"?? stagedModified", //
"?? stagedNew", //
@@ -235,15 +282,45 @@ public class StatusTest extends CLIRepositoryTestCase {
"?? trackedModified", //
"?? untracked", //
"" //
- }, execute("git status --porcelain")); //
- // Add to index
- git.add().addFilepattern("tracked").call();
- git.add().addFilepattern("stagedModified").call();
- git.add().addFilepattern("stagedDeleted").call();
- git.add().addFilepattern("trackedModified").call();
- git.add().addFilepattern("trackedDeleted").call();
- // Test staged count
- assertArrayOfLinesEquals(new String[] { // git status output
+ };
+ } else {
+ output = new String[] { //
+ "" //
+ };
+ }
+ } else {
+ if (untrackedFiles) {
+ output = new String[] { //
+ "On branch master", //
+ "Untracked files:", //
+ "",//
+ "\tstagedDeleted", //
+ "\tstagedModified", //
+ "\tstagedNew", //
+ "\ttracked", //
+ "\ttrackedDeleted", //
+ "\ttrackedModified", //
+ "\tuntracked", //
+ "" //
+ };
+ } else {
+ output = new String[] { //
+ "On branch master", //
+ "" //
+ };
+ }
+ }
+
+ assertArrayOfLinesEquals(output, execute(command));
+ }
+
+ private void assertStagedFiles(String command, boolean porcelain,
+ boolean untrackedFiles) throws Exception {
+ String[] output = new String[0];
+
+ if (porcelain) {
+ if (untrackedFiles) {
+ output = new String[] { //
"A stagedDeleted", //
"A stagedModified", //
"A tracked", //
@@ -252,24 +329,97 @@ public class StatusTest extends CLIRepositoryTestCase {
"?? stagedNew", //
"?? untracked", //
"" //
- }, execute("git status --porcelain")); //
- // Commit
- git.commit().setMessage("initial commit").call();
- assertArrayOfLinesEquals(new String[] { // git status output
+ };
+ } else {
+ output = new String[] { //
+ "A stagedDeleted", //
+ "A stagedModified", //
+ "A tracked", //
+ "A trackedDeleted", //
+ "A trackedModified", //
+ "" //
+ };
+ }
+ } else {
+ if (untrackedFiles) {
+ output = new String[] { //
+ "On branch master", //
+ "Changes to be committed:", //
+ "", //
+ "\tnew file: stagedDeleted", //
+ "\tnew file: stagedModified", //
+ "\tnew file: tracked", //
+ "\tnew file: trackedDeleted", //
+ "\tnew file: trackedModified", //
+ "", //
+ "Untracked files:", //
+ "", //
+ "\tstagedNew", //
+ "\tuntracked", //
+ "" //
+ };
+ } else {
+ output = new String[] { //
+ "On branch master", //
+ "Changes to be committed:", //
+ "", //
+ "\tnew file: stagedDeleted", //
+ "\tnew file: stagedModified", //
+ "\tnew file: tracked", //
+ "\tnew file: trackedDeleted", //
+ "\tnew file: trackedModified", //
+ "" //
+ };
+ }
+ }
+
+ assertArrayOfLinesEquals(output, execute(command));
+ }
+
+ private void assertAfterInitialCommit(String command, boolean porcelain,
+ boolean untrackedFiles) throws Exception {
+ String[] output = new String[0];
+
+ if (porcelain) {
+ if (untrackedFiles) {
+ output = new String[] { //
"?? stagedNew", //
"?? untracked", //
"" //
- }, execute("git status --porcelain")); //
- // Make some changes and stage them
- writeTrashFile("stagedModified", "stagedModified modified");
- deleteTrashFile("stagedDeleted");
- writeTrashFile("trackedModified", "trackedModified modified");
- deleteTrashFile("trackedDeleted");
- git.add().addFilepattern("stagedModified").call();
- git.rm().addFilepattern("stagedDeleted").call();
- git.add().addFilepattern("stagedNew").call();
- // Test staged/not-staged status
- assertArrayOfLinesEquals(new String[] { // git status output
+ };
+ } else {
+ output = new String[] { //
+ "" //
+ };
+ }
+ } else {
+ if (untrackedFiles) {
+ output = new String[] { //
+ "On branch master", //
+ "Untracked files:", //
+ "", //
+ "\tstagedNew", //
+ "\tuntracked", //
+ "" //
+ };
+ } else {
+ output = new String[] { //
+ "On branch master", //
+ "" //
+ };
+ }
+ }
+
+ assertArrayOfLinesEquals(output, execute(command));
+ }
+
+ private void assertStagedStatus(String command, boolean porcelain,
+ boolean untrackedFiles) throws Exception {
+ String[] output = new String[0];
+
+ if (porcelain) {
+ if (untrackedFiles) {
+ output = new String[] { //
"D stagedDeleted", //
"M stagedModified", //
"A stagedNew", //
@@ -277,58 +427,139 @@ public class StatusTest extends CLIRepositoryTestCase {
" M trackedModified", //
"?? untracked", //
"" //
- }, execute("git status --porcelain")); //
- // Create unmerged file
- writeTrashFile("unmerged", "unmerged");
- git.add().addFilepattern("unmerged").call();
- // Commit pending changes
- git.add().addFilepattern("trackedModified").call();
- git.rm().addFilepattern("trackedDeleted").call();
- git.commit().setMessage("commit before branching").call();
- assertArrayOfLinesEquals(new String[] { // git status output
- "?? untracked", //
+ };
+ } else {
+ output = new String[] { //
+ "D stagedDeleted", //
+ "M stagedModified", //
+ "A stagedNew", //
+ " D trackedDeleted", //
+ " M trackedModified", //
"" //
- }, execute("git status --porcelain")); //
- // Checkout new branch
- git.checkout().setCreateBranch(true).setName("test").call();
- // Test branch status
- assertArrayOfLinesEquals(new String[] { // git status output
- "?? untracked", //
+ };
+ }
+ } else {
+ if (untrackedFiles) {
+ output = new String[] { //
+ "On branch master", //
+ "Changes to be committed:", //
+ "", //
+ "\tdeleted: stagedDeleted", //
+ "\tmodified: stagedModified", //
+ "\tnew file: stagedNew", //
+ "", //
+ "Changes not staged for commit:", //
+ "", //
+ "\tdeleted: trackedDeleted", //
+ "\tmodified: trackedModified", //
+ "", //
+ "Untracked files:", //
+ "", //
+ "\tuntracked", //
"" //
- }, execute("git status --porcelain")); //
- // Commit change and checkout master again
- writeTrashFile("unmerged", "changed in test branch");
- git.add().addFilepattern("unmerged").call();
- RevCommit testBranch = git.commit()
- .setMessage("changed unmerged in test branch").call();
- assertArrayOfLinesEquals(new String[] { // git status output
+ };
+ } else {
+ output = new String[] { //
+ "On branch master", //
+ "Changes to be committed:", //
+ "", //
+ "\tdeleted: stagedDeleted", //
+ "\tmodified: stagedModified", //
+ "\tnew file: stagedNew", //
+ "", //
+ "Changes not staged for commit:", //
+ "", //
+ "\tdeleted: trackedDeleted", //
+ "\tmodified: trackedModified", //
+ "", //
+ };
+ }
+ }
+
+ assertArrayOfLinesEquals(output, execute(command));
+ }
+
+ private void assertUntracked(String command,
+ boolean porcelain,
+ boolean untrackedFiles, String branch) throws Exception {
+ String[] output = new String[0];
+ String branchHeader = "On branch " + branch;
+
+ if (porcelain) {
+ if (untrackedFiles) {
+ output = new String[] { //
"?? untracked", //
"" //
- }, execute("git status --porcelain")); //
- git.checkout().setName("master").call();
- // Change the same file and commit
- writeTrashFile("unmerged", "changed in master branch");
- git.add().addFilepattern("unmerged").call();
- git.commit().setMessage("changed unmerged in master branch").call();
- assertArrayOfLinesEquals(new String[] { // git status output
- "?? untracked", //
+ };
+ } else {
+ output = new String[] { //
"" //
- }, execute("git status --porcelain")); //
- // Merge test branch into master
- git.merge().include(testBranch.getId()).call();
- // Test unmerged status
- assertArrayOfLinesEquals(new String[] { // git status output
+ };
+ }
+ } else {
+ if (untrackedFiles) {
+ output = new String[] { //
+ branchHeader, //
+ "Untracked files:", //
+ "", //
+ "\tuntracked", //
+ "" //
+ };
+ } else {
+ output = new String[] { //
+ branchHeader, //
+ "" //
+ };
+ }
+ }
+
+ assertArrayOfLinesEquals(output, execute(command));
+ }
+
+ private void assertUntrackedAndUnmerged(String command, boolean porcelain,
+ boolean untrackedFiles, String branch) throws Exception {
+ String[] output = new String[0];
+ String branchHeader = (branch == null) //
+ ? "Not currently on any branch." //
+ : "On branch " + branch;
+
+ if (porcelain) {
+ if (untrackedFiles) {
+ output = new String[] { //
"UU unmerged", //
"?? untracked", //
"" //
- }, execute("git status --porcelain")); //
- // Test detached head
- String commitId = db.getRef(Constants.MASTER).getObjectId().name();
- git.checkout().setName(commitId).call();
- assertArrayOfLinesEquals(new String[] { // git status output
+ };
+ } else {
+ output = new String[] { //
"UU unmerged", //
- "?? untracked", //
"" //
- }, execute("git status --porcelain")); //
+ };
+ }
+ } else {
+ if (untrackedFiles) {
+ output = new String[] { //
+ branchHeader, //
+ "Unmerged paths:", //
+ "", //
+ "\tboth modified: unmerged", //
+ "", //
+ "Untracked files:", //
+ "", //
+ "\tuntracked", //
+ "" //
+ };
+ } else {
+ output = new String[] { //
+ branchHeader, //
+ "Unmerged paths:", //
+ "", //
+ "\tboth modified: unmerged", //
+ "" //
+ };
+ }
+ }
+
+ assertArrayOfLinesEquals(output, execute(command));
}
}
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/TagTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/TagTest.java
index ab09db5..0fe25f5 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/TagTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/TagTest.java
@@ -68,6 +68,6 @@ public class TagTest extends CLIRepositoryTestCase {
git.commit().setMessage("commit").call();
assertEquals("fatal: tag 'test' already exists",
- execute("git tag test")[0]);
+ executeUnchecked("git tag test")[0]);
}
}
diff --git a/org.eclipse.jgit.pgm/.classpath b/org.eclipse.jgit.pgm/.classpath
index 2f7278b..c5d5a57 100644
--- a/org.eclipse.jgit.pgm/.classpath
+++ b/org.eclipse.jgit.pgm/.classpath
@@ -3,7 +3,7 @@
<classpathentry kind="src" path="src"/>
<classpathentry excluding="*|resources/|resources/" including="META-INF/" kind="src" path=""/>
<classpathentry kind="src" path="resources"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs
index 12a5556..45d6d2c 100644
--- a/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.pgm/.settings/org.eclipse.jdt.core.prefs
@@ -1,15 +1,15 @@
eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled
+org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=enabled
org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
-org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
-org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
-org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
-org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
+org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jgit.annotations.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jgit.annotations.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jgit.annotations.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
@@ -67,11 +67,11 @@ org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
org.eclipse.jdt.core.compiler.problem.nullReference=error
org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
-org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
+org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=ignore
org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
-org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=error
org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
@@ -111,7 +111,7 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
-org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.compiler.source=1.7
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
diff --git a/org.eclipse.jgit.pgm/BUCK b/org.eclipse.jgit.pgm/BUCK
new file mode 100644
index 0000000..edcf2fc
--- /dev/null
+++ b/org.eclipse.jgit.pgm/BUCK
@@ -0,0 +1,70 @@
+include_defs('//tools/git.defs')
+
+java_library(
+ name = 'pgm',
+ srcs = glob(['src/**']),
+ resources = glob(['resources/**']),
+ deps = [
+ ':services',
+ '//org.eclipse.jgit:jgit',
+ '//org.eclipse.jgit.archive:jgit-archive',
+ '//org.eclipse.jgit.http.apache:http-apache',
+ '//org.eclipse.jgit.ui:ui',
+ '//lib:args4j',
+ ],
+ visibility = ['PUBLIC'],
+)
+
+prebuilt_jar(
+ name = 'services',
+ binary_jar = ':services__jar',
+)
+
+genrule(
+ name = 'services__jar',
+ cmd = 'cd $SRCDIR ; zip -qr $OUT .',
+ srcs = glob(['META-INF/services/*']),
+ out = 'services.jar',
+)
+
+genrule(
+ name = 'jgit',
+ cmd = ''.join([
+ 'mkdir $TMP/META-INF &&',
+ 'cp $(location :binary_manifest) $TMP/META-INF/MANIFEST.MF &&',
+ 'cp $(location :jgit_jar) $TMP/jgit.jar &&',
+ 'cd $TMP && zip $TMP/jgit.jar META-INF/MANIFEST.MF &&',
+ 'cat $SRCDIR/jgit.sh $TMP/jgit.jar >$OUT &&',
+ 'chmod a+x $OUT',
+ ]),
+ srcs = ['jgit.sh'],
+ out = 'jgit',
+ visibility = ['PUBLIC'],
+)
+
+java_binary(
+ name = 'jgit_jar',
+ deps = [
+ ':pgm',
+ '//lib:slf4j-simple',
+ '//lib:tukaani-xz',
+ ],
+ blacklist = [
+ 'META-INF/DEPENDENCIES',
+ 'META-INF/maven/.*',
+ ],
+)
+
+genrule(
+ name = 'binary_manifest',
+ cmd = ';'.join(['echo "%s: %s" >>$OUT' % e for e in [
+ ('Manifest-Version', '1.0'),
+ ('Main-Class', 'org.eclipse.jgit.pgm.Main'),
+ ('Bundle-Version', git_version()),
+ ('Implementation-Title', 'JGit Command Line Interface'),
+ ('Implementation-Vendor', 'Eclipse.org - JGit'),
+ ('Implementation-Vendor-URL', 'http://www.eclipse.org/jgit/'),
+ ('Implementation-Vendor-Id', 'org.eclipse.jgit'),
+ ]] + ['echo >>$OUT']),
+ out = 'MANIFEST.MF',
+)
diff --git a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
index 9ea4a0a..ec306d8 100644
--- a/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/MANIFEST.MF
@@ -2,43 +2,48 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Bundle-SymbolicName: org.eclipse.jgit.pgm
-Bundle-Version: 3.7.1.201504261725-r
+Bundle-Version: 4.2.0.201601211800-r
Bundle-Vendor: %provider_name
+Bundle-ActivationPolicy: lazy
Bundle-Localization: plugin
-Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Import-Package: org.apache.commons.compress.archivers;version="[1.3,2.0)",
org.apache.commons.compress.archivers.tar;version="[1.3,2.0)",
org.apache.commons.compress.archivers.zip;version="[1.3,2.0)",
- org.eclipse.jgit.api;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.api.errors;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.archive;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.awtui;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.blame;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.diff;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.dircache;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.errors;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.gitrepo;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.internal.storage.file;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.internal.storage.pack;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.lib;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.merge;version="3.7.1",
- org.eclipse.jgit.nls;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.notes;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.revplot;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.revwalk;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.revwalk.filter;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.storage.file;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.storage.pack;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.transport;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.transport.resolver;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.treewalk;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.treewalk.filter;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.util;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.util.io;version="[3.7.1,3.8.0)",
+ org.eclipse.jgit.api;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.api.errors;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.archive;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.awtui;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.blame;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.diff;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.dircache;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.errors;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.gitrepo;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.internal.storage.reftree;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.lib;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.merge;version="4.2.0",
+ org.eclipse.jgit.nls;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.notes;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.revplot;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.revwalk;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.revwalk.filter;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.storage.file;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.storage.pack;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.transport;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.transport.http.apache;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.treewalk;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.util;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.util.io;version="[4.2.0,4.3.0)",
org.kohsuke.args4j;version="[2.0.12,2.1.0)",
- org.kohsuke.args4j.spi;version="[2.0.12,2.1.0)"
-Bundle-ActivationPolicy: lazy
-Export-Package: org.eclipse.jgit.pgm;version="3.7.1";
+ org.kohsuke.args4j.spi;version="[2.0.15,2.1.0)"
+Export-Package: org.eclipse.jgit.console;version="4.2.0";
+ uses:="org.eclipse.jgit.transport,
+ org.eclipse.jgit.util",
+ org.eclipse.jgit.pgm;version="4.2.0";
uses:="org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.pgm.opt,
@@ -49,10 +54,11 @@ Export-Package: org.eclipse.jgit.pgm;version="3.7.1";
org.eclipse.jgit.treewalk,
javax.swing,
org.eclipse.jgit.transport",
- org.eclipse.jgit.pgm.debug;version="3.7.1";
- uses:="org.eclipse.jgit.pgm",
- org.eclipse.jgit.pgm.internal;version="3.7.1";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test",
- org.eclipse.jgit.pgm.opt;version="3.7.1";
+ org.eclipse.jgit.pgm.debug;version="4.2.0";
+ uses:="org.eclipse.jgit.util.io,
+ org.eclipse.jgit.pgm",
+ org.eclipse.jgit.pgm.internal;version="4.2.0";x-friends:="org.eclipse.jgit.pgm.test,org.eclipse.jgit.test",
+ org.eclipse.jgit.pgm.opt;version="4.2.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
org.kohsuke.args4j.spi,
diff --git a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
index 5a520a2..3ae1b87 100644
--- a/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit.pgm/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit.pgm - Sources
Bundle-SymbolicName: org.eclipse.jgit.pgm.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 3.7.1.201504261725-r
-Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="3.7.1.201504261725-r";roots="."
+Bundle-Version: 4.2.0.201601211800-r
+Eclipse-SourceBundle: org.eclipse.jgit.pgm;version="4.2.0.201601211800-r";roots="."
diff --git a/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin b/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
index e1b0549..6aa2004 100644
--- a/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
+++ b/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
@@ -24,6 +24,7 @@ org.eclipse.jgit.pgm.MergeBase
org.eclipse.jgit.pgm.Push
org.eclipse.jgit.pgm.ReceivePack
org.eclipse.jgit.pgm.Reflog
+org.eclipse.jgit.pgm.Remote
org.eclipse.jgit.pgm.Repo
org.eclipse.jgit.pgm.Reset
org.eclipse.jgit.pgm.RevList
@@ -40,6 +41,7 @@ org.eclipse.jgit.pgm.debug.DiffAlgorithms
org.eclipse.jgit.pgm.debug.MakeCacheTree
org.eclipse.jgit.pgm.debug.ReadDirCache
org.eclipse.jgit.pgm.debug.RebuildCommitGraph
+org.eclipse.jgit.pgm.debug.RebuildRefTree
org.eclipse.jgit.pgm.debug.ShowCacheTree
org.eclipse.jgit.pgm.debug.ShowCommands
org.eclipse.jgit.pgm.debug.ShowDirCache
diff --git a/org.eclipse.jgit.pgm/pom.xml b/org.eclipse.jgit.pgm/pom.xml
index 713f9f6..d3fdf3f 100644
--- a/org.eclipse.jgit.pgm/pom.xml
+++ b/org.eclipse.jgit.pgm/pom.xml
@@ -50,7 +50,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>3.7.1.201504261725-r</version>
+ <version>4.2.0.201601211800-r</version>
</parent>
<artifactId>org.eclipse.jgit.pgm</artifactId>
@@ -93,42 +93,36 @@
<artifactId>org.eclipse.jgit.ui</artifactId>
<version>${project.version}</version>
</dependency>
- </dependencies>
- <profiles>
- <profile>
- <id>java6</id>
- <activation>
- <jdk>1.6</jdk>
- </activation>
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jgit</groupId>
- <artifactId>org.eclipse.jgit.console</artifactId>
- <version>${project.version}</version>
- </dependency>
- </dependencies>
- </profile>
- <profile>
- <id>java7</id>
- <activation>
- <jdk>[1.7,)</jdk>
- </activation>
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jgit</groupId>
- <artifactId>org.eclipse.jgit.java7</artifactId>
- <version>${project.version}</version>
- <optional>true</optional>
- </dependency>
- <dependency>
- <groupId>org.eclipse.jgit</groupId>
- <artifactId>org.eclipse.jgit.console</artifactId>
- <version>${project.version}</version>
- </dependency>
- </dependencies>
- </profile>
- </profiles>
+ <dependency>
+ <groupId>org.eclipse.jgit</groupId>
+ <artifactId>org.eclipse.jgit.http.apache</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>${slf4j-version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>${slf4j-version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>${log4j-version}</version>
+ </dependency>
+ </dependencies>
<build>
<sourceDirectory>src/</sourceDirectory>
diff --git a/org.eclipse.jgit.pgm/resources/log4j.properties b/org.eclipse.jgit.pgm/resources/log4j.properties
new file mode 100644
index 0000000..1496c5a
--- /dev/null
+++ b/org.eclipse.jgit.pgm/resources/log4j.properties
@@ -0,0 +1,6 @@
+log4j.rootLogger=WARN, stderr
+
+log4j.appender.stderr=org.apache.log4j.ConsoleAppender
+log4j.appender.stderr.Target=System.err
+log4j.appender.stderr.layout=org.apache.log4j.PatternLayout
+log4j.appender.stderr.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
\ No newline at end of file
diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
index 2806f91..b4b1261 100644
--- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
+++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
@@ -12,12 +12,15 @@ N=N
alreadyOnBranch=Already on ''{0}''
alreadyUpToDate=Already up-to-date.
+answerNo=n
+answerYes=y
authorInfo=Author: {0} <{1}>
averageMSPerRead=average {0} ms/read
branchAlreadyExists=A branch named ''{0}'' already exists.
branchCreatedFrom=branch: Created from {0}
branchDetachedHEAD=detached HEAD
branchIsNotAnAncestorOfYourCurrentHEAD=The branch ''{0}'' is not an ancestor of your current HEAD.\nIf you are sure you want to delete it, run ''jgit branch -D {0}''.
+branchNameRequired=branch name required
branchNotFound=branch ''{0}'' not found.
cacheTreePathInfo="{0}": {1} entries, {2} children
cannotBeRenamed={0} cannot be renamed
@@ -87,7 +90,9 @@ metaVar_author=AUTHOR
metaVar_base=base
metaVar_blameL=START,END
metaVar_blameReverse=START..END
+metaVar_branchAndStartPoint=branch [start-name]
metaVar_branchName=branch
+metaVar_branchNames=branch ...
metaVar_bucket=BUCKET
metaVar_command=command
metaVar_commandDetail=DETAIL
@@ -107,6 +112,7 @@ metaVar_message=message
metaVar_n=n
metaVar_name=name
metaVar_object=object
+metaVar_oldNewBranchNames=[oldbranch] newbranch
metaVar_op=OP
metaVar_pass=PASS
metaVar_path=path
@@ -123,13 +129,16 @@ metaVar_treeish=tree-ish
metaVar_uriish=uri-ish
metaVar_url=URL
metaVar_user=USER
+metaVar_values=value ...
metaVar_version=VERSION
mostCommonlyUsedCommandsAre=The most commonly used commands are:
needApprovalToDestroyCurrentRepository=Need approval to destroy current repository
+needSingleRevision=Needed a single revision
noGitRepositoryConfigured=No Git repository configured.
noNamesFound=No names found, cannot describe anything.
noSuchFile=no such file: {0}
noSuchRemoteRef=no such remote ref: ''{0}''
+noSystemConsoleAvailable=No System.console available
noTREESectionInIndex=no 'TREE' section in index
nonFastForward=non-fast forward
notABranch={0} is not a branch
@@ -139,6 +148,7 @@ notAJgitCommand={0} is not a jgit command
notARevision=Not a revision: {0}
notATree={0} is not a tree
notAValidRefName={0} is not a valid ref name
+notAValidCommitName={0} is not a valid commit name
notAnIndexFile={0} is not an index file
notAnObject={0} is not an object
notFound=!! NOT FOUND !!
@@ -149,6 +159,7 @@ onBranch=On branch {0}
onBranchToBeBorn=You are on a branch yet to be born
onlyOneMetaVarExpectedIn=Only one {0} expected in {1}.
onlyOneOfIncludeOnlyAllInteractiveCanBeUsed=Only one of --include/--only/--all/--interactive can be used.
+password=Password:
pathspecDidNotMatch=error: pathspec ''{0}'' did not match any file(s) known to git.
pushTo=To {0}
pathsRequired=at least one path has to be specified when using --only
@@ -182,12 +193,14 @@ treeIsRequired=argument tree is required
tooManyRefsGiven=Too many refs given
unknownIoErrorStdout=An unknown I/O error occurred on standard output
unknownMergeStrategy=unknown merge strategy {0} specified
+unknownSubcommand=Unknown subcommand: {0}
unmergedPaths=Unmerged paths:
unsupportedOperation=Unsupported operation: {0}
untrackedFiles=Untracked files:
updating=Updating {0}..{1}
usage_Aggressive=This option will cause gc to more aggressively optimize the repository at the expense of taking much more time
usage_bareClone=Make a bare Git repository. That is, instead of creating [DIRECTORY] and placing the administrative files in [DIRECTORY]/.git, make the [DIRECTORY] itself the $GIT_DIR.
+usage_branches=Set branch field in .gitmodules
usage_Blame=Show what revision and author last modified each line
usage_CommandLineClientForamazonsS3Service=Command line client for Amazon's S3 service
usage_CommitAll=commit all modified and deleted files
@@ -205,6 +218,7 @@ usage_DisplayTheVersionOfJgit=Display the version of jgit
usage_Gc=Cleanup unnecessary files and optimize the local repository
usage_Glog=View commit history as a graph
usage_IndexPack=Build pack index file for an existing packed archive
+usage_LongFormat=Always output the long format
usage_LsRemote=List references in a remote repository
usage_lsRemoteHeads=Show only refs starting with refs/heads
usage_lsRemoteTags=Show only refs starting with refs/tags
@@ -214,6 +228,8 @@ usage_MergeBase=Find as good common ancestors as possible for a merge
usage_MergesTwoDevelopmentHistories=Merges two development histories
usage_ReadDirCache= Read the DirCache 100 times
usage_RebuildCommitGraph=Recreate a repository from another one's commit graph
+usage_RebuildRefTree=Copy references into a RefTree
+usage_Remote=Manage set of tracked repositories
usage_RepositoryToReadFrom=Repository to read from
usage_RepositoryToReceiveInto=Repository to receive into
usage_RevList=List commit objects in reverse chronological order
@@ -321,11 +337,13 @@ usage_performFsckStyleChecksOnReceive=perform fsck style checks on receive
usage_portNumberToListenOn=port number to listen on
usage_printOnlyBranchesThatContainTheCommit=print only branches that contain the commit
usage_pruneStaleTrackingRefs=prune stale tracking refs
+usage_pushUrls=push URLs are manipulated
usage_quiet=don't show progress messages
usage_recordChangesToRepository=Record changes to the repository
usage_recurseIntoSubtrees=recurse into subtrees
usage_renameLimit=limit size of rename matrix
usage_reset=Reset current HEAD to the specified state
+usage_resetReference=Reset to given reference name
usage_resetHard=Resets the index and working tree
usage_resetSoft=Resets without touching the index file nor the working tree
usage_resetMixed=Resets the index but not the working tree
@@ -341,8 +359,11 @@ usage_symbolicVersionForTheProject=Symbolic version for the project
usage_tags=fetch all tags
usage_notags=do not fetch tags
usage_tagMessage=tag message
+usage_untrackedFilesMode=show untracked files
+usage_updateRef=reference to update
usage_updateRemoteRefsFromAnotherRepository=Update remote refs from another repository
usage_useNameInsteadOfOriginToTrackUpstream=use <name> instead of 'origin' to track upstream
usage_checkoutBranchAfterClone=checkout named branch instead of remotes's HEAD
usage_viewCommitHistory=View commit history
-usage_orphan=Create a new orphan branch. The first commit made on this new branch will have no parents amd it will be the root of a new history totally disconnected from other branches and commits.
\ No newline at end of file
+usage_orphan=Create a new orphan branch. The first commit made on this new branch will have no parents amd it will be the root of a new history totally disconnected from other branches and commits.
+usernameFor=Username for {0}:
diff --git a/org.eclipse.jgit.console/src/org/eclipse/jgit/console/ConsoleAuthenticator.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/console/ConsoleAuthenticator.java
similarity index 88%
rename from org.eclipse.jgit.console/src/org/eclipse/jgit/console/ConsoleAuthenticator.java
rename to org.eclipse.jgit.pgm/src/org/eclipse/jgit/console/ConsoleAuthenticator.java
index 941205a..bdaccb0 100644
--- a/org.eclipse.jgit.console/src/org/eclipse/jgit/console/ConsoleAuthenticator.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/console/ConsoleAuthenticator.java
@@ -51,13 +51,20 @@ import java.text.MessageFormat;
import org.eclipse.jgit.util.CachedAuthenticator;
-/** Basic network prompt for username/password when using the console. */
+import org.eclipse.jgit.pgm.internal.CLIText;
+
+/**
+ * Basic network prompt for username/password when using the console.
+ *
+ * @since 4.0
+ */
public class ConsoleAuthenticator extends CachedAuthenticator {
/** Install this authenticator implementation into the JVM. */
public static void install() {
final ConsoleAuthenticator c = new ConsoleAuthenticator();
if (c.cons == null)
- throw new NoClassDefFoundError(ConsoleText.get().noSystemConsoleAvailable);
+ throw new NoClassDefFoundError(
+ CLIText.get().noSystemConsoleAvailable);
Authenticator.setDefault(c);
}
@@ -66,11 +73,12 @@ public class ConsoleAuthenticator extends CachedAuthenticator {
@Override
protected PasswordAuthentication promptPasswordAuthentication() {
final String realm = formatRealm();
- String username = cons.readLine(MessageFormat.format(ConsoleText.get().usernameFor + " ", realm)); //$NON-NLS-1$
+ String username = cons.readLine(MessageFormat.format(
+ CLIText.get().usernameFor + " ", realm)); //$NON-NLS-1$
if (username == null || username.isEmpty()) {
return null;
}
- char[] password = cons.readPassword(ConsoleText.get().password + " "); //$NON-NLS-1$
+ char[] password = cons.readPassword(CLIText.get().password + " "); //$NON-NLS-1$
if (password == null) {
password = new char[0];
}
diff --git a/org.eclipse.jgit.console/src/org/eclipse/jgit/console/ConsoleCredentialsProvider.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/console/ConsoleCredentialsProvider.java
similarity index 94%
rename from org.eclipse.jgit.console/src/org/eclipse/jgit/console/ConsoleCredentialsProvider.java
rename to org.eclipse.jgit.pgm/src/org/eclipse/jgit/console/ConsoleCredentialsProvider.java
index a01fd86..e805add 100644
--- a/org.eclipse.jgit.console/src/org/eclipse/jgit/console/ConsoleCredentialsProvider.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/console/ConsoleCredentialsProvider.java
@@ -54,14 +54,20 @@ import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.NetRCCredentialsProvider;
import org.eclipse.jgit.transport.URIish;
-/** Interacts with the user during authentication by using the text console. */
+import org.eclipse.jgit.pgm.internal.CLIText;
+
+/**
+ * Interacts with the user during authentication by using the text console.
+ *
+ * @since 4.0
+ */
public class ConsoleCredentialsProvider extends CredentialsProvider {
/** Install this implementation as the default. */
public static void install() {
final ConsoleCredentialsProvider c = new ConsoleCredentialsProvider();
if (c.cons == null)
throw new NoClassDefFoundError(
- ConsoleText.get().noSystemConsoleAvailable);
+ CLIText.get().noSystemConsoleAvailable);
CredentialsProvider cp = new ChainingCredentialsProvider(
new NetRCCredentialsProvider(), c);
CredentialsProvider.setDefault(cp);
@@ -168,9 +174,9 @@ public class ConsoleCredentialsProvider extends CredentialsProvider {
private boolean get(CredentialItem.YesNoType item) {
String r = cons.readLine("%s [%s/%s]? ", item.getPromptText(), //$NON-NLS-1$
- ConsoleText.get().answerYes, ConsoleText.get().answerNo);
+ CLIText.get().answerYes, CLIText.get().answerNo);
if (r != null) {
- item.setValue(ConsoleText.get().answerYes.equalsIgnoreCase(r));
+ item.setValue(CLIText.get().answerYes.equalsIgnoreCase(r));
return true;
} else {
return false;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/AbstractFetchCommand.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/AbstractFetchCommand.java
index d226df2..2cee2cb 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/AbstractFetchCommand.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/AbstractFetchCommand.java
@@ -67,8 +67,7 @@ abstract class AbstractFetchCommand extends TextBuiltin {
private boolean verbose;
protected void showFetchResult(final FetchResult r) throws IOException {
- ObjectReader reader = db.newObjectReader();
- try {
+ try (ObjectReader reader = db.newObjectReader()) {
boolean shownURI = false;
for (final TrackingRefUpdate u : r.getTrackingRefUpdates()) {
if (!verbose && u.getResult() == RefUpdate.Result.NO_CHANGE)
@@ -89,8 +88,6 @@ abstract class AbstractFetchCommand extends TextBuiltin {
src, dst);
outw.println();
}
- } finally {
- reader.release();
}
showRemoteMessages(errw, r.getMessages());
}
@@ -132,20 +129,20 @@ abstract class AbstractFetchCommand extends TextBuiltin {
final TrackingRefUpdate u) {
final RefUpdate.Result r = u.getResult();
if (r == RefUpdate.Result.LOCK_FAILURE)
- return "[lock fail]";
+ return "[lock fail]"; //$NON-NLS-1$
if (r == RefUpdate.Result.IO_FAILURE)
- return "[i/o error]";
+ return "[i/o error]"; //$NON-NLS-1$
if (r == RefUpdate.Result.REJECTED)
- return "[rejected]";
+ return "[rejected]"; //$NON-NLS-1$
if (ObjectId.zeroId().equals(u.getNewObjectId()))
- return "[deleted]";
+ return "[deleted]"; //$NON-NLS-1$
if (r == RefUpdate.Result.NEW) {
if (u.getRemoteName().startsWith(Constants.R_HEADS))
- return "[new branch]";
+ return "[new branch]"; //$NON-NLS-1$
else if (u.getLocalName().startsWith(Constants.R_TAGS))
- return "[new tag]";
- return "[new]";
+ return "[new tag]"; //$NON-NLS-1$
+ return "[new]"; //$NON-NLS-1$
}
if (r == RefUpdate.Result.FORCED) {
@@ -161,7 +158,7 @@ abstract class AbstractFetchCommand extends TextBuiltin {
}
if (r == RefUpdate.Result.NO_CHANGE)
- return "[up to date]";
+ return "[up to date]"; //$NON-NLS-1$
return "[" + r.name() + "]"; //$NON-NLS-1$//$NON-NLS-2$
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Add.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Add.java
index 12aac77..c36c485 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Add.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Add.java
@@ -62,10 +62,12 @@ class Add extends TextBuiltin {
@Override
protected void run() throws Exception {
- AddCommand addCmd = new Git(db).add();
- addCmd.setUpdate(update);
- for (String p : filepatterns)
- addCmd.addFilepattern(p);
- addCmd.call();
+ try (Git git = new Git(db)) {
+ AddCommand addCmd = git.add();
+ addCmd.setUpdate(update);
+ for (String p : filepatterns)
+ addCmd.addFilepattern(p);
+ addCmd.call();
+ }
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Archive.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Archive.java
index 80bb9ec..fe2ba83 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Archive.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Archive.java
@@ -87,8 +87,8 @@ class Archive extends TextBuiltin {
else
stream = outs;
- try {
- ArchiveCommand cmd = new Git(db).archive()
+ try (Git git = new Git(db)) {
+ ArchiveCommand cmd = git.archive()
.setTree(tree)
.setFormat(format)
.setPrefix(prefix)
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java
index 271e934..0f54171 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Blame.java
@@ -156,10 +156,9 @@ class Blame extends TextBuiltin {
else
dateFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ZZZZ"); //$NON-NLS-1$
- BlameGenerator generator = new BlameGenerator(db, file);
- RevFlag scanned = generator.newFlag("SCANNED"); //$NON-NLS-1$
reader = db.newObjectReader();
- try {
+ try (BlameGenerator generator = new BlameGenerator(db, file)) {
+ RevFlag scanned = generator.newFlag("SCANNED"); //$NON-NLS-1$
generator.setTextComparator(comparator);
if (!reverseRange.isEmpty()) {
@@ -247,8 +246,7 @@ class Blame extends TextBuiltin {
} while (++line < end && blame.getSourceCommit(line) == c);
}
} finally {
- generator.release();
- reader.release();
+ reader.close();
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Branch.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Branch.java
index 7147544..045f357 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Branch.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Branch.java
@@ -45,7 +45,6 @@ package org.eclipse.jgit.pgm;
import java.io.IOException;
import java.text.MessageFormat;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
@@ -65,15 +64,18 @@ import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.pgm.internal.CLIText;
-import org.eclipse.jgit.pgm.opt.CmdLineParser;
+import org.eclipse.jgit.pgm.opt.OptionWithValuesListHandler;
import org.eclipse.jgit.revwalk.RevWalk;
import org.kohsuke.args4j.Argument;
-import org.kohsuke.args4j.ExampleMode;
import org.kohsuke.args4j.Option;
@Command(common = true, usage = "usage_listCreateOrDeleteBranches")
class Branch extends TextBuiltin {
+ private String otherBranch;
+ private boolean createForce;
+ private boolean rename;
+
@Option(name = "--remote", aliases = { "-r" }, usage = "usage_actOnRemoteTrackingBranches")
private boolean remote = false;
@@ -83,23 +85,69 @@ class Branch extends TextBuiltin {
@Option(name = "--contains", metaVar = "metaVar_commitish", usage = "usage_printOnlyBranchesThatContainTheCommit")
private String containsCommitish;
- @Option(name = "--delete", aliases = { "-d" }, usage = "usage_deleteFullyMergedBranch")
- private boolean delete = false;
+ private List<String> delete;
+
+ @Option(name = "--delete", aliases = {
+ "-d" }, metaVar = "metaVar_branchNames", usage = "usage_deleteFullyMergedBranch", handler = OptionWithValuesListHandler.class)
+ public void delete(List<String> names) {
+ if (names.isEmpty()) {
+ throw die(CLIText.get().branchNameRequired);
+ }
+ delete = names;
+ }
+
+ private List<String> deleteForce;
- @Option(name = "--delete-force", aliases = { "-D" }, usage = "usage_deleteBranchEvenIfNotMerged")
- private boolean deleteForce = false;
+ @Option(name = "--delete-force", aliases = {
+ "-D" }, metaVar = "metaVar_branchNames", usage = "usage_deleteBranchEvenIfNotMerged", handler = OptionWithValuesListHandler.class)
+ public void deleteForce(List<String> names) {
+ if (names.isEmpty()) {
+ throw die(CLIText.get().branchNameRequired);
+ }
+ deleteForce = names;
+ }
- @Option(name = "--create-force", aliases = { "-f" }, usage = "usage_forceCreateBranchEvenExists")
- private boolean createForce = false;
+ @Option(name = "--create-force", aliases = {
+ "-f" }, metaVar = "metaVar_branchAndStartPoint", usage = "usage_forceCreateBranchEvenExists", handler = OptionWithValuesListHandler.class)
+ public void createForce(List<String> branchAndStartPoint) {
+ createForce = true;
+ if (branchAndStartPoint.isEmpty()) {
+ throw die(CLIText.get().branchNameRequired);
+ }
+ if (branchAndStartPoint.size() > 2) {
+ throw die(CLIText.get().tooManyRefsGiven);
+ }
+ if (branchAndStartPoint.size() == 1) {
+ branch = branchAndStartPoint.get(0);
+ } else {
+ branch = branchAndStartPoint.get(0);
+ otherBranch = branchAndStartPoint.get(1);
+ }
+ }
- @Option(name = "-m", usage = "usage_moveRenameABranch")
- private boolean rename = false;
+ @Option(name = "--move", aliases = {
+ "-m" }, metaVar = "metaVar_oldNewBranchNames", usage = "usage_moveRenameABranch", handler = OptionWithValuesListHandler.class)
+ public void moveRename(List<String> currentAndNew) {
+ rename = true;
+ if (currentAndNew.isEmpty()) {
+ throw die(CLIText.get().branchNameRequired);
+ }
+ if (currentAndNew.size() > 2) {
+ throw die(CLIText.get().tooManyRefsGiven);
+ }
+ if (currentAndNew.size() == 1) {
+ branch = currentAndNew.get(0);
+ } else {
+ branch = currentAndNew.get(0);
+ otherBranch = currentAndNew.get(1);
+ }
+ }
@Option(name = "--verbose", aliases = { "-v" }, usage = "usage_beVerbose")
private boolean verbose = false;
- @Argument
- private List<String> branches = new ArrayList<String>();
+ @Argument(metaVar = "metaVar_name")
+ private String branch;
private final Map<String, Ref> printRefs = new LinkedHashMap<String, Ref>();
@@ -110,30 +158,33 @@ class Branch extends TextBuiltin {
@Override
protected void run() throws Exception {
- if (delete || deleteForce)
- delete(deleteForce);
- else {
- if (branches.size() > 2)
- throw die(CLIText.get().tooManyRefsGiven + new CmdLineParser(this).printExample(ExampleMode.ALL));
-
+ if (delete != null || deleteForce != null) {
+ if (delete != null) {
+ delete(delete, false);
+ }
+ if (deleteForce != null) {
+ delete(deleteForce, true);
+ }
+ } else {
if (rename) {
String src, dst;
- if (branches.size() == 1) {
+ if (otherBranch == null) {
final Ref head = db.getRef(Constants.HEAD);
- if (head != null && head.isSymbolic())
+ if (head != null && head.isSymbolic()) {
src = head.getLeaf().getName();
- else
+ } else {
throw die(CLIText.get().cannotRenameDetachedHEAD);
- dst = branches.get(0);
+ }
+ dst = branch;
} else {
- src = branches.get(0);
+ src = branch;
final Ref old = db.getRef(src);
if (old == null)
throw die(MessageFormat.format(CLIText.get().doesNotExist, src));
if (!old.getName().startsWith(Constants.R_HEADS))
throw die(MessageFormat.format(CLIText.get().notABranch, src));
src = old.getName();
- dst = branches.get(1);
+ dst = otherBranch;
}
if (!dst.startsWith(Constants.R_HEADS))
@@ -145,37 +196,47 @@ class Branch extends TextBuiltin {
if (r.rename() != Result.RENAMED)
throw die(MessageFormat.format(CLIText.get().cannotBeRenamed, src));
- } else if (branches.size() > 0) {
- String newHead = branches.get(0);
+ } else if (createForce || branch != null) {
+ String newHead = branch;
String startBranch;
- if (branches.size() == 2)
- startBranch = branches.get(1);
- else
+ if (createForce) {
+ startBranch = otherBranch;
+ } else {
startBranch = Constants.HEAD;
+ }
Ref startRef = db.getRef(startBranch);
ObjectId startAt = db.resolve(startBranch + "^0"); //$NON-NLS-1$
- if (startRef != null)
+ if (startRef != null) {
startBranch = startRef.getName();
- else
+ } else if (startAt != null) {
startBranch = startAt.name();
+ } else {
+ throw die(MessageFormat.format(
+ CLIText.get().notAValidCommitName, startBranch));
+ }
startBranch = Repository.shortenRefName(startBranch);
String newRefName = newHead;
- if (!newRefName.startsWith(Constants.R_HEADS))
+ if (!newRefName.startsWith(Constants.R_HEADS)) {
newRefName = Constants.R_HEADS + newRefName;
- if (!Repository.isValidRefName(newRefName))
+ }
+ if (!Repository.isValidRefName(newRefName)) {
throw die(MessageFormat.format(CLIText.get().notAValidRefName, newRefName));
- if (!createForce && db.resolve(newRefName) != null)
+ }
+ if (!createForce && db.resolve(newRefName) != null) {
throw die(MessageFormat.format(CLIText.get().branchAlreadyExists, newHead));
+ }
RefUpdate updateRef = db.updateRef(newRefName);
updateRef.setNewObjectId(startAt);
updateRef.setForceUpdate(createForce);
updateRef.setRefLogMessage(MessageFormat.format(CLIText.get().branchCreatedFrom, startBranch), false);
Result update = updateRef.update();
- if (update == Result.REJECTED)
+ if (update == Result.REJECTED) {
throw die(MessageFormat.format(CLIText.get().couldNotCreateBranch, newHead, update.toString()));
+ }
} else {
- if (verbose)
+ if (verbose) {
rw = new RevWalk(db);
+ }
list();
}
}
@@ -186,33 +247,32 @@ class Branch extends TextBuiltin {
// This can happen if HEAD is stillborn
if (head != null) {
String current = head.getLeaf().getName();
- ListBranchCommand command = new Git(db).branchList();
- if (all)
- command.setListMode(ListMode.ALL);
- else if (remote)
- command.setListMode(ListMode.REMOTE);
-
- if (containsCommitish != null)
- command.setContains(containsCommitish);
-
- List<Ref> refs = command.call();
- for (Ref ref : refs) {
- if (ref.getName().equals(Constants.HEAD))
- addRef("(no branch)", head); //$NON-NLS-1$
- }
+ try (Git git = new Git(db)) {
+ ListBranchCommand command = git.branchList();
+ if (all)
+ command.setListMode(ListMode.ALL);
+ else if (remote)
+ command.setListMode(ListMode.REMOTE);
+
+ if (containsCommitish != null)
+ command.setContains(containsCommitish);
+
+ List<Ref> refs = command.call();
+ for (Ref ref : refs) {
+ if (ref.getName().equals(Constants.HEAD))
+ addRef("(no branch)", head); //$NON-NLS-1$
+ }
- addRefs(refs, Constants.R_HEADS);
- addRefs(refs, Constants.R_REMOTES);
+ addRefs(refs, Constants.R_HEADS);
+ addRefs(refs, Constants.R_REMOTES);
- ObjectReader reader = db.newObjectReader();
- try {
- for (final Entry<String, Ref> e : printRefs.entrySet()) {
- final Ref ref = e.getValue();
- printHead(reader, e.getKey(),
- current.equals(ref.getName()), ref);
+ try (ObjectReader reader = db.newObjectReader()) {
+ for (final Entry<String, Ref> e : printRefs.entrySet()) {
+ final Ref ref = e.getValue();
+ printHead(reader, e.getKey(),
+ current.equals(ref.getName()), ref);
+ }
}
- } finally {
- reader.release();
}
}
}
@@ -246,11 +306,12 @@ class Branch extends TextBuiltin {
outw.println();
}
- private void delete(boolean force) throws IOException {
+ private void delete(List<String> branches, boolean force)
+ throws IOException {
String current = db.getBranch();
ObjectId head = db.resolve(Constants.HEAD);
for (String branch : branches) {
- if (current.equals(branch)) {
+ if (branch.equals(current)) {
throw die(MessageFormat.format(CLIText.get().cannotDeleteTheBranchWhichYouAreCurrentlyOn, branch));
}
RefUpdate update = db.updateRef((remote ? Constants.R_REMOTES
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java
index 56d4fcf..94517db 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Checkout.java
@@ -60,7 +60,7 @@ import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.pgm.internal.CLIText;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
-import org.kohsuke.args4j.spi.StopOptionHandler;
+import org.kohsuke.args4j.spi.RestOfArgumentsHandler;
@Command(common = true, usage = "usage_checkout")
class Checkout extends TextBuiltin {
@@ -74,11 +74,10 @@ class Checkout extends TextBuiltin {
@Option(name = "--orphan", usage = "usage_orphan")
private boolean orphan = false;
- @Argument(required = true, index = 0, metaVar = "metaVar_name", usage = "usage_checkout")
+ @Argument(required = false, index = 0, metaVar = "metaVar_name", usage = "usage_checkout")
private String name;
- @Argument(index = 1)
- @Option(name = "--", metaVar = "metaVar_paths", multiValued = true, handler = StopOptionHandler.class)
+ @Option(name = "--", metaVar = "metaVar_paths", multiValued = true, handler = RestOfArgumentsHandler.class)
private List<String> paths = new ArrayList<String>();
@Override
@@ -89,47 +88,49 @@ class Checkout extends TextBuiltin {
throw die(CLIText.get().onBranchToBeBorn);
}
- CheckoutCommand command = new Git(db).checkout();
- if (paths.size() > 0) {
- command.setStartPoint(name);
- for (String path : paths)
- command.addPath(path);
- } else {
- command.setCreateBranch(createBranch);
- command.setName(name);
- command.setForce(force);
- command.setOrphan(orphan);
- }
- try {
- String oldBranch = db.getBranch();
- Ref ref = command.call();
- if (ref == null)
- return;
- if (Repository.shortenRefName(ref.getName()).equals(oldBranch)) {
+ try (Git git = new Git(db)) {
+ CheckoutCommand command = git.checkout();
+ if (paths.size() > 0) {
+ command.setStartPoint(name);
+ for (String path : paths)
+ command.addPath(path);
+ } else {
+ command.setCreateBranch(createBranch);
+ command.setName(name);
+ command.setForce(force);
+ command.setOrphan(orphan);
+ }
+ try {
+ String oldBranch = db.getBranch();
+ Ref ref = command.call();
+ if (ref == null)
+ return;
+ if (Repository.shortenRefName(ref.getName()).equals(oldBranch)) {
+ outw.println(MessageFormat.format(
+ CLIText.get().alreadyOnBranch,
+ name));
+ return;
+ }
+ if (createBranch || orphan)
+ outw.println(MessageFormat.format(
+ CLIText.get().switchedToNewBranch, name));
+ else
+ outw.println(MessageFormat.format(
+ CLIText.get().switchedToBranch,
+ Repository.shortenRefName(ref.getName())));
+ } catch (RefNotFoundException e) {
outw.println(MessageFormat.format(
- CLIText.get().alreadyOnBranch,
+ CLIText.get().pathspecDidNotMatch,
+ name));
+ } catch (RefAlreadyExistsException e) {
+ throw die(MessageFormat.format(CLIText.get().branchAlreadyExists,
name));
- return;
+ } catch (CheckoutConflictException e) {
+ outw.println(CLIText.get().checkoutConflict);
+ for (String path : e.getConflictingPaths())
+ outw.println(MessageFormat.format(
+ CLIText.get().checkoutConflictPathLine, path));
}
- if (createBranch || orphan)
- outw.println(MessageFormat.format(
- CLIText.get().switchedToNewBranch, name));
- else
- outw.println(MessageFormat.format(
- CLIText.get().switchedToBranch,
- Repository.shortenRefName(ref.getName())));
- } catch (RefNotFoundException e) {
- outw.println(MessageFormat.format(
- CLIText.get().pathspecDidNotMatch,
- name));
- } catch (RefAlreadyExistsException e) {
- throw die(MessageFormat.format(CLIText.get().branchAlreadyExists,
- name));
- } catch (CheckoutConflictException e) {
- outw.println(CLIText.get().checkoutConflict);
- for (String path : e.getConflictingPaths())
- outw.println(MessageFormat.format(
- CLIText.get().checkoutConflictPathLine, path));
}
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java
index cd6953c..0407828 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java
@@ -50,6 +50,7 @@ import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.InvalidRemoteException;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.TextProgressMonitor;
import org.eclipse.jgit.pgm.internal.CLIText;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.util.SystemReader;
@@ -70,6 +71,9 @@ class Clone extends AbstractFetchCommand {
@Option(name = "--bare", usage = "usage_bareClone")
private boolean isBare;
+ @Option(name = "--quiet", usage = "usage_quiet")
+ private Boolean quiet;
+
@Argument(index = 0, required = true, metaVar = "metaVar_uriish")
private String sourceUri;
@@ -109,10 +113,16 @@ class Clone extends AbstractFetchCommand {
command.setGitDir(gitdir == null ? null : new File(gitdir));
command.setDirectory(localNameF);
- outw.println(MessageFormat.format(CLIText.get().cloningInto, localName));
+ boolean msgs = quiet == null || !quiet.booleanValue();
+ if (msgs) {
+ command.setProgressMonitor(new TextProgressMonitor(errw));
+ outw.println(MessageFormat.format(
+ CLIText.get().cloningInto, localName));
+ outw.flush();
+ }
try {
db = command.call().getRepository();
- if (db.resolve(Constants.HEAD) == null)
+ if (msgs && db.resolve(Constants.HEAD) == null)
outw.println(CLIText.get().clonedEmptyRepository);
} catch (InvalidRemoteException e) {
throw die(MessageFormat.format(CLIText.get().doesNotExist,
@@ -121,8 +131,9 @@ class Clone extends AbstractFetchCommand {
if (db != null)
db.close();
}
-
- outw.println();
- outw.flush();
+ if (msgs) {
+ outw.println();
+ outw.flush();
+ }
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Commit.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Commit.java
index 14c449a..38d8d70 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Commit.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Commit.java
@@ -80,37 +80,42 @@ class Commit extends TextBuiltin {
@Override
protected void run() throws NoHeadException, NoMessageException,
ConcurrentRefUpdateException, JGitInternalException, Exception {
- CommitCommand commitCmd = new Git(db).commit();
- if (author != null)
- commitCmd.setAuthor(RawParseUtils.parsePersonIdent(author));
- if (message != null)
- commitCmd.setMessage(message);
- if (only && paths.isEmpty())
- throw die(CLIText.get().pathsRequired);
- if (only && all)
- throw die(CLIText.get().onlyOneOfIncludeOnlyAllInteractiveCanBeUsed);
- if (!paths.isEmpty())
- for (String p : paths)
- commitCmd.setOnly(p);
- commitCmd.setAmend(amend);
- commitCmd.setAll(all);
- Ref head = db.getRef(Constants.HEAD);
- RevCommit commit;
- try {
- commit = commitCmd.call();
- } catch (JGitInternalException e) {
- throw die(e.getMessage());
- }
+ try (Git git = new Git(db)) {
+ CommitCommand commitCmd = git.commit();
+ if (author != null)
+ commitCmd.setAuthor(RawParseUtils.parsePersonIdent(author));
+ if (message != null)
+ commitCmd.setMessage(message);
+ if (only && paths.isEmpty())
+ throw die(CLIText.get().pathsRequired);
+ if (only && all)
+ throw die(CLIText.get().onlyOneOfIncludeOnlyAllInteractiveCanBeUsed);
+ if (!paths.isEmpty())
+ for (String p : paths)
+ commitCmd.setOnly(p);
+ commitCmd.setAmend(amend);
+ commitCmd.setAll(all);
+ Ref head = db.getRef(Constants.HEAD);
+ if (head == null) {
+ throw die(CLIText.get().onBranchToBeBorn);
+ }
+ RevCommit commit;
+ try {
+ commit = commitCmd.call();
+ } catch (JGitInternalException e) {
+ throw die(e.getMessage());
+ }
- String branchName;
- if (!head.isSymbolic())
- branchName = CLIText.get().branchDetachedHEAD;
- else {
- branchName = head.getTarget().getName();
- if (branchName.startsWith(Constants.R_HEADS))
- branchName = branchName.substring(Constants.R_HEADS.length());
+ String branchName;
+ if (!head.isSymbolic())
+ branchName = CLIText.get().branchDetachedHEAD;
+ else {
+ branchName = head.getTarget().getName();
+ if (branchName.startsWith(Constants.R_HEADS))
+ branchName = branchName.substring(Constants.R_HEADS.length());
+ }
+ outw.println("[" + branchName + " " + commit.name() + "] " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ + commit.getShortMessage());
}
- outw.println("[" + branchName + " " + commit.name() + "] " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
- + commit.getShortMessage());
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Config.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Config.java
index 8569e92..faae13a 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Config.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Config.java
@@ -74,7 +74,7 @@ class Config extends TextBuiltin {
list();
else
throw new NotSupportedException(
- "only --list option is currently supported");
+ "only --list option is currently supported"); //$NON-NLS-1$
}
private void list() throws IOException, ConfigInvalidException {
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java
index 52e8809..ec000f3 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Describe.java
@@ -48,6 +48,7 @@ import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.pgm.internal.CLIText;
import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.Option;
@Command(common = true, usage = "usage_Describe")
class Describe extends TextBuiltin {
@@ -55,21 +56,26 @@ class Describe extends TextBuiltin {
@Argument(index = 0, metaVar = "metaVar_treeish")
private ObjectId tree;
+ @Option(name = "--long", usage = "usage_LongFormat")
+ private boolean longDesc;
+
@Override
protected void run() throws Exception {
- DescribeCommand cmd = new Git(db).describe();
- if (tree != null)
- cmd.setTarget(tree);
- String result = null;
- try {
- result = cmd.call();
- } catch (RefNotFoundException e) {
- throw die(CLIText.get().noNamesFound, e);
- }
- if (result == null)
- throw die(CLIText.get().noNamesFound);
+ try (Git git = new Git(db)) {
+ DescribeCommand cmd = git.describe();
+ if (tree != null)
+ cmd.setTarget(tree);
+ cmd.setLong(longDesc);
+ String result = null;
+ try {
+ result = cmd.call();
+ } catch (RefNotFoundException e) {
+ throw die(CLIText.get().noNamesFound, e);
+ }
+ if (result == null)
+ throw die(CLIText.get().noNamesFound);
- outw.println(result);
+ outw.println(result);
+ }
}
-
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Die.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Die.java
index f07df1a..a25f1e9 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Die.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Die.java
@@ -86,6 +86,21 @@ public class Die extends RuntimeException {
* @since 3.4
*/
public Die(boolean aborted) {
+ this(aborted, null);
+ }
+
+ /**
+ * Construct a new exception reflecting the fact that the command execution
+ * has been aborted before running.
+ *
+ * @param aborted
+ * boolean indicating the fact the execution has been aborted
+ * @param cause
+ * can be null
+ * @since 4.2
+ */
+ public Die(boolean aborted, final Throwable cause) {
+ super(cause != null ? cause.getMessage() : null, cause);
this.aborted = aborted;
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Diff.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Diff.java
index fe2df65..61a385d 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Diff.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Diff.java
@@ -184,11 +184,8 @@ class Diff extends TextBuiltin {
if (head == null)
die(MessageFormat.format(CLIText.get().notATree, HEAD));
CanonicalTreeParser p = new CanonicalTreeParser();
- ObjectReader reader = db.newObjectReader();
- try {
+ try (ObjectReader reader = db.newObjectReader()) {
p.reset(reader, head);
- } finally {
- reader.release();
}
oldTree = p;
}
@@ -219,7 +216,7 @@ class Diff extends TextBuiltin {
diffFmt.flush();
}
} finally {
- diffFmt.release();
+ diffFmt.close();
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/DiffTree.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/DiffTree.java
index d89053c..32adf6d 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/DiffTree.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/DiffTree.java
@@ -74,46 +74,47 @@ class DiffTree extends TextBuiltin {
@Override
protected void run() throws Exception {
- final TreeWalk walk = new TreeWalk(db);
- walk.setRecursive(recursive);
- for (final AbstractTreeIterator i : trees)
- walk.addTree(i);
- walk.setFilter(AndTreeFilter.create(TreeFilter.ANY_DIFF, pathFilter));
+ try (final TreeWalk walk = new TreeWalk(db)) {
+ walk.setRecursive(recursive);
+ for (final AbstractTreeIterator i : trees)
+ walk.addTree(i);
+ walk.setFilter(AndTreeFilter.create(TreeFilter.ANY_DIFF, pathFilter));
- final int nTree = walk.getTreeCount();
- while (walk.next()) {
- for (int i = 1; i < nTree; i++)
- outw.print(':');
- for (int i = 0; i < nTree; i++) {
- final FileMode m = walk.getFileMode(i);
- final String s = m.toString();
- for (int pad = 6 - s.length(); pad > 0; pad--)
- outw.print('0');
- outw.print(s);
- outw.print(' ');
- }
+ final int nTree = walk.getTreeCount();
+ while (walk.next()) {
+ for (int i = 1; i < nTree; i++)
+ outw.print(':');
+ for (int i = 0; i < nTree; i++) {
+ final FileMode m = walk.getFileMode(i);
+ final String s = m.toString();
+ for (int pad = 6 - s.length(); pad > 0; pad--)
+ outw.print('0');
+ outw.print(s);
+ outw.print(' ');
+ }
- for (int i = 0; i < nTree; i++) {
- outw.print(walk.getObjectId(i).name());
- outw.print(' ');
- }
+ for (int i = 0; i < nTree; i++) {
+ outw.print(walk.getObjectId(i).name());
+ outw.print(' ');
+ }
- char chg = 'M';
- if (nTree == 2) {
- final int m0 = walk.getRawMode(0);
- final int m1 = walk.getRawMode(1);
- if (m0 == 0 && m1 != 0)
- chg = 'A';
- else if (m0 != 0 && m1 == 0)
- chg = 'D';
- else if (m0 != m1 && walk.idEqual(0, 1))
- chg = 'T';
- }
- outw.print(chg);
+ char chg = 'M';
+ if (nTree == 2) {
+ final int m0 = walk.getRawMode(0);
+ final int m1 = walk.getRawMode(1);
+ if (m0 == 0 && m1 != 0)
+ chg = 'A';
+ else if (m0 != 0 && m1 == 0)
+ chg = 'D';
+ else if (m0 != m1 && walk.idEqual(0, 1))
+ chg = 'T';
+ }
+ outw.print(chg);
- outw.print('\t');
- outw.print(walk.getPathString());
- outw.println();
+ outw.print('\t');
+ outw.print(walk.getPathString());
+ outw.println();
+ }
}
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Fetch.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Fetch.java
index 186fdd8..ed06733 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Fetch.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Fetch.java
@@ -104,31 +104,32 @@ class Fetch extends AbstractFetchCommand {
@Override
protected void run() throws Exception {
- Git git = new Git(db);
- FetchCommand fetch = git.fetch();
- if (fsck != null)
- fetch.setCheckFetchedObjects(fsck.booleanValue());
- if (prune != null)
- fetch.setRemoveDeletedRefs(prune.booleanValue());
- if (toget != null)
- fetch.setRefSpecs(toget);
- if (tags != null) {
- fetch.setTagOpt(tags.booleanValue() ? TagOpt.FETCH_TAGS
- : TagOpt.NO_TAGS);
+ try (Git git = new Git(db)) {
+ FetchCommand fetch = git.fetch();
+ if (fsck != null)
+ fetch.setCheckFetchedObjects(fsck.booleanValue());
+ if (prune != null)
+ fetch.setRemoveDeletedRefs(prune.booleanValue());
+ if (toget != null)
+ fetch.setRefSpecs(toget);
+ if (tags != null) {
+ fetch.setTagOpt(tags.booleanValue() ? TagOpt.FETCH_TAGS
+ : TagOpt.NO_TAGS);
+ }
+ if (0 <= timeout)
+ fetch.setTimeout(timeout);
+ fetch.setDryRun(dryRun);
+ fetch.setRemote(remote);
+ if (thin != null)
+ fetch.setThin(thin.booleanValue());
+ if (quiet == null || !quiet.booleanValue())
+ fetch.setProgressMonitor(new TextProgressMonitor(errw));
+
+ FetchResult result = fetch.call();
+ if (result.getTrackingRefUpdates().isEmpty())
+ return;
+
+ showFetchResult(result);
}
- if (0 <= timeout)
- fetch.setTimeout(timeout);
- fetch.setDryRun(dryRun);
- fetch.setRemote(remote);
- if (thin != null)
- fetch.setThin(thin.booleanValue());
- if (quiet == null || !quiet.booleanValue())
- fetch.setProgressMonitor(new TextProgressMonitor(errw));
-
- FetchResult result = fetch.call();
- if (result.getTrackingRefUpdates().isEmpty())
- return;
-
- showFetchResult(result);
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/IndexPack.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/IndexPack.java
index 4afb6d5..22f3be9 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/IndexPack.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/IndexPack.java
@@ -63,8 +63,7 @@ class IndexPack extends TextBuiltin {
@Override
protected void run() throws Exception {
BufferedInputStream in = new BufferedInputStream(ins);
- ObjectInserter inserter = db.newObjectInserter();
- try {
+ try (ObjectInserter inserter = db.newObjectInserter()) {
PackParser p = inserter.newPackParser(in);
p.setAllowThin(fixThin);
if (indexVersion != -1 && p instanceof ObjectDirectoryPackParser) {
@@ -73,8 +72,6 @@ class IndexPack extends TextBuiltin {
}
p.parse(new TextProgressMonitor(errw));
inserter.flush();
- } finally {
- inserter.release();
}
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java
index 048526e..6947cdd 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java
@@ -223,7 +223,7 @@ class Log extends RevWalkTextBuiltin {
super.run();
} finally {
- diffFmt.release();
+ diffFmt.close();
}
}
@@ -267,6 +267,7 @@ class Log extends RevWalkTextBuiltin {
outw.print(s);
outw.println();
}
+ c.disposeBody();
outw.println();
if (showNotes(c))
@@ -323,7 +324,7 @@ class Log extends RevWalkTextBuiltin {
return false;
if (emptyLine)
outw.println();
- outw.print("Notes");
+ outw.print("Notes"); //$NON-NLS-1$
if (label != null) {
outw.print(" ("); //$NON-NLS-1$
outw.print(label);
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsTree.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsTree.java
index 4b16ed8..872ea67 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsTree.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/LsTree.java
@@ -72,27 +72,28 @@ class LsTree extends TextBuiltin {
@Override
protected void run() throws Exception {
- final TreeWalk walk = new TreeWalk(db);
- walk.reset(); // drop the first empty tree, which we do not need here
- if (paths.size() > 0)
- walk.setFilter(PathFilterGroup.createFromStrings(paths));
- walk.setRecursive(recursive);
- walk.addTree(tree);
+ try (final TreeWalk walk = new TreeWalk(db)) {
+ walk.reset(); // drop the first empty tree, which we do not need here
+ if (paths.size() > 0)
+ walk.setFilter(PathFilterGroup.createFromStrings(paths));
+ walk.setRecursive(recursive);
+ walk.addTree(tree);
- while (walk.next()) {
- final FileMode mode = walk.getFileMode(0);
- if (mode == FileMode.TREE)
- outw.print('0');
- outw.print(mode);
- outw.print(' ');
- outw.print(Constants.typeString(mode.getObjectType()));
+ while (walk.next()) {
+ final FileMode mode = walk.getFileMode(0);
+ if (mode == FileMode.TREE)
+ outw.print('0');
+ outw.print(mode);
+ outw.print(' ');
+ outw.print(Constants.typeString(mode.getObjectType()));
- outw.print(' ');
- outw.print(walk.getObjectId(0).name());
+ outw.print(' ');
+ outw.print(walk.getObjectId(0).name());
- outw.print('\t');
- outw.print(QuotedString.GIT_PATH.quote(walk.getPathString()));
- outw.println();
+ outw.print('\t');
+ outw.print(QuotedString.GIT_PATH.quote(walk.getPathString()));
+ outw.println();
+ }
}
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java
index 7151de7..d701f22 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java
@@ -62,6 +62,8 @@ import org.eclipse.jgit.lib.RepositoryBuilder;
import org.eclipse.jgit.pgm.internal.CLIText;
import org.eclipse.jgit.pgm.opt.CmdLineParser;
import org.eclipse.jgit.pgm.opt.SubcommandHandler;
+import org.eclipse.jgit.transport.HttpTransport;
+import org.eclipse.jgit.transport.http.apache.HttpClientConnectionFactory;
import org.eclipse.jgit.util.CachedAuthenticator;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
@@ -88,13 +90,23 @@ public class Main {
@Argument(index = 1, metaVar = "metaVar_arg")
private List<String> arguments = new ArrayList<String>();
+ PrintWriter writer;
+
+ /**
+ *
+ */
+ public Main() {
+ HttpTransport.setConnectionFactory(new HttpClientConnectionFactory());
+ }
+
/**
* Execute the command line.
*
* @param argv
* arguments.
+ * @throws Exception
*/
- public static void main(final String[] argv) {
+ public static void main(final String[] argv) throws Exception {
new Main().run(argv);
}
@@ -113,8 +125,10 @@ public class Main {
*
* @param argv
* arguments.
+ * @throws Exception
*/
- protected void run(final String[] argv) {
+ protected void run(final String[] argv) throws Exception {
+ writer = createErrorWriter();
try {
if (!installConsole()) {
AwtAuthenticator.install();
@@ -123,12 +137,14 @@ public class Main {
configureHttpProxy();
execute(argv);
} catch (Die err) {
- if (err.isAborted())
- System.exit(1);
- System.err.println(MessageFormat.format(CLIText.get().fatalError, err.getMessage()));
- if (showStackTrace)
- err.printStackTrace();
- System.exit(128);
+ if (err.isAborted()) {
+ exit(1, err);
+ }
+ writer.println(CLIText.fatalError(err.getMessage()));
+ if (showStackTrace) {
+ err.printStackTrace(writer);
+ }
+ exit(128, err);
} catch (Exception err) {
// Try to detect errno == EPIPE and exit normally if that happens
// There may be issues with operating system versions and locale,
@@ -136,52 +152,60 @@ public class Main {
// under other circumstances.
if (err.getClass() == IOException.class) {
// Linux, OS X
- if (err.getMessage().equals("Broken pipe")) //$NON-NLS-1$
- System.exit(0);
+ if (err.getMessage().equals("Broken pipe")) { //$NON-NLS-1$
+ exit(0, err);
+ }
// Windows
- if (err.getMessage().equals("The pipe is being closed")) //$NON-NLS-1$
- System.exit(0);
+ if (err.getMessage().equals("The pipe is being closed")) { //$NON-NLS-1$
+ exit(0, err);
+ }
}
if (!showStackTrace && err.getCause() != null
- && err instanceof TransportException)
- System.err.println(MessageFormat.format(CLIText.get().fatalError, err.getCause().getMessage()));
+ && err instanceof TransportException) {
+ writer.println(CLIText.fatalError(err.getCause().getMessage()));
+ }
if (err.getClass().getName().startsWith("org.eclipse.jgit.errors.")) { //$NON-NLS-1$
- System.err.println(MessageFormat.format(CLIText.get().fatalError, err.getMessage()));
- if (showStackTrace)
+ writer.println(CLIText.fatalError(err.getMessage()));
+ if (showStackTrace) {
err.printStackTrace();
- System.exit(128);
+ }
+ exit(128, err);
}
err.printStackTrace();
- System.exit(1);
+ exit(1, err);
}
if (System.out.checkError()) {
- System.err.println(CLIText.get().unknownIoErrorStdout);
- System.exit(1);
+ writer.println(CLIText.get().unknownIoErrorStdout);
+ exit(1, null);
}
- if (System.err.checkError()) {
+ if (writer.checkError()) {
// No idea how to present an error here, most likely disk full or
// broken pipe
- System.exit(1);
+ exit(1, null);
}
}
+ PrintWriter createErrorWriter() {
+ return new PrintWriter(System.err);
+ }
+
private void execute(final String[] argv) throws Exception {
- final CmdLineParser clp = new CmdLineParser(this);
- PrintWriter writer = new PrintWriter(System.err);
+ final CmdLineParser clp = new SubcommandLineParser(this);
+
try {
clp.parseArgument(argv);
} catch (CmdLineException err) {
if (argv.length > 0 && !help && !version) {
- writer.println(MessageFormat.format(CLIText.get().fatalError, err.getMessage()));
+ writer.println(CLIText.fatalError(err.getMessage()));
writer.flush();
- System.exit(1);
+ exit(1, err);
}
}
if (argv.length == 0 || help) {
final String ex = clp.printExample(ExampleMode.ALL, CLIText.get().resourceBundle());
- writer.println("jgit" + ex + " command [ARG ...]"); //$NON-NLS-1$
+ writer.println("jgit" + ex + " command [ARG ...]"); //$NON-NLS-1$ //$NON-NLS-2$
if (help) {
writer.println();
clp.printUsage(writer, CLIText.get().resourceBundle());
@@ -191,22 +215,24 @@ public class Main {
writer.println(CLIText.get().mostCommonlyUsedCommandsAre);
final CommandRef[] common = CommandCatalog.common();
int width = 0;
- for (final CommandRef c : common)
+ for (final CommandRef c : common) {
width = Math.max(width, c.getName().length());
+ }
width += 2;
for (final CommandRef c : common) {
writer.print(' ');
writer.print(c.getName());
- for (int i = c.getName().length(); i < width; i++)
+ for (int i = c.getName().length(); i < width; i++) {
writer.print(' ');
+ }
writer.print(CLIText.get().resourceBundle().getString(c.getUsage()));
writer.println();
}
writer.println();
}
writer.flush();
- System.exit(1);
+ exit(1, null);
}
if (version) {
@@ -215,21 +241,39 @@ public class Main {
}
final TextBuiltin cmd = subcommand;
- if (cmd.requiresRepository())
- cmd.init(openGitDir(gitdir), null);
- else
- cmd.init(null, gitdir);
+ init(cmd);
try {
cmd.execute(arguments.toArray(new String[arguments.size()]));
} finally {
- if (cmd.outw != null)
+ if (cmd.outw != null) {
cmd.outw.flush();
- if (cmd.errw != null)
+ }
+ if (cmd.errw != null) {
cmd.errw.flush();
+ }
+ }
+ }
+
+ void init(final TextBuiltin cmd) throws IOException {
+ if (cmd.requiresRepository()) {
+ cmd.init(openGitDir(gitdir), null);
+ } else {
+ cmd.init(null, gitdir);
}
}
/**
+ * @param status
+ * @param t
+ * can be {@code null}
+ * @throws Exception
+ */
+ void exit(int status, Exception t) throws Exception {
+ writer.flush();
+ System.exit(status);
+ }
+
+ /**
* Evaluate the {@code --git-dir} option and open the repository.
*
* @param aGitdir
@@ -278,7 +322,7 @@ public class Main {
throws IllegalAccessException, InvocationTargetException,
NoSuchMethodException, ClassNotFoundException {
try {
- Class.forName(name).getMethod("install").invoke(null); //$NON-NLS-1$
+ Class.forName(name).getMethod("install").invoke(null); //$NON-NLS-1$
} catch (InvocationTargetException e) {
if (e.getCause() instanceof RuntimeException)
throw (RuntimeException) e.getCause();
@@ -291,39 +335,60 @@ public class Main {
/**
* Configure the JRE's standard HTTP based on <code>http_proxy</code>.
* <p>
- * The popular libcurl library honors the <code>http_proxy</code>
- * environment variable as a means of specifying an HTTP proxy for requests
- * made behind a firewall. This is not natively recognized by the JRE, so
- * this method can be used by command line utilities to configure the JRE
- * before the first request is sent.
+ * The popular libcurl library honors the <code>http_proxy</code>,
+ * <code>https_proxy</code> environment variables as a means of specifying
+ * an HTTP/S proxy for requests made behind a firewall. This is not natively
+ * recognized by the JRE, so this method can be used by command line
+ * utilities to configure the JRE before the first request is sent.
*
* @throws MalformedURLException
- * the value in <code>http_proxy</code> is unsupportable.
+ * the value in <code>http_proxy</code> or
+ * <code>https_proxy</code> is unsupportable.
*/
private static void configureHttpProxy() throws MalformedURLException {
- final String s = System.getenv("http_proxy"); //$NON-NLS-1$
- if (s == null || s.equals("")) //$NON-NLS-1$
- return;
-
- final URL u = new URL((s.indexOf("://") == -1) ? "http://" + s : s); //$NON-NLS-1$ //$NON-NLS-2$
- if (!"http".equals(u.getProtocol())) //$NON-NLS-1$
- throw new MalformedURLException(MessageFormat.format(CLIText.get().invalidHttpProxyOnlyHttpSupported, s));
-
- final String proxyHost = u.getHost();
- final int proxyPort = u.getPort();
-
- System.setProperty("http.proxyHost", proxyHost); //$NON-NLS-1$
- if (proxyPort > 0)
- System.setProperty("http.proxyPort", String.valueOf(proxyPort)); //$NON-NLS-1$
-
- final String userpass = u.getUserInfo();
- if (userpass != null && userpass.contains(":")) { //$NON-NLS-1$
- final int c = userpass.indexOf(':');
- final String user = userpass.substring(0, c);
- final String pass = userpass.substring(c + 1);
- CachedAuthenticator
- .add(new CachedAuthenticator.CachedAuthentication(
- proxyHost, proxyPort, user, pass));
+ for (String protocol : new String[] { "http", "https" }) { //$NON-NLS-1$ //$NON-NLS-2$
+ final String s = System.getenv(protocol + "_proxy"); //$NON-NLS-1$
+ if (s == null || s.equals("")) //$NON-NLS-1$
+ return;
+
+ final URL u = new URL(
+ (s.indexOf("://") == -1) ? protocol + "://" + s : s); //$NON-NLS-1$ //$NON-NLS-2$
+ if (!u.getProtocol().startsWith("http")) //$NON-NLS-1$
+ throw new MalformedURLException(MessageFormat.format(
+ CLIText.get().invalidHttpProxyOnlyHttpSupported, s));
+
+ final String proxyHost = u.getHost();
+ final int proxyPort = u.getPort();
+
+ System.setProperty(protocol + ".proxyHost", proxyHost); //$NON-NLS-1$
+ if (proxyPort > 0)
+ System.setProperty(protocol + ".proxyPort", //$NON-NLS-1$
+ String.valueOf(proxyPort));
+
+ final String userpass = u.getUserInfo();
+ if (userpass != null && userpass.contains(":")) { //$NON-NLS-1$
+ final int c = userpass.indexOf(':');
+ final String user = userpass.substring(0, c);
+ final String pass = userpass.substring(c + 1);
+ CachedAuthenticator.add(
+ new CachedAuthenticator.CachedAuthentication(proxyHost,
+ proxyPort, user, pass));
+ }
+ }
+ }
+
+ /**
+ * Parser for subcommands which doesn't stop parsing on help options and so
+ * proceeds all specified options
+ */
+ static class SubcommandLineParser extends CmdLineParser {
+ public SubcommandLineParser(Object bean) {
+ super(bean);
+ }
+
+ @Override
+ protected boolean containsHelp(String... args) {
+ return false;
}
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java
index 93c4388..e739b58 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java
@@ -120,23 +120,24 @@ class Merge extends TextBuiltin {
throw die(MessageFormat.format(
CLIText.get().refDoesNotExistOrNoCommit, ref));
- Ref oldHead = db.getRef(Constants.HEAD);
- Git git = new Git(db);
- MergeCommand mergeCmd = git.merge().setStrategy(mergeStrategy)
- .setSquash(squash).setFastForward(ff).setCommit(!noCommit);
- if (srcRef != null)
- mergeCmd.include(srcRef);
- else
- mergeCmd.include(src);
+ Ref oldHead = getOldHead();
+ MergeResult result;
+ try (Git git = new Git(db)) {
+ MergeCommand mergeCmd = git.merge().setStrategy(mergeStrategy)
+ .setSquash(squash).setFastForward(ff).setCommit(!noCommit);
+ if (srcRef != null)
+ mergeCmd.include(srcRef);
+ else
+ mergeCmd.include(src);
- if (message != null)
- mergeCmd.setMessage(message);
+ if (message != null)
+ mergeCmd.setMessage(message);
- MergeResult result;
- try {
- result = mergeCmd.call();
- } catch (CheckoutConflictException e) {
- result = new MergeResult(e.getConflictingPaths()); // CHECKOUT_CONFLICT
+ try {
+ result = mergeCmd.call();
+ } catch (CheckoutConflictException e) {
+ result = new MergeResult(e.getConflictingPaths()); // CHECKOUT_CONFLICT
+ }
}
switch (result.getMergeStatus()) {
@@ -147,9 +148,12 @@ class Merge extends TextBuiltin {
break;
case FAST_FORWARD:
ObjectId oldHeadId = oldHead.getObjectId();
- outw.println(MessageFormat.format(CLIText.get().updating, oldHeadId
- .abbreviate(7).name(), result.getNewHead().abbreviate(7)
- .name()));
+ if (oldHeadId != null) {
+ String oldId = oldHeadId.abbreviate(7).name();
+ String newId = result.getNewHead().abbreviate(7).name();
+ outw.println(MessageFormat.format(CLIText.get().updating, oldId,
+ newId));
+ }
outw.println(result.getMergeStatus().toString());
break;
case CHECKOUT_CONFLICT:
@@ -204,14 +208,23 @@ class Merge extends TextBuiltin {
}
}
+ private Ref getOldHead() throws IOException {
+ Ref oldHead = db.getRef(Constants.HEAD);
+ if (oldHead == null) {
+ throw die(CLIText.get().onBranchToBeBorn);
+ }
+ return oldHead;
+ }
+
private boolean isMergedInto(Ref oldHead, AnyObjectId src)
throws IOException {
- RevWalk revWalk = new RevWalk(db);
- ObjectId oldHeadObjectId = oldHead.getPeeledObjectId();
- if (oldHeadObjectId == null)
- oldHeadObjectId = oldHead.getObjectId();
- RevCommit oldHeadCommit = revWalk.lookupCommit(oldHeadObjectId);
- RevCommit srcCommit = revWalk.lookupCommit(src);
- return revWalk.isMergedInto(oldHeadCommit, srcCommit);
+ try (RevWalk revWalk = new RevWalk(db)) {
+ ObjectId oldHeadObjectId = oldHead.getPeeledObjectId();
+ if (oldHeadObjectId == null)
+ oldHeadObjectId = oldHead.getObjectId();
+ RevCommit oldHeadCommit = revWalk.lookupCommit(oldHeadObjectId);
+ RevCommit srcCommit = revWalk.lookupCommit(src);
+ return revWalk.isMergedInto(oldHeadCommit, srcCommit);
+ }
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Push.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Push.java
index 1119337..33ea1de 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Push.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Push.java
@@ -82,6 +82,9 @@ class Push extends TextBuiltin {
@Option(name = "--all")
private boolean all;
+ @Option(name = "--atomic")
+ private boolean atomic;
+
@Option(name = "--tags")
private boolean tags;
@@ -109,27 +112,26 @@ class Push extends TextBuiltin {
@Override
protected void run() throws Exception {
- Git git = new Git(db);
- PushCommand push = git.push();
- push.setDryRun(dryRun);
- push.setForce(force);
- push.setProgressMonitor(new TextProgressMonitor(errw));
- push.setReceivePack(receivePack);
- push.setRefSpecs(refSpecs);
- if (all)
- push.setPushAll();
- if (tags)
- push.setPushTags();
- push.setRemote(remote);
- push.setThin(thin);
- push.setTimeout(timeout);
- Iterable<PushResult> results = push.call();
- for (PushResult result : results) {
- ObjectReader reader = db.newObjectReader();
- try {
- printPushResult(reader, result.getURI(), result);
- } finally {
- reader.release();
+ try (Git git = new Git(db)) {
+ PushCommand push = git.push();
+ push.setDryRun(dryRun);
+ push.setForce(force);
+ push.setProgressMonitor(new TextProgressMonitor(errw));
+ push.setReceivePack(receivePack);
+ push.setRefSpecs(refSpecs);
+ if (all)
+ push.setPushAll();
+ if (tags)
+ push.setPushTags();
+ push.setRemote(remote);
+ push.setThin(thin);
+ push.setAtomic(atomic);
+ push.setTimeout(timeout);
+ Iterable<PushResult> results = push.call();
+ for (PushResult result : results) {
+ try (ObjectReader reader = db.newObjectReader()) {
+ printPushResult(reader, result.getURI(), result);
+ }
}
}
}
@@ -180,15 +182,15 @@ class Push extends TextBuiltin {
switch (rru.getStatus()) {
case OK:
if (rru.isDelete())
- printUpdateLine('-', "[deleted]", null, remoteName, null);
+ printUpdateLine('-', "[deleted]", null, remoteName, null); //$NON-NLS-1$
else {
final Ref oldRef = result.getAdvertisedRef(remoteName);
if (oldRef == null) {
final String summary;
if (remoteName.startsWith(Constants.R_TAGS))
- summary = "[new tag]";
+ summary = "[new tag]"; //$NON-NLS-1$
else
- summary = "[new branch]";
+ summary = "[new branch]"; //$NON-NLS-1$
printUpdateLine('*', summary, srcRef, remoteName, null);
} else {
boolean fastForward = rru.isFastForward();
@@ -204,16 +206,16 @@ class Push extends TextBuiltin {
break;
case NON_EXISTING:
- printUpdateLine('X', "[no match]", null, remoteName, null);
+ printUpdateLine('X', "[no match]", null, remoteName, null); //$NON-NLS-1$
break;
case REJECTED_NODELETE:
- printUpdateLine('!', "[rejected]", null, remoteName,
+ printUpdateLine('!', "[rejected]", null, remoteName, //$NON-NLS-1$
CLIText.get().remoteSideDoesNotSupportDeletingRefs);
break;
case REJECTED_NONFASTFORWARD:
- printUpdateLine('!', "[rejected]", srcRef, remoteName,
+ printUpdateLine('!', "[rejected]", srcRef, remoteName, //$NON-NLS-1$
CLIText.get().nonFastForward);
break;
@@ -221,22 +223,22 @@ class Push extends TextBuiltin {
final String message = MessageFormat.format(
CLIText.get().remoteRefObjectChangedIsNotExpectedOne,
safeAbbreviate(reader, rru.getExpectedOldObjectId()));
- printUpdateLine('!', "[rejected]", srcRef, remoteName, message);
+ printUpdateLine('!', "[rejected]", srcRef, remoteName, message); //$NON-NLS-1$
break;
case REJECTED_OTHER_REASON:
- printUpdateLine('!', "[remote rejected]", srcRef, remoteName, rru
+ printUpdateLine('!', "[remote rejected]", srcRef, remoteName, rru //$NON-NLS-1$
.getMessage());
break;
case UP_TO_DATE:
if (verbose)
- printUpdateLine('=', "[up to date]", srcRef, remoteName, null);
+ printUpdateLine('=', "[up to date]", srcRef, remoteName, null); //$NON-NLS-1$
break;
case NOT_ATTEMPTED:
case AWAITING_REPORT:
- printUpdateLine('?', "[unexpected push-process behavior]", srcRef,
+ printUpdateLine('?', "[unexpected push-process behavior]", srcRef, //$NON-NLS-1$
remoteName, rru.getMessage());
break;
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Reflog.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Reflog.java
index aa90f8d..86a021d 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Reflog.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Reflog.java
@@ -59,13 +59,15 @@ class Reflog extends TextBuiltin {
@Override
protected void run() throws Exception {
- ReflogCommand cmd = new Git(db).reflog();
- if (ref != null)
- cmd.setRef(ref);
- Collection<ReflogEntry> entries = cmd.call();
- int i = 0;
- for (ReflogEntry entry : entries) {
- outw.println(toString(entry, i++));
+ try (Git git = new Git(db)) {
+ ReflogCommand cmd = git.reflog();
+ if (ref != null)
+ cmd.setRef(ref);
+ Collection<ReflogEntry> entries = cmd.call();
+ int i = 0;
+ for (ReflogEntry entry : entries) {
+ outw.println(toString(entry, i++));
+ }
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Remote.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Remote.java
new file mode 100644
index 0000000..24916bd
--- /dev/null
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Remote.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r at zend.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.pgm;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.RemoteAddCommand;
+import org.eclipse.jgit.api.RemoteListCommand;
+import org.eclipse.jgit.api.RemoteRemoveCommand;
+import org.eclipse.jgit.api.RemoteSetUrlCommand;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.pgm.internal.CLIText;
+import org.eclipse.jgit.pgm.opt.CmdLineParser;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.util.io.ThrowingPrintWriter;
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.Option;
+
+ at Command(common = false, usage = "usage_Remote")
+class Remote extends TextBuiltin {
+
+ @Option(name = "--verbose", aliases = { "-v" }, usage = "usage_beVerbose")
+ private boolean verbose = false;
+
+ @Option(name = "--prune", aliases = {
+ "-p" }, usage = "usage_pruneStaleTrackingRefs")
+ private boolean prune;
+
+ @Option(name = "--push", usage = "usage_pushUrls")
+ private boolean push;
+
+ @Argument(index = 0, metaVar = "metaVar_command")
+ private String command;
+
+ @Argument(index = 1, metaVar = "metaVar_remoteName")
+ private String name;
+
+ @Argument(index = 2, metaVar = "metaVar_uriish")
+ private String uri;
+
+ @Override
+ protected void run() throws Exception {
+ try (Git git = new Git(db)) {
+ if (command == null) {
+ RemoteListCommand cmd = git.remoteList();
+ List<RemoteConfig> remotes = cmd.call();
+ print(remotes);
+ } else if ("add".equals(command)) { //$NON-NLS-1$
+ RemoteAddCommand cmd = git.remoteAdd();
+ cmd.setName(name);
+ cmd.setUri(new URIish(uri));
+ cmd.call();
+ } else if ("remove".equals(command) || "rm".equals(command)) { //$NON-NLS-1$ //$NON-NLS-2$
+ RemoteRemoveCommand cmd = git.remoteRemove();
+ cmd.setName(name);
+ cmd.call();
+ } else if ("set-url".equals(command)) { //$NON-NLS-1$
+ RemoteSetUrlCommand cmd = git.remoteSetUrl();
+ cmd.setName(name);
+ cmd.setUri(new URIish(uri));
+ cmd.setPush(push);
+ cmd.call();
+ } else if ("update".equals(command)) { //$NON-NLS-1$
+ // reuse fetch command for basic implementation of remote update
+ Fetch fetch = new Fetch();
+ fetch.init(db, gitdir);
+
+ // redirect the output stream
+ StringWriter osw = new StringWriter();
+ fetch.outw = new ThrowingPrintWriter(osw);
+ // redirect the error stream
+ StringWriter esw = new StringWriter();
+ fetch.errw = new ThrowingPrintWriter(esw);
+
+ List<String> fetchArgs = new ArrayList<>();
+ if (verbose) {
+ fetchArgs.add("--verbose"); //$NON-NLS-1$
+ }
+ if (prune) {
+ fetchArgs.add("--prune"); //$NON-NLS-1$
+ }
+ if (name != null) {
+ fetchArgs.add(name);
+ }
+
+ fetch.execute(fetchArgs.toArray(new String[fetchArgs.size()]));
+
+ // flush the streams
+ fetch.outw.flush();
+ fetch.errw.flush();
+ outw.println(osw.toString());
+ errw.println(esw.toString());
+ } else {
+ throw new JGitInternalException(MessageFormat
+ .format(CLIText.get().unknownSubcommand, command));
+ }
+ }
+ }
+
+ @Override
+ public void printUsage(final String message, final CmdLineParser clp)
+ throws IOException {
+ errw.println(message);
+ errw.println("jgit remote [--verbose (-v)] [--help (-h)]"); //$NON-NLS-1$
+ errw.println("jgit remote add name uri-ish [--help (-h)]"); //$NON-NLS-1$
+ errw.println("jgit remote remove name [--help (-h)]"); //$NON-NLS-1$
+ errw.println("jgit remote rm name [--help (-h)]"); //$NON-NLS-1$
+ errw.println(
+ "jgit remote [--verbose (-v)] update [name] [--prune (-p)] [--help (-h)]"); //$NON-NLS-1$
+ errw.println("jgit remote set-url name uri-ish [--push] [--help (-h)]"); //$NON-NLS-1$
+
+ errw.println();
+ clp.printUsage(errw, getResourceBundle());
+ errw.println();
+
+ errw.flush();
+ }
+
+ private void print(List<RemoteConfig> remotes) throws IOException {
+ for (RemoteConfig remote : remotes) {
+ String remoteName = remote.getName();
+ if (verbose) {
+ List<URIish> fetchURIs = remote.getURIs();
+ List<URIish> pushURIs = remote.getPushURIs();
+
+ String fetchURI = ""; //$NON-NLS-1$
+ if (!fetchURIs.isEmpty()) {
+ fetchURI = fetchURIs.get(0).toString();
+ } else if (!pushURIs.isEmpty()) {
+ fetchURI = pushURIs.get(0).toString();
+ }
+
+ String pushURI = ""; //$NON-NLS-1$
+ if (!pushURIs.isEmpty()) {
+ pushURI = pushURIs.get(0).toString();
+ } else if (!fetchURIs.isEmpty()) {
+ pushURI = fetchURIs.get(0).toString();
+ }
+
+ outw.println(
+ String.format("%s\t%s (fetch)", remoteName, fetchURI)); //$NON-NLS-1$
+ outw.println(
+ String.format("%s\t%s (push)", remoteName, pushURI)); //$NON-NLS-1$
+ } else {
+ outw.println(remoteName);
+ }
+ }
+ }
+
+}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Repo.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Repo.java
index 9b191e6..ea59527 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Repo.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Repo.java
@@ -55,15 +55,19 @@ class Repo extends TextBuiltin {
@Option(name = "--groups", aliases = { "-g" }, usage = "usage_groups")
private String groups = "default"; //$NON-NLS-1$
- @Argument(required = true, usage = "usage_pathToXml")
+ @Argument(required = true, metaVar = "metaVar_path", usage = "usage_pathToXml")
private String path;
+ @Option(name = "--record-remote-branch", usage = "usage_branches")
+ private boolean branches;
+
@Override
protected void run() throws Exception {
new RepoCommand(db)
.setURI(uri)
.setPath(path)
.setGroups(groups)
+ .setRecordRemoteBranch(branches)
.call();
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Reset.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Reset.java
index f4cbcaf..9cee37b 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Reset.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Reset.java
@@ -43,11 +43,15 @@
package org.eclipse.jgit.pgm;
+import java.util.ArrayList;
+import java.util.List;
+
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ResetCommand;
import org.eclipse.jgit.api.ResetCommand.ResetType;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
+import org.kohsuke.args4j.spi.RestOfArgumentsHandler;
@Command(common = true, usage = "usage_reset")
class Reset extends TextBuiltin {
@@ -61,29 +65,40 @@ class Reset extends TextBuiltin {
@Option(name = "--hard", usage = "usage_resetHard")
private boolean hard = false;
- @Argument(required = true, metaVar = "metaVar_name", usage = "usage_reset")
+ @Argument(required = false, index = 0, metaVar = "metaVar_commitish", usage = "usage_resetReference")
private String commit;
+ @Argument(required = false, index = 1, metaVar = "metaVar_paths")
+ @Option(name = "--", metaVar = "metaVar_paths", multiValued = true, handler = RestOfArgumentsHandler.class)
+ private List<String> paths = new ArrayList<>();
+
@Override
protected void run() throws Exception {
- ResetCommand command = new Git(db).reset();
- command.setRef(commit);
- ResetType mode = null;
- if (soft)
- mode = selectMode(mode, ResetType.SOFT);
- if (mixed)
- mode = selectMode(mode, ResetType.MIXED);
- if (hard)
- mode = selectMode(mode, ResetType.HARD);
- if (mode == null)
- throw die("no reset mode set");
- command.setMode(mode);
- command.call();
+ try (Git git = new Git(db)) {
+ ResetCommand command = git.reset();
+ command.setRef(commit);
+ if (paths.size() > 0) {
+ for (String path : paths)
+ command.addPath(path);
+ } else {
+ ResetType mode = null;
+ if (soft)
+ mode = selectMode(mode, ResetType.SOFT);
+ if (mixed)
+ mode = selectMode(mode, ResetType.MIXED);
+ if (hard)
+ mode = selectMode(mode, ResetType.HARD);
+ if (mode == null)
+ throw die("no reset mode set"); //$NON-NLS-1$
+ command.setMode(mode);
+ }
+ command.call();
+ }
}
private static ResetType selectMode(ResetType mode, ResetType want) {
if (mode != null)
- throw die("reset modes are mutually exclusive, select one");
+ throw die("reset modes are mutually exclusive, select one"); //$NON-NLS-1$
return want;
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevParse.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevParse.java
index 5530ac5..c5ecb84 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevParse.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevParse.java
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2009, Daniel Cheng (aka SDiZ) <git at sdiz.net>
* Copyright (C) 2009, Daniel Cheng (aka SDiZ) <j16sdiz+freenet at gmail.com>
+ * Copyright (C) 2015 Thomas Meyer <thomas at m3y3r.de>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -51,14 +52,20 @@ import java.util.List;
import java.util.Map;
import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.Option;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.pgm.internal.CLIText;
+import org.eclipse.jgit.pgm.opt.CmdLineParser;
@Command(usage = "usage_RevParse")
class RevParse extends TextBuiltin {
@Option(name = "--all", usage = "usage_RevParseAll")
- boolean all = false;
+ boolean all;
+
+ @Option(name = "--verify", usage = "usage_RevParseVerify")
+ boolean verify;
@Argument(index = 0, metaVar = "metaVar_commitish")
private final List<ObjectId> commits = new ArrayList<ObjectId>();
@@ -67,11 +74,24 @@ class RevParse extends TextBuiltin {
protected void run() throws Exception {
if (all) {
Map<String, Ref> allRefs = db.getRefDatabase().getRefs(ALL);
- for (final Ref r : allRefs.values())
- outw.println(r.getObjectId().name());
+ for (final Ref r : allRefs.values()) {
+ ObjectId objectId = r.getObjectId();
+ // getRefs skips dangling symrefs, so objectId should never be
+ // null.
+ if (objectId == null) {
+ throw new NullPointerException();
+ }
+ outw.println(objectId.name());
+ }
} else {
- for (final ObjectId o : commits)
+ if (verify && commits.size() > 1) {
+ final CmdLineParser clp = new CmdLineParser(this);
+ throw new CmdLineException(clp, CLIText.get().needSingleRevision);
+ }
+
+ for (final ObjectId o : commits) {
outw.println(o.name());
+ }
}
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Rm.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Rm.java
index 816b310..f4f864b 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Rm.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Rm.java
@@ -63,10 +63,11 @@ class Rm extends TextBuiltin {
@Override
protected void run() throws Exception {
- RmCommand command = new Git(db).rm();
- for (String p : paths)
- command.addFilepattern(p);
- command.call();
+ try (Git git = new Git(db)) {
+ RmCommand command = git.rm();
+ for (String p : paths)
+ command.addFilepattern(p);
+ command.call();
+ }
}
-
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java
index a33a2d4..c5986b0 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java
@@ -184,8 +184,7 @@ class Show extends TextBuiltin {
else
objectId = db.resolve(objectName);
- RevWalk rw = new RevWalk(db);
- try {
+ try (RevWalk rw = new RevWalk(db)) {
RevObject obj = rw.parseAny(objectId);
while (obj instanceof RevTag) {
show((RevTag) obj);
@@ -216,11 +215,9 @@ class Show extends TextBuiltin {
CLIText.get().cannotReadBecause, obj.name(),
obj.getType()));
}
- } finally {
- rw.release();
}
} finally {
- diffFmt.release();
+ diffFmt.close();
}
}
@@ -254,16 +251,17 @@ class Show extends TextBuiltin {
private void show(RevTree obj) throws MissingObjectException,
IncorrectObjectTypeException, CorruptObjectException, IOException {
- final TreeWalk walk = new TreeWalk(db);
- walk.reset();
- walk.addTree(obj);
-
- while (walk.next()) {
- outw.print(walk.getPathString());
- final FileMode mode = walk.getFileMode(0);
- if (mode == FileMode.TREE)
- outw.print("/"); //$NON-NLS-1$
- outw.println();
+ try (final TreeWalk walk = new TreeWalk(db)) {
+ walk.reset();
+ walk.addTree(obj);
+
+ while (walk.next()) {
+ outw.print(walk.getPathString());
+ final FileMode mode = walk.getFileMode(0);
+ if (mode == FileMode.TREE)
+ outw.print("/"); //$NON-NLS-1$
+ outw.println();
+ }
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java
index 2ae950b..6a63221 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Status.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, 2013 François Rey <eclipse.org_ at _francois_._rey_._name>
+ * Copyright (C) 2011, 2015 François Rey <eclipse.org_ at _francois_._rey_._name>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -59,8 +59,14 @@ import org.eclipse.jgit.lib.IndexDiff.StageState;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.pgm.internal.CLIText;
+import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
+import org.kohsuke.args4j.spi.RestOfArgumentsHandler;
+import org.eclipse.jgit.pgm.opt.UntrackedFilesHandler;
+/**
+ * Status command
+ */
@Command(usage = "usage_Status", common = true)
class Status extends TextBuiltin {
@@ -75,17 +81,23 @@ class Status extends TextBuiltin {
@Option(name = "--porcelain", usage = "usage_machineReadableOutput")
protected boolean porcelain;
- @Option(name = "--", metaVar = "metaVar_path", multiValued = true)
+ @Option(name = "--untracked-files", aliases = { "-u", "-uno", "-uall" }, usage = "usage_untrackedFilesMode", handler = UntrackedFilesHandler.class)
+ protected String untrackedFilesMode = "all"; // default value //$NON-NLS-1$
+
+ @Argument(required = false, index = 0, metaVar = "metaVar_paths")
+ @Option(name = "--", metaVar = "metaVar_paths", multiValued = true, handler = RestOfArgumentsHandler.class)
protected List<String> filterPaths;
@Override
protected void run() throws Exception {
- StatusCommand statusCommand = new Git(db).status();
- if (filterPaths != null && filterPaths.size() > 0)
- for (String path : filterPaths)
- statusCommand.addPath(path);
- org.eclipse.jgit.api.Status status = statusCommand.call();
- printStatus(status);
+ try (Git git = new Git(db)) {
+ StatusCommand statusCommand = git.status();
+ if (filterPaths != null && filterPaths.size() > 0)
+ for (String path : filterPaths)
+ statusCommand.addPath(path);
+ org.eclipse.jgit.api.Status status = statusCommand.call();
+ printStatus(status);
+ }
}
private void printStatus(org.eclipse.jgit.api.Status status)
@@ -174,9 +186,12 @@ class Status extends TextBuiltin {
}
// untracked are always at the end of the list
- TreeSet<String> untracked = new TreeSet<String>(status.getUntracked());
- for (String path : untracked)
- printPorcelainLine('?', '?', path);
+ if ("all".equals(untrackedFilesMode)) { //$NON-NLS-1$
+ TreeSet<String> untracked = new TreeSet<String>(
+ status.getUntracked());
+ for (String path : untracked)
+ printPorcelainLine('?', '?', path);
+ }
}
private void printPorcelainLine(char x, char y, String path)
@@ -240,7 +255,7 @@ class Status extends TextBuiltin {
firstHeader = false;
}
int nbUntracked = untracked.size();
- if (nbUntracked > 0) {
+ if (nbUntracked > 0 && ("all".equals(untrackedFilesMode))) { //$NON-NLS-1$
if (!firstHeader)
printSectionHeader(""); //$NON-NLS-1$
printSectionHeader(CLIText.get().untrackedFiles);
@@ -250,11 +265,13 @@ class Status extends TextBuiltin {
protected void printSectionHeader(String pattern, Object... arguments)
throws IOException {
- outw.println(CLIText.formatLine(MessageFormat
- .format(pattern, arguments)));
- if (!pattern.equals("")) //$NON-NLS-1$
- outw.println(CLIText.formatLine("")); //$NON-NLS-1$
- outw.flush();
+ if (!porcelain) {
+ outw.println(CLIText.formatLine(MessageFormat.format(pattern,
+ arguments)));
+ if (!pattern.equals("")) //$NON-NLS-1$
+ outw.println(CLIText.formatLine("")); //$NON-NLS-1$
+ outw.flush();
+ }
}
protected int printList(Collection<String> list) throws IOException {
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java
index a90d4c4..45fceb5 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java
@@ -79,26 +79,28 @@ class Tag extends TextBuiltin {
@Override
protected void run() throws Exception {
- Git git = new Git(db);
- if (tagName != null) {
- TagCommand command = git.tag().setForceUpdate(force)
- .setMessage(message).setName(tagName);
+ try (Git git = new Git(db)) {
+ if (tagName != null) {
+ TagCommand command = git.tag().setForceUpdate(force)
+ .setMessage(message).setName(tagName);
- if (object != null) {
- RevWalk walk = new RevWalk(db);
- command.setObjectId(walk.parseAny(object));
- }
- try {
- command.call();
- } catch (RefAlreadyExistsException e) {
- throw die(MessageFormat.format(CLIText.get().tagAlreadyExists,
- tagName));
- }
- } else {
- ListTagCommand command = git.tagList();
- List<Ref> list = command.call();
- for (Ref ref : list) {
- outw.println(Repository.shortenRefName(ref.getName()));
+ if (object != null) {
+ try (RevWalk walk = new RevWalk(db)) {
+ command.setObjectId(walk.parseAny(object));
+ }
+ }
+ try {
+ command.call();
+ } catch (RefAlreadyExistsException e) {
+ throw die(MessageFormat.format(CLIText.get().tagAlreadyExists,
+ tagName));
+ }
+ } else {
+ ListTagCommand command = git.tagList();
+ List<Ref> list = command.call();
+ for (Ref ref : list) {
+ outw.println(Repository.shortenRefName(ref.getName()));
+ }
}
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java
index 8e8b82f..0dc549c 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java
@@ -56,7 +56,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
import java.text.MessageFormat;
import java.util.ResourceBundle;
@@ -108,14 +107,6 @@ public abstract class TextBuiltin {
protected OutputStream outs;
/**
- * Stream to output to, typically this is standard output.
- *
- * @deprecated Use outw instead
- */
- @Deprecated
- protected PrintWriter out;
-
- /**
* Error writer, typically this is standard error.
*
* @since 3.4
@@ -172,7 +163,6 @@ public abstract class TextBuiltin {
outputEncoding));
else
outbufw = new BufferedWriter(new OutputStreamWriter(outs));
- out = new PrintWriter(outbufw);
outw = new ThrowingPrintWriter(outbufw);
BufferedWriter errbufw;
if (outputEncoding != null)
@@ -222,17 +212,20 @@ public abstract class TextBuiltin {
*/
protected void parseArguments(final String[] args) throws IOException {
final CmdLineParser clp = new CmdLineParser(this);
+ help = containsHelp(args);
try {
clp.parseArgument(args);
} catch (CmdLineException err) {
- if (!help) {
- this.errw.println(MessageFormat.format(CLIText.get().fatalError, err.getMessage()));
- throw die(true);
+ this.errw.println(CLIText.fatalError(err.getMessage()));
+ if (help) {
+ printUsage("", clp); //$NON-NLS-1$
}
+ throw die(true, err);
}
if (help) {
- printUsageAndExit(clp);
+ printUsage("", clp); //$NON-NLS-1$
+ throw new TerminatedByHelpException();
}
argWalk = clp.getRevWalkGently();
@@ -256,6 +249,20 @@ public abstract class TextBuiltin {
* @throws IOException
*/
public void printUsageAndExit(final String message, final CmdLineParser clp) throws IOException {
+ printUsage(message, clp);
+ throw die(true);
+ }
+
+ /**
+ * @param message
+ * non null
+ * @param clp
+ * parser used to print options
+ * @throws IOException
+ * @since 4.2
+ */
+ protected void printUsage(final String message, final CmdLineParser clp)
+ throws IOException {
errw.println(message);
errw.print("jgit "); //$NON-NLS-1$
errw.print(commandName);
@@ -267,12 +274,19 @@ public abstract class TextBuiltin {
errw.println();
errw.flush();
- throw die(true);
}
/**
- * @return the resource bundle that will be passed to args4j for purpose
- * of string localization
+ * @return error writer, typically this is standard error.
+ * @since 4.2
+ */
+ public ThrowingPrintWriter getErrorWriter() {
+ return errw;
+ }
+
+ /**
+ * @return the resource bundle that will be passed to args4j for purpose of
+ * string localization
*/
protected ResourceBundle getResourceBundle() {
return CLIText.get().resourceBundle();
@@ -334,6 +348,19 @@ public abstract class TextBuiltin {
return new Die(aborted);
}
+ /**
+ * @param aborted
+ * boolean indicating that the execution has been aborted before
+ * running
+ * @param cause
+ * why the command has failed.
+ * @return a runtime exception the caller is expected to throw
+ * @since 4.2
+ */
+ protected static Die die(boolean aborted, final Throwable cause) {
+ return new Die(aborted, cause);
+ }
+
String abbreviateRef(String dst, boolean abbreviateRemote) {
if (dst.startsWith(R_HEADS))
dst = dst.substring(R_HEADS.length());
@@ -343,4 +370,36 @@ public abstract class TextBuiltin {
dst = dst.substring(R_REMOTES.length());
return dst;
}
+
+ /**
+ * @param args
+ * non null
+ * @return true if the given array contains help option
+ * @since 4.2
+ */
+ public static boolean containsHelp(String[] args) {
+ for (String str : args) {
+ if (str.equals("-h") || str.equals("--help")) { //$NON-NLS-1$ //$NON-NLS-2$
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Exception thrown by {@link TextBuiltin} if it proceeds 'help' option
+ *
+ * @since 4.2
+ */
+ public static class TerminatedByHelpException extends Die {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Default constructor
+ */
+ public TerminatedByHelpException() {
+ super(true);
+ }
+
+ }
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java
index 91b5917..c96f2c1 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java
@@ -136,7 +136,7 @@ class DiffAlgorithms extends TextBuiltin {
protected void run() throws Exception {
mxBean = ManagementFactory.getThreadMXBean();
if (!mxBean.isCurrentThreadCpuTimeSupported())
- throw die("Current thread CPU time not supported on this JRE");
+ throw die("Current thread CPU time not supported on this JRE"); //$NON-NLS-1$
if (gitDirs.isEmpty()) {
RepositoryBuilder rb = new RepositoryBuilder() //
@@ -173,10 +173,9 @@ class DiffAlgorithms extends TextBuiltin {
int maxN = 0;
AbbreviatedObjectId startId;
- ObjectReader or = db.newObjectReader();
- try {
+ try (ObjectReader or = db.newObjectReader();
+ RevWalk rw = new RevWalk(or)) {
final MutableObjectId id = new MutableObjectId();
- RevWalk rw = new RevWalk(or);
TreeWalk tw = new TreeWalk(or);
tw.setFilter(TreeFilter.ANY_DIFF);
tw.setRecursive(true);
@@ -232,8 +231,6 @@ class DiffAlgorithms extends TextBuiltin {
if (count > 0 && files > count)
break;
}
- } finally {
- or.release();
}
Collections.sort(all, new Comparator<Test>() {
@@ -245,23 +242,24 @@ class DiffAlgorithms extends TextBuiltin {
}
});
- if (db.getDirectory() != null) {
- String name = db.getDirectory().getName();
- File parent = db.getDirectory().getParentFile();
+ File directory = db.getDirectory();
+ if (directory != null) {
+ String name = directory.getName();
+ File parent = directory.getParentFile();
if (name.equals(Constants.DOT_GIT) && parent != null)
name = parent.getName();
- outw.println(name + ": start at " + startId.name());
+ outw.println(name + ": start at " + startId.name()); //$NON-NLS-1$
}
- outw.format(" %12d files, %8d commits\n", valueOf(files),
+ outw.format(" %12d files, %8d commits\n", valueOf(files), //$NON-NLS-1$
valueOf(commits));
- outw.format(" N=%10d min lines, %8d max lines\n", valueOf(minN),
+ outw.format(" N=%10d min lines, %8d max lines\n", valueOf(minN), //$NON-NLS-1$
valueOf(maxN));
- outw.format("%-25s %12s ( %12s %12s )\n", //
- "Algorithm", "Time(ns)", "Time(ns) on", "Time(ns) on");
- outw.format("%-25s %12s ( %12s %12s )\n", //
- "", "", "N=" + minN, "N=" + maxN);
+ outw.format("%-25s %12s ( %12s %12s )\n", //$NON-NLS-1$
+ "Algorithm", "Time(ns)", "Time(ns) on", "Time(ns) on"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ outw.format("%-25s %12s ( %12s %12s )\n", //$NON-NLS-1$
+ "", "", "N=" + minN, "N=" + maxN); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
outw.println("-----------------------------------------------------" //$NON-NLS-1$
+ "----------------"); //$NON-NLS-1$
@@ -337,9 +335,9 @@ class DiffAlgorithms extends TextBuiltin {
}
}
} catch (IllegalArgumentException e) {
- throw die("Cannot determine names", e);
+ throw die("Cannot determine names", e); //$NON-NLS-1$
} catch (IllegalAccessException e) {
- throw die("Cannot determine names", e);
+ throw die("Cannot determine names", e); //$NON-NLS-1$
}
return all;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
index afa4696..e2f93f4 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
@@ -117,9 +117,12 @@ class RebuildCommitGraph extends TextBuiltin {
@Override
protected void run() throws Exception {
if (!really && !db.getRefDatabase().getRefs(ALL).isEmpty()) {
+ File directory = db.getDirectory();
+ String absolutePath = directory == null ? "null" //$NON-NLS-1$
+ : directory.getAbsolutePath();
errw.println(
MessageFormat.format(CLIText.get().fatalThisProgramWillDestroyTheRepository
- , db.getDirectory().getAbsolutePath(), REALLY));
+ , absolutePath, REALLY));
throw die(CLIText.get().needApprovalToDestroyCurrentRepository);
}
if (!refList.isFile())
@@ -134,12 +137,12 @@ class RebuildCommitGraph extends TextBuiltin {
}
private void recreateCommitGraph() throws IOException {
- final RevWalk rw = new RevWalk(db);
final Map<ObjectId, ToRewrite> toRewrite = new HashMap<ObjectId, ToRewrite>();
List<ToRewrite> queue = new ArrayList<ToRewrite>();
- final BufferedReader br = new BufferedReader(new InputStreamReader(
- new FileInputStream(graph), Constants.CHARSET));
- try {
+ try (RevWalk rw = new RevWalk(db);
+ final BufferedReader br = new BufferedReader(
+ new InputStreamReader(new FileInputStream(graph),
+ Constants.CHARSET))) {
String line;
while ((line = br.readLine()) != null) {
final String[] parts = line.split("[ \t]{1,}"); //$NON-NLS-1$
@@ -162,52 +165,52 @@ class RebuildCommitGraph extends TextBuiltin {
toRewrite.put(oldId, t);
queue.add(t);
}
- } finally {
- br.close();
}
- pm.beginTask("Rewriting commits", queue.size());
- final ObjectInserter oi = db.newObjectInserter();
- final ObjectId emptyTree = oi.insert(Constants.OBJ_TREE, new byte[] {});
- final PersonIdent me = new PersonIdent("jgit rebuild-commitgraph", //$NON-NLS-1$
- "rebuild-commitgraph at localhost"); //$NON-NLS-1$
- while (!queue.isEmpty()) {
- final ListIterator<ToRewrite> itr = queue
- .listIterator(queue.size());
- queue = new ArrayList<ToRewrite>();
- REWRITE: while (itr.hasPrevious()) {
- final ToRewrite t = itr.previous();
- final ObjectId[] newParents = new ObjectId[t.oldParents.length];
- for (int k = 0; k < t.oldParents.length; k++) {
- final ToRewrite p = toRewrite.get(t.oldParents[k]);
- if (p != null) {
- if (p.newId == null) {
- // Must defer until after the parent is rewritten.
- queue.add(t);
- continue REWRITE;
+ pm.beginTask("Rewriting commits", queue.size()); //$NON-NLS-1$
+ try (ObjectInserter oi = db.newObjectInserter()) {
+ final ObjectId emptyTree = oi.insert(Constants.OBJ_TREE,
+ new byte[] {});
+ final PersonIdent me = new PersonIdent("jgit rebuild-commitgraph", //$NON-NLS-1$
+ "rebuild-commitgraph at localhost"); //$NON-NLS-1$
+ while (!queue.isEmpty()) {
+ final ListIterator<ToRewrite> itr = queue
+ .listIterator(queue.size());
+ queue = new ArrayList<ToRewrite>();
+ REWRITE: while (itr.hasPrevious()) {
+ final ToRewrite t = itr.previous();
+ final ObjectId[] newParents = new ObjectId[t.oldParents.length];
+ for (int k = 0; k < t.oldParents.length; k++) {
+ final ToRewrite p = toRewrite.get(t.oldParents[k]);
+ if (p != null) {
+ if (p.newId == null) {
+ // Must defer until after the parent is
+ // rewritten.
+ queue.add(t);
+ continue REWRITE;
+ } else {
+ newParents[k] = p.newId;
+ }
} else {
- newParents[k] = p.newId;
+ // We have the old parent object. Use it.
+ //
+ newParents[k] = t.oldParents[k];
}
- } else {
- // We have the old parent object. Use it.
- //
- newParents[k] = t.oldParents[k];
}
- }
- final CommitBuilder newc = new CommitBuilder();
- newc.setTreeId(emptyTree);
- newc.setAuthor(new PersonIdent(me, new Date(t.commitTime)));
- newc.setCommitter(newc.getAuthor());
- newc.setParentIds(newParents);
- newc.setMessage("ORIGINAL " + t.oldId.name() + "\n"); //$NON-NLS-2$
- t.newId = oi.insert(newc);
- rewrites.put(t.oldId, t.newId);
- pm.update(1);
+ final CommitBuilder newc = new CommitBuilder();
+ newc.setTreeId(emptyTree);
+ newc.setAuthor(new PersonIdent(me, new Date(t.commitTime)));
+ newc.setCommitter(newc.getAuthor());
+ newc.setParentIds(newParents);
+ newc.setMessage("ORIGINAL " + t.oldId.name() + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ t.newId = oi.insert(newc);
+ rewrites.put(t.oldId, t.newId);
+ pm.update(1);
+ }
}
+ oi.flush();
}
- oi.flush();
- oi.release();
pm.endTask();
}
@@ -275,11 +278,11 @@ class RebuildCommitGraph extends TextBuiltin {
}
private Map<String, Ref> computeNewRefs() throws IOException {
- final RevWalk rw = new RevWalk(db);
final Map<String, Ref> refs = new HashMap<String, Ref>();
- final BufferedReader br = new BufferedReader(new InputStreamReader(
- new FileInputStream(refList), Constants.CHARSET));
- try {
+ try (RevWalk rw = new RevWalk(db);
+ BufferedReader br = new BufferedReader(
+ new InputStreamReader(new FileInputStream(refList),
+ Constants.CHARSET))) {
String line;
while ((line = br.readLine()) != null) {
final String[] parts = line.split("[ \t]{1,}"); //$NON-NLS-1$
@@ -302,9 +305,6 @@ class RebuildCommitGraph extends TextBuiltin {
refs.put(name, new ObjectIdRef.Unpeeled(Ref.Storage.PACKED,
name, id));
}
- } finally {
- rw.release();
- br.close();
}
return refs;
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildRefTree.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildRefTree.java
new file mode 100644
index 0000000..78ca1a7
--- /dev/null
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildRefTree.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.pgm.debug;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jgit.internal.storage.reftree.RefTree;
+import org.eclipse.jgit.internal.storage.reftree.RefTreeDatabase;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.pgm.Command;
+import org.eclipse.jgit.pgm.TextBuiltin;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+ at Command(usage = "usage_RebuildRefTree")
+class RebuildRefTree extends TextBuiltin {
+ private String txnNamespace;
+ private String txnCommitted;
+
+ @Override
+ protected void run() throws Exception {
+ try (ObjectReader reader = db.newObjectReader();
+ RevWalk rw = new RevWalk(reader);
+ ObjectInserter inserter = db.newObjectInserter()) {
+ RefDatabase refDb = db.getRefDatabase();
+ if (refDb instanceof RefTreeDatabase) {
+ RefTreeDatabase d = (RefTreeDatabase) refDb;
+ refDb = d.getBootstrap();
+ txnNamespace = d.getTxnNamespace();
+ txnCommitted = d.getTxnCommitted();
+ } else {
+ RefTreeDatabase d = new RefTreeDatabase(db, refDb);
+ txnNamespace = d.getTxnNamespace();
+ txnCommitted = d.getTxnCommitted();
+ }
+
+ errw.format("Rebuilding %s from %s", //$NON-NLS-1$
+ txnCommitted, refDb.getClass().getSimpleName());
+ errw.println();
+ errw.flush();
+
+ CommitBuilder b = new CommitBuilder();
+ Ref ref = refDb.exactRef(txnCommitted);
+ RefUpdate update = refDb.newUpdate(txnCommitted, true);
+ ObjectId oldTreeId;
+
+ if (ref != null && ref.getObjectId() != null) {
+ ObjectId oldId = ref.getObjectId();
+ update.setExpectedOldObjectId(oldId);
+ b.setParentId(oldId);
+ oldTreeId = rw.parseCommit(oldId).getTree();
+ } else {
+ update.setExpectedOldObjectId(ObjectId.zeroId());
+ oldTreeId = ObjectId.zeroId();
+ }
+
+ RefTree tree = rebuild(refDb.getRefs(RefDatabase.ALL));
+ b.setTreeId(tree.writeTree(inserter));
+ b.setAuthor(new PersonIdent(db));
+ b.setCommitter(b.getAuthor());
+ if (b.getTreeId().equals(oldTreeId)) {
+ return;
+ }
+
+ update.setNewObjectId(inserter.insert(b));
+ inserter.flush();
+
+ RefUpdate.Result result = update.update(rw);
+ switch (result) {
+ case NEW:
+ case FAST_FORWARD:
+ break;
+ default:
+ throw die(String.format("%s: %s", update.getName(), result)); //$NON-NLS-1$
+ }
+ }
+ }
+
+ private RefTree rebuild(Map<String, Ref> refMap) {
+ RefTree tree = RefTree.newEmptyTree();
+ List<org.eclipse.jgit.internal.storage.reftree.Command> cmds
+ = new ArrayList<>();
+
+ for (Ref r : refMap.values()) {
+ if (r.getName().equals(txnCommitted)
+ || r.getName().startsWith(txnNamespace)) {
+ continue;
+ }
+ cmds.add(new org.eclipse.jgit.internal.storage.reftree.Command(
+ null,
+ db.peel(r)));
+ }
+ tree.apply(cmds);
+ return tree;
+ }
+}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java
index 7b5cdbf..150fe6e 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/ShowPackDelta.java
@@ -75,7 +75,10 @@ class ShowPackDelta extends TextBuiltin {
@Override
protected void run() throws Exception {
ObjectReader reader = db.newObjectReader();
- RevObject obj = new RevWalk(reader).parseAny(objectId);
+ RevObject obj;
+ try (RevWalk rw = new RevWalk(reader)) {
+ obj = rw.parseAny(objectId);
+ }
byte[] delta = getDelta(reader, obj);
// We're crossing our fingers that this will be a delta. Double
@@ -84,9 +87,9 @@ class ShowPackDelta extends TextBuiltin {
long size = reader.getObjectSize(obj, obj.getType());
try {
if (BinaryDelta.getResultSize(delta) != size)
- throw die("Object " + obj.name() + " is not a delta");
+ throw die("Object " + obj.name() + " is not a delta"); //$NON-NLS-1$ //$NON-NLS-2$
} catch (ArrayIndexOutOfBoundsException bad) {
- throw die("Object " + obj.name() + " is not a delta");
+ throw die("Object " + obj.name() + " is not a delta"); //$NON-NLS-1$ //$NON-NLS-2$
}
outw.println(BinaryDelta.format(delta));
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java
index 4205140..062f4e7 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java
@@ -300,11 +300,10 @@ class TextHashFunctions extends TextBuiltin {
long fileCnt = 0;
long lineCnt = 0;
- ObjectReader or = db.newObjectReader();
- try {
- final MutableObjectId id = new MutableObjectId();
+ try (ObjectReader or = db.newObjectReader();
RevWalk rw = new RevWalk(or);
- TreeWalk tw = new TreeWalk(or);
+ TreeWalk tw = new TreeWalk(or)) {
+ final MutableObjectId id = new MutableObjectId();
tw.reset(rw.parseTree(db.resolve(Constants.HEAD)));
tw.setRecursive(true);
@@ -340,21 +339,20 @@ class TextHashFunctions extends TextBuiltin {
for (Function fun : all)
testOne(fun, txt, lines, cnt);
}
- } finally {
- or.release();
}
- if (db.getDirectory() != null) {
- String name = db.getDirectory().getName();
- File parent = db.getDirectory().getParentFile();
+ File directory = db.getDirectory();
+ if (directory != null) {
+ String name = directory.getName();
+ File parent = directory.getParentFile();
if (name.equals(Constants.DOT_GIT) && parent != null)
name = parent.getName();
outw.println(name + ":"); //$NON-NLS-1$
}
- outw.format(" %6d files; %5d avg. unique lines/file\n", //
+ outw.format(" %6d files; %5d avg. unique lines/file\n", //$NON-NLS-1$
valueOf(fileCnt), //
valueOf(lineCnt / fileCnt));
- outw.format("%-20s %-15s %9s\n", "Hash", "Fold", "Max Len");
+ outw.format("%-20s %-15s %9s\n", "Hash", "Fold", "Max Len"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
outw.println("-----------------------------------------------"); //$NON-NLS-1$
String lastHashName = null;
for (Function fun : all) {
@@ -407,9 +405,9 @@ class TextHashFunctions extends TextBuiltin {
}
}
} catch (IllegalArgumentException e) {
- throw new RuntimeException("Cannot determine names", e);
+ throw new RuntimeException("Cannot determine names", e); //$NON-NLS-1$
} catch (IllegalAccessException e) {
- throw new RuntimeException("Cannot determine names", e);
+ throw new RuntimeException("Cannot determine names", e); //$NON-NLS-1$
}
List<Function> all = new ArrayList<Function>();
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
index e7d995e..2812137 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
@@ -74,15 +74,31 @@ public class CLIText extends TranslationBundle {
return MessageFormat.format(get().lineFormat, line);
}
+ /**
+ * Format the given argument as fatal error using the format defined by
+ * {@link #fatalError} ("fatal: " by default).
+ *
+ * @param message
+ * the message to format
+ * @return the formatted line
+ * @since 4.2
+ */
+ public static String fatalError(String message) {
+ return MessageFormat.format(get().fatalError, message);
+ }
+
// @formatter:off
/***/ public String alreadyOnBranch;
/***/ public String alreadyUpToDate;
+ /***/ public String answerNo;
+ /***/ public String answerYes;
/***/ public String authorInfo;
/***/ public String averageMSPerRead;
/***/ public String branchAlreadyExists;
/***/ public String branchCreatedFrom;
/***/ public String branchDetachedHEAD;
/***/ public String branchIsNotAnAncestorOfYourCurrentHEAD;
+ /***/ public String branchNameRequired;
/***/ public String branchNotFound;
/***/ public String cacheTreePathInfo;
/***/ public String configFileNotFound;
@@ -182,15 +198,18 @@ public class CLIText extends TranslationBundle {
/***/ public String metaVar_uriish;
/***/ public String metaVar_url;
/***/ public String metaVar_user;
+ /***/ public String metaVar_values;
/***/ public String metaVar_version;
/***/ public String mostCommonlyUsedCommandsAre;
/***/ public String needApprovalToDestroyCurrentRepository;
+ /***/ public String needSingleRevision;
/***/ public String noGitRepositoryConfigured;
/***/ public String noNamesFound;
/***/ public String noSuchFile;
/***/ public String noSuchRemoteRef;
/***/ public String noTREESectionInIndex;
/***/ public String nonFastForward;
+ /***/ public String noSystemConsoleAvailable;
/***/ public String notABranch;
/***/ public String notACommit;
/***/ public String notAGitRepository;
@@ -198,6 +217,7 @@ public class CLIText extends TranslationBundle {
/***/ public String notARevision;
/***/ public String notATree;
/***/ public String notAValidRefName;
+ /***/ public String notAValidCommitName;
/***/ public String notAnIndexFile;
/***/ public String notAnObject;
/***/ public String notFound;
@@ -208,6 +228,7 @@ public class CLIText extends TranslationBundle {
/***/ public String onBranch;
/***/ public String onlyOneMetaVarExpectedIn;
/***/ public String onlyOneOfIncludeOnlyAllInteractiveCanBeUsed;
+ /***/ public String password;
/***/ public String pathspecDidNotMatch;
/***/ public String pushTo;
/***/ public String pathsRequired;
@@ -241,8 +262,10 @@ public class CLIText extends TranslationBundle {
/***/ public String treeIsRequired;
/***/ public char[] unknownIoErrorStdout;
/***/ public String unknownMergeStrategy;
+ /***/ public String unknownSubcommand;
/***/ public String unmergedPaths;
/***/ public String unsupportedOperation;
/***/ public String untrackedFiles;
/***/ public String updating;
+ /***/ public String usernameFor;
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/AbstractTreeIteratorHandler.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/AbstractTreeIteratorHandler.java
index 087dbb0..6b8a61d 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/AbstractTreeIteratorHandler.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/AbstractTreeIteratorHandler.java
@@ -109,7 +109,7 @@ public class AbstractTreeIteratorHandler extends
try {
dirc = DirCache.read(new File(name), FS.DETECTED);
} catch (IOException e) {
- throw new CmdLineException(MessageFormat.format(CLIText.get().notAnIndexFile, name), e);
+ throw new CmdLineException(clp, MessageFormat.format(CLIText.get().notAnIndexFile, name), e);
}
setter.addValue(new DirCacheIterator(dirc));
return 1;
@@ -119,23 +119,20 @@ public class AbstractTreeIteratorHandler extends
try {
id = clp.getRepository().resolve(name);
} catch (IOException e) {
- throw new CmdLineException(e.getMessage());
+ throw new CmdLineException(clp, e.getMessage());
}
if (id == null)
- throw new CmdLineException(MessageFormat.format(CLIText.get().notATree, name));
+ throw new CmdLineException(clp, MessageFormat.format(CLIText.get().notATree, name));
final CanonicalTreeParser p = new CanonicalTreeParser();
- final ObjectReader curs = clp.getRepository().newObjectReader();
- try {
+ try (ObjectReader curs = clp.getRepository().newObjectReader()) {
p.reset(curs, clp.getRevWalk().parseTree(id));
} catch (MissingObjectException e) {
- throw new CmdLineException(MessageFormat.format(CLIText.get().notATree, name));
+ throw new CmdLineException(clp, MessageFormat.format(CLIText.get().notATree, name));
} catch (IncorrectObjectTypeException e) {
- throw new CmdLineException(MessageFormat.format(CLIText.get().notATree, name));
+ throw new CmdLineException(clp, MessageFormat.format(CLIText.get().notATree, name));
} catch (IOException e) {
- throw new CmdLineException(MessageFormat.format(CLIText.get().cannotReadBecause, name, e.getMessage()));
- } finally {
- curs.release();
+ throw new CmdLineException(clp, MessageFormat.format(CLIText.get().cannotReadBecause, name, e.getMessage()));
}
setter.addValue(p);
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java
index 3f77aa6..b531ba6 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/CmdLineParser.java
@@ -43,19 +43,18 @@
package org.eclipse.jgit.pgm.opt;
+import java.io.IOException;
+import java.io.Writer;
import java.lang.reflect.Field;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ResourceBundle;
-import org.kohsuke.args4j.Argument;
-import org.kohsuke.args4j.CmdLineException;
-import org.kohsuke.args4j.IllegalAnnotationError;
-import org.kohsuke.args4j.NamedOptionDef;
-import org.kohsuke.args4j.Option;
-import org.kohsuke.args4j.OptionDef;
-import org.kohsuke.args4j.spi.OptionHandler;
-import org.kohsuke.args4j.spi.Setter;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.pgm.Die;
import org.eclipse.jgit.pgm.TextBuiltin;
import org.eclipse.jgit.pgm.internal.CLIText;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -63,6 +62,15 @@ import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.IllegalAnnotationError;
+import org.kohsuke.args4j.NamedOptionDef;
+import org.kohsuke.args4j.Option;
+import org.kohsuke.args4j.OptionDef;
+import org.kohsuke.args4j.spi.OptionHandler;
+import org.kohsuke.args4j.spi.RestOfArgumentsHandler;
+import org.kohsuke.args4j.spi.Setter;
/**
* Extended command line parser which handles --foo=value arguments.
@@ -80,12 +88,17 @@ public class CmdLineParser extends org.kohsuke.args4j.CmdLineParser {
registerHandler(RefSpec.class, RefSpecHandler.class);
registerHandler(RevCommit.class, RevCommitHandler.class);
registerHandler(RevTree.class, RevTreeHandler.class);
+ registerHandler(List.class, OptionWithValuesListHandler.class);
}
private final Repository db;
private RevWalk walk;
+ private boolean seenHelp;
+
+ private TextBuiltin cmd;
+
/**
* Creates a new command line owner that parses arguments/options and set
* them into the given object.
@@ -117,8 +130,12 @@ public class CmdLineParser extends org.kohsuke.args4j.CmdLineParser {
*/
public CmdLineParser(final Object bean, Repository repo) {
super(bean);
- if (repo == null && bean instanceof TextBuiltin)
- repo = ((TextBuiltin) bean).getRepository();
+ if (bean instanceof TextBuiltin) {
+ cmd = (TextBuiltin) bean;
+ }
+ if (repo == null && cmd != null) {
+ repo = cmd.getRepository();
+ }
this.db = repo;
}
@@ -143,9 +160,75 @@ public class CmdLineParser extends org.kohsuke.args4j.CmdLineParser {
}
tmp.add(str);
+
+ if (containsHelp(args)) {
+ // suppress exceptions on required parameters if help is present
+ seenHelp = true;
+ // stop argument parsing here
+ break;
+ }
+ }
+ List<OptionHandler> backup = null;
+ if (seenHelp) {
+ backup = unsetRequiredOptions();
}
- super.parseArgument(tmp.toArray(new String[tmp.size()]));
+ try {
+ super.parseArgument(tmp.toArray(new String[tmp.size()]));
+ } catch (Die e) {
+ if (!seenHelp) {
+ throw e;
+ }
+ printToErrorWriter(CLIText.fatalError(e.getMessage()));
+ } finally {
+ // reset "required" options to defaults for correct command printout
+ if (backup != null && !backup.isEmpty()) {
+ restoreRequiredOptions(backup);
+ }
+ seenHelp = false;
+ }
+ }
+
+ private void printToErrorWriter(String error) {
+ if (cmd == null) {
+ System.err.println(error);
+ } else {
+ try {
+ cmd.getErrorWriter().println(error);
+ } catch (IOException e1) {
+ System.err.println(error);
+ }
+ }
+ }
+
+ private List<OptionHandler> unsetRequiredOptions() {
+ List<OptionHandler> options = getOptions();
+ List<OptionHandler> backup = new ArrayList<>(options);
+ for (Iterator<OptionHandler> iterator = options.iterator(); iterator
+ .hasNext();) {
+ OptionHandler handler = iterator.next();
+ if (handler.option instanceof NamedOptionDef
+ && handler.option.required()) {
+ iterator.remove();
+ }
+ }
+ return backup;
+ }
+
+ private void restoreRequiredOptions(List<OptionHandler> backup) {
+ List<OptionHandler> options = getOptions();
+ options.clear();
+ options.addAll(backup);
+ }
+
+ /**
+ * @param args
+ * non null
+ * @return true if the given array contains help option
+ * @since 4.2
+ */
+ protected boolean containsHelp(final String... args) {
+ return TextBuiltin.containsHelp(args);
}
/**
@@ -181,7 +264,7 @@ public class CmdLineParser extends org.kohsuke.args4j.CmdLineParser {
return walk;
}
- static class MyOptionDef extends OptionDef {
+ class MyOptionDef extends OptionDef {
public MyOptionDef(OptionDef o) {
super(o.usage(), o.metaVar(), o.required(), o.handler(), o
@@ -201,6 +284,11 @@ public class CmdLineParser extends org.kohsuke.args4j.CmdLineParser {
return metaVar();
}
}
+
+ @Override
+ public boolean required() {
+ return seenHelp ? false : super.required();
+ }
}
@Override
@@ -211,4 +299,55 @@ public class CmdLineParser extends org.kohsuke.args4j.CmdLineParser {
return super.createOptionHandler(new MyOptionDef(o), setter);
}
+
+ @SuppressWarnings("unchecked")
+ private List<OptionHandler> getOptions() {
+ List<OptionHandler> options = null;
+ try {
+ Field field = org.kohsuke.args4j.CmdLineParser.class
+ .getDeclaredField("options"); //$NON-NLS-1$
+ field.setAccessible(true);
+ options = (List<OptionHandler>) field.get(this);
+ } catch (NoSuchFieldException | SecurityException
+ | IllegalArgumentException | IllegalAccessException e) {
+ // ignore
+ }
+ if (options == null) {
+ return Collections.emptyList();
+ }
+ return options;
+ }
+
+ @Override
+ public void printSingleLineUsage(Writer w, ResourceBundle rb) {
+ List<OptionHandler> options = getOptions();
+ if (options.isEmpty()) {
+ super.printSingleLineUsage(w, rb);
+ return;
+ }
+ List<OptionHandler> backup = new ArrayList<>(options);
+ boolean changed = sortRestOfArgumentsHandlerToTheEnd(options);
+ try {
+ super.printSingleLineUsage(w, rb);
+ } finally {
+ if (changed) {
+ options.clear();
+ options.addAll(backup);
+ }
+ }
+ }
+
+ private boolean sortRestOfArgumentsHandlerToTheEnd(
+ List<OptionHandler> options) {
+ for (int i = 0; i < options.size(); i++) {
+ OptionHandler handler = options.get(i);
+ if (handler instanceof RestOfArgumentsHandler
+ || handler instanceof PathTreeFilterHandler) {
+ options.remove(i);
+ options.add(handler);
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/ObjectIdHandler.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/ObjectIdHandler.java
index fa24d4b..364809d 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/ObjectIdHandler.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/ObjectIdHandler.java
@@ -86,14 +86,14 @@ public class ObjectIdHandler extends OptionHandler<ObjectId> {
try {
id = clp.getRepository().resolve(name);
} catch (IOException e) {
- throw new CmdLineException(e.getMessage());
+ throw new CmdLineException(clp, e.getMessage());
}
if (id != null) {
setter.addValue(id);
return 1;
}
- throw new CmdLineException(MessageFormat.format(CLIText.get().notAnObject, name));
+ throw new CmdLineException(clp, MessageFormat.format(CLIText.get().notAnObject, name));
}
@Override
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/OptionWithValuesListHandler.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/OptionWithValuesListHandler.java
new file mode 100644
index 0000000..3de7a81
--- /dev/null
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/OptionWithValuesListHandler.java
@@ -0,0 +1,52 @@
+package org.eclipse.jgit.pgm.opt;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jgit.pgm.internal.CLIText;
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.CmdLineParser;
+import org.kohsuke.args4j.OptionDef;
+import org.kohsuke.args4j.spi.OptionHandler;
+import org.kohsuke.args4j.spi.Parameters;
+import org.kohsuke.args4j.spi.Setter;
+
+/**
+ * Handler which allows to parse option with few values
+ *
+ * @since 4.2
+ */
+public class OptionWithValuesListHandler extends OptionHandler<List<?>> {
+
+ /**
+ * @param parser
+ * @param option
+ * @param setter
+ */
+ public OptionWithValuesListHandler(CmdLineParser parser,
+ OptionDef option, Setter<List<?>> setter) {
+ super(parser, option, setter);
+ }
+
+ @Override
+ public int parseArguments(Parameters params) throws CmdLineException {
+ final List<String> list = new ArrayList<>();
+ for (int idx = 0; idx < params.size(); idx++) {
+ final String p;
+ try {
+ p = params.getParameter(idx);
+ } catch (CmdLineException cle) {
+ break;
+ }
+ list.add(p);
+ }
+ setter.addValue(list);
+ return list.size();
+ }
+
+ @Override
+ public String getDefaultMetaVariable() {
+ return CLIText.get().metaVar_values;
+ }
+
+}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevCommitHandler.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevCommitHandler.java
index b1be128..9ae56e4 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevCommitHandler.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevCommitHandler.java
@@ -96,8 +96,10 @@ public class RevCommitHandler extends OptionHandler<RevCommit> {
final int dot2 = name.indexOf(".."); //$NON-NLS-1$
if (dot2 != -1) {
if (!option.isMultiValued())
- throw new CmdLineException(MessageFormat.format(CLIText.get().onlyOneMetaVarExpectedIn
- , option.metaVar(), name));
+ throw new CmdLineException(clp,
+ MessageFormat.format(
+ CLIText.get().onlyOneMetaVarExpectedIn,
+ option.metaVar(), name));
final String left = name.substring(0, dot2);
final String right = name.substring(dot2 + 2);
@@ -116,20 +118,20 @@ public class RevCommitHandler extends OptionHandler<RevCommit> {
try {
id = clp.getRepository().resolve(name);
} catch (IOException e) {
- throw new CmdLineException(e.getMessage());
+ throw new CmdLineException(clp, e.getMessage());
}
if (id == null)
- throw new CmdLineException(MessageFormat.format(CLIText.get().notACommit, name));
+ throw new CmdLineException(clp, MessageFormat.format(CLIText.get().notACommit, name));
final RevCommit c;
try {
c = clp.getRevWalk().parseCommit(id);
} catch (MissingObjectException e) {
- throw new CmdLineException(MessageFormat.format(CLIText.get().notACommit, name));
+ throw new CmdLineException(clp, MessageFormat.format(CLIText.get().notACommit, name));
} catch (IncorrectObjectTypeException e) {
- throw new CmdLineException(MessageFormat.format(CLIText.get().notACommit, name));
+ throw new CmdLineException(clp, MessageFormat.format(CLIText.get().notACommit, name));
} catch (IOException e) {
- throw new CmdLineException(MessageFormat.format(CLIText.get().cannotReadBecause, name, e.getMessage()));
+ throw new CmdLineException(clp, MessageFormat.format(CLIText.get().cannotReadBecause, name, e.getMessage()));
}
if (interesting)
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevTreeHandler.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevTreeHandler.java
index eb155af..e2879e0 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevTreeHandler.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/RevTreeHandler.java
@@ -89,20 +89,20 @@ public class RevTreeHandler extends OptionHandler<RevTree> {
try {
id = clp.getRepository().resolve(name);
} catch (IOException e) {
- throw new CmdLineException(e.getMessage());
+ throw new CmdLineException(clp, e.getMessage());
}
if (id == null)
- throw new CmdLineException(MessageFormat.format(CLIText.get().notATree, name));
+ throw new CmdLineException(clp, MessageFormat.format(CLIText.get().notATree, name));
final RevTree c;
try {
c = clp.getRevWalk().parseTree(id);
} catch (MissingObjectException e) {
- throw new CmdLineException(MessageFormat.format(CLIText.get().notATree, name));
+ throw new CmdLineException(clp, MessageFormat.format(CLIText.get().notATree, name));
} catch (IncorrectObjectTypeException e) {
- throw new CmdLineException(MessageFormat.format(CLIText.get().notATree, name));
+ throw new CmdLineException(clp, MessageFormat.format(CLIText.get().notATree, name));
} catch (IOException e) {
- throw new CmdLineException(MessageFormat.format(CLIText.get().cannotReadBecause, name, e.getMessage()));
+ throw new CmdLineException(clp, MessageFormat.format(CLIText.get().cannotReadBecause, name, e.getMessage()));
}
setter.addValue(c);
return 1;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/SubcommandHandler.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/SubcommandHandler.java
index c62ef0d..96f3ed0 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/SubcommandHandler.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/SubcommandHandler.java
@@ -63,6 +63,8 @@ import org.eclipse.jgit.pgm.internal.CLIText;
* we can execute at runtime with the remaining arguments of the parser.
*/
public class SubcommandHandler extends OptionHandler<TextBuiltin> {
+ private final org.eclipse.jgit.pgm.opt.CmdLineParser clp;
+
/**
* Create a new handler for the command name.
* <p>
@@ -75,6 +77,7 @@ public class SubcommandHandler extends OptionHandler<TextBuiltin> {
public SubcommandHandler(final CmdLineParser parser,
final OptionDef option, final Setter<? super TextBuiltin> setter) {
super(parser, option, setter);
+ clp = (org.eclipse.jgit.pgm.opt.CmdLineParser) parser;
}
@Override
@@ -82,7 +85,7 @@ public class SubcommandHandler extends OptionHandler<TextBuiltin> {
final String name = params.getParameter(0);
final CommandRef cr = CommandCatalog.get(name);
if (cr == null)
- throw new CmdLineException(MessageFormat.format(
+ throw new CmdLineException(clp, MessageFormat.format(
CLIText.get().notAJgitCommand, name));
// Force option parsing to stop. Everything after us should
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/ObjectIdHandler.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/UntrackedFilesHandler.java
similarity index 53%
copy from org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/ObjectIdHandler.java
copy to org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/UntrackedFilesHandler.java
index fa24d4b..c4e8b05 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/ObjectIdHandler.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/opt/UntrackedFilesHandler.java
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2009, Google Inc.
- * Copyright (C) 2008, Shawn O. Pearce <spearce at spearce.org>
+ * Copyright (C) 2015 Zend Technologies Ltd. and others
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -41,63 +40,75 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-
package org.eclipse.jgit.pgm.opt;
-import java.io.IOException;
-import java.text.MessageFormat;
-
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.OptionDef;
-import org.kohsuke.args4j.spi.OptionHandler;
import org.kohsuke.args4j.spi.Parameters;
import org.kohsuke.args4j.spi.Setter;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.pgm.internal.CLIText;
+import org.kohsuke.args4j.spi.StringOptionHandler;
/**
- * Custom argument handler {@link ObjectId} from string values.
- * <p>
- * Assumes the parser has been initialized with a Repository.
+ * Special handler for the <code>--untracked-files</code> option of the
+ * <code>status</code> command.
+ *
+ * The following rules apply:
+ * <ul>
+ * <li>If no mode is given, i.e. just <code>--untracked-files</code> is passed,
+ * then it is the same as <code>--untracked-files=all</code></li>
+ * <li>If the <code>-u</code> alias is passed then it is the same as
+ * <code>--untracked-files</code></li>
+ * <li>If the <code>-uno</code> alias is passed then it is the same as
+ * <code>--untracked-files=no</code></li>
+ * <li>If the <code>-uall</code> alias is passed then it is the same as
+ * <code>--untracked-files=all</code></li>
+ * </ul>
+ *
+ * @since 4.0
*/
-public class ObjectIdHandler extends OptionHandler<ObjectId> {
- private final org.eclipse.jgit.pgm.opt.CmdLineParser clp;
+public class UntrackedFilesHandler extends StringOptionHandler {
/**
- * Create a new handler for the command name.
- * <p>
- * This constructor is used only by args4j.
- *
* @param parser
+ * The parser to which this handler belongs to.
* @param option
+ * The annotation.
* @param setter
+ * Object to be used for setting value.
*/
- public ObjectIdHandler(final CmdLineParser parser, final OptionDef option,
- final Setter<? super ObjectId> setter) {
+ public UntrackedFilesHandler(CmdLineParser parser, OptionDef option,
+ Setter<? super String> setter) {
super(parser, option, setter);
- clp = (org.eclipse.jgit.pgm.opt.CmdLineParser) parser;
}
@Override
- public int parseArguments(final Parameters params) throws CmdLineException {
- final String name = params.getParameter(0);
- final ObjectId id;
- try {
- id = clp.getRepository().resolve(name);
- } catch (IOException e) {
- throw new CmdLineException(e.getMessage());
- }
- if (id != null) {
- setter.addValue(id);
+ public int parseArguments(Parameters params) throws CmdLineException {
+ String alias = params.getParameter(-1);
+ if ("-u".equals(alias)) { //$NON-NLS-1$
+ setter.addValue("all"); //$NON-NLS-1$
+ return 0;
+ } else if ("-uno".equals(alias)) { //$NON-NLS-1$
+ setter.addValue("no"); //$NON-NLS-1$
+ return 0;
+ } else if ("-uall".equals(alias)) { //$NON-NLS-1$
+ setter.addValue("all"); //$NON-NLS-1$
+ return 0;
+ } else if (params.size() == 0) {
+ setter.addValue("all"); //$NON-NLS-1$
+ return 0;
+ } else if (params.size() == 1) {
+ String mode = params.getParameter(0);
+ if ("no".equals(mode) || "all".equals(mode)) { //$NON-NLS-1$ //$NON-NLS-2$
+ setter.addValue(mode);
+ } else {
+ throw new CmdLineException(owner, String.format(
+ "Invalid untracked files mode '%s'", mode)); //$NON-NLS-1$
+ }
return 1;
+ } else {
+ return super.parseArguments(params);
}
-
- throw new CmdLineException(MessageFormat.format(CLIText.get().notAnObject, name));
}
- @Override
- public String getDefaultMetaVariable() {
- return CLIText.get().metaVar_object;
- }
-}
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit.test/.classpath b/org.eclipse.jgit.test/.classpath
index ea41285..8b81bf5 100644
--- a/org.eclipse.jgit.test/.classpath
+++ b/org.eclipse.jgit.test/.classpath
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry excluding="**/*.idx|**/*.pack" kind="src" path="tst"/>
+ <classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="tst-rsrc"/>
<classpathentry kind="src" path="exttst"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs
index 5fb6234..dcc0d3a 100644
--- a/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.test/.settings/org.eclipse.jdt.core.prefs
@@ -7,9 +7,9 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
@@ -111,7 +111,7 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
-org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.compiler.source=1.7
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
diff --git a/org.eclipse.jgit.test/BUCK b/org.eclipse.jgit.test/BUCK
new file mode 100644
index 0000000..3df3336
--- /dev/null
+++ b/org.eclipse.jgit.test/BUCK
@@ -0,0 +1,95 @@
+PKG = 'tst/org/eclipse/jgit/'
+HELPERS = glob(['src/**/*.java']) + [PKG + c for c in [
+ 'api/AbstractRemoteCommandTest.java',
+ 'diff/AbstractDiffTestCase.java',
+ 'internal/storage/file/GcTestCase.java',
+ 'internal/storage/file/PackIndexTestCase.java',
+ 'internal/storage/file/XInputStream.java',
+ 'nls/GermanTranslatedBundle.java',
+ 'nls/MissingPropertyBundle.java',
+ 'nls/NoPropertiesBundle.java',
+ 'nls/NonTranslatedBundle.java',
+ 'revwalk/RevQueueTestCase.java',
+ 'revwalk/RevWalkTestCase.java',
+ 'transport/SpiTransport.java',
+ 'treewalk/FileTreeIteratorWithTimeControl.java',
+ 'treewalk/filter/AlwaysCloneTreeFilter.java',
+ 'test/resources/SampleDataRepositoryTestCase.java',
+ 'util/CPUTimeStopWatch.java',
+ 'util/io/Strings.java',
+]]
+
+DATA = [
+ PKG + 'lib/empty.gitindex.dat',
+ PKG + 'lib/sorttest.gitindex.dat',
+]
+
+TESTS = glob(
+ ['tst/**/*.java'],
+ excludes = HELPERS + DATA,
+)
+
+DEPS = {
+ PKG + 'nls/RootLocaleTest.java': [
+ '//org.eclipse.jgit.pgm:pgm',
+ '//org.eclipse.jgit.ui:ui',
+ ],
+}
+
+for src in TESTS:
+ name = src[len('tst/'):len(src)-len('.java')].replace('/', '.')
+ labels = []
+ if name.startswith('org.eclipse.jgit.'):
+ l = name[len('org.eclipse.jgit.'):]
+ if l.startswith('internal.storage.'):
+ l = l[len('internal.storage.'):]
+ i = l.find('.')
+ if i > 0:
+ labels.append(l[:i])
+ else:
+ labels.append(i)
+ if 'lib' not in labels:
+ labels.append('lib')
+
+ java_test(
+ name = name,
+ labels = labels,
+ srcs = [src],
+ deps = [
+ ':helpers',
+ ':tst_rsrc',
+ '//org.eclipse.jgit:jgit',
+ '//org.eclipse.jgit.junit:junit',
+ '//lib:hamcrest-core',
+ '//lib:hamcrest-library',
+ '//lib:javaewah',
+ '//lib:junit',
+ '//lib:slf4j-api',
+ '//lib:slf4j-simple',
+ ] + DEPS.get(src, []),
+ source_under_test = ['//org.eclipse.jgit:jgit'],
+ vm_args = ['-Xmx256m', '-Dfile.encoding=UTF-8'],
+ )
+
+java_library(
+ name = 'helpers',
+ srcs = HELPERS,
+ resources = DATA,
+ deps = [
+ '//org.eclipse.jgit:jgit',
+ '//org.eclipse.jgit.junit:junit',
+ '//lib:junit',
+ ],
+)
+
+prebuilt_jar(
+ name = 'tst_rsrc',
+ binary_jar = ':tst_rsrc_jar',
+)
+
+genrule(
+ name = 'tst_rsrc_jar',
+ cmd = 'cd $SRCDIR/tst-rsrc ; zip -qr $OUT .',
+ srcs = glob(['tst-rsrc/**']),
+ out = 'tst_rsrc.jar',
+)
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index 1c67bab..9c0d760 100644
--- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
@@ -2,53 +2,56 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Bundle-SymbolicName: org.eclipse.jgit.test
-Bundle-Version: 3.7.1.201504261725-r
+Bundle-Version: 4.2.0.201601211800-r
Bundle-Localization: plugin
Bundle-Vendor: %provider_name
Bundle-ActivationPolicy: lazy
-Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Import-Package: com.googlecode.javaewah;version="[0.7.9,0.8.0)",
- org.eclipse.jgit.api;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.api.errors;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.attributes;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.awtui;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.blame;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.console;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.diff;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.dircache;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.errors;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.events;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.fnmatch;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.gitrepo;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.ignore;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.ignore.internal;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.internal;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.internal.storage.dfs;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.internal.storage.file;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.internal.storage.pack;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.junit;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.lib;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.merge;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.nls;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.notes;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.patch;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.pgm;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.pgm.internal;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.revplot;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.revwalk;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.revwalk.filter;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.storage.file;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.storage.pack;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.submodule;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.transport;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.transport.http;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.treewalk;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.treewalk.filter;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.util;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.util.io;version="[3.7.1,3.8.0)",
+ org.eclipse.jgit.api;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.api.errors;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.attributes;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.awtui;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.blame;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.diff;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.dircache;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.errors;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.events;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.fnmatch;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.gitrepo;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.hooks;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.ignore;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.ignore.internal;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.internal;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.internal.storage.dfs;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.internal.storage.file;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.internal.storage.pack;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.internal.storage.reftree;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.junit;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.lib;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.merge;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.nls;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.notes;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.patch;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.pgm;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.pgm.internal;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.revplot;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.revwalk;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.revwalk.filter;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.storage.file;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.storage.pack;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.submodule;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.transport;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.transport.http;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.transport.resolver;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.treewalk;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.treewalk.filter;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.util;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.util.io;version="[4.2.0,4.3.0)",
org.hamcrest;version="[1.1.0,2.0.0)",
org.junit;version="[4.4.0,5.0.0)",
org.junit.experimental.theories;version="[4.4.0,5.0.0)",
org.junit.runner;version="[4.4.0,5.0.0)",
- org.junit.runners;version="[4.11.0,5.0.0)"
+ org.junit.runners;version="[4.11.0,5.0.0)",
+ org.slf4j;version="[1.7.2,2.0.0)"
Require-Bundle: org.hamcrest.core;bundle-version="[1.1.0,2.0.0)"
diff --git a/org.eclipse.jgit.test/build.properties b/org.eclipse.jgit.test/build.properties
index afc4855..786046c 100644
--- a/org.eclipse.jgit.test/build.properties
+++ b/org.eclipse.jgit.test/build.properties
@@ -4,3 +4,4 @@ source.. = tst/,\
bin.includes = META-INF/,\
.,\
plugin.properties
+additional.bundles = org.apache.log4j
diff --git a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/ignore/CGitVsJGitRandomIgnorePatternTest.java b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/ignore/CGitVsJGitRandomIgnorePatternTest.java
new file mode 100644
index 0000000..db5f1b2
--- /dev/null
+++ b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/ignore/CGitVsJGitRandomIgnorePatternTest.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2015, Sebastien Arod <sebastien.arod at gmail.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.ignore;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.file.Files;
+import java.nio.file.StandardOpenOption;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+import org.eclipse.jgit.api.Git;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * This test generates random ignore patterns and random path and compares the
+ * output of Cgit check-ignore to the output of {@link FastIgnoreRule}.
+ */
+public class CGitVsJGitRandomIgnorePatternTest {
+
+ private static class PseudoRandomPatternGenerator {
+
+ private static final int DEFAULT_MAX_FRAGMENTS_PER_PATTERN = 15;
+
+ /**
+ * Generates 75% Special fragments and 25% "standard" characters
+ */
+ private static final double DEFAULT_SPECIAL_FRAGMENTS_FREQUENCY = 0.75d;
+
+ private static final List<String> SPECIAL_FRAGMENTS = Arrays.asList(
+ "\\", "!", "#", "[", "]", "|", "/", "*", "?", "{", "}", "(",
+ ")", "\\d", "(", "**", "[a\\]]", "\\ ", "+", "-", "^", "$", ".",
+ ":", "=", "[[:", ":]]"
+
+ );
+
+ private static final String STANDARD_CHARACTERS = new String(
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
+
+ private final Random random = new Random();
+
+ private final int maxFragmentsPerPattern;
+
+ private final double specialFragmentsFrequency;
+
+ public PseudoRandomPatternGenerator() {
+ this(DEFAULT_MAX_FRAGMENTS_PER_PATTERN,
+ DEFAULT_SPECIAL_FRAGMENTS_FREQUENCY);
+ }
+
+ public PseudoRandomPatternGenerator(int maxFragmentsPerPattern,
+ double specialFragmentsFrequency) {
+ this.maxFragmentsPerPattern = maxFragmentsPerPattern;
+ this.specialFragmentsFrequency = specialFragmentsFrequency;
+ }
+
+ public String nextRandomString() {
+ StringBuilder builder = new StringBuilder();
+ int length = randomFragmentCount();
+ for (int i = 0; i < length; i++) {
+ if (useSpecialFragment()) {
+ builder.append(randomSpecialFragment());
+ } else {
+ builder.append(randomStandardCharacters());
+ }
+
+ }
+ return builder.toString();
+ }
+
+ private int randomFragmentCount() {
+ // We want at least one fragment
+ return 1 + random.nextInt(maxFragmentsPerPattern - 1);
+ }
+
+ private char randomStandardCharacters() {
+ return STANDARD_CHARACTERS
+ .charAt(random.nextInt(STANDARD_CHARACTERS.length()));
+ }
+
+ private boolean useSpecialFragment() {
+ return random.nextDouble() < specialFragmentsFrequency;
+ }
+
+ private String randomSpecialFragment() {
+ return SPECIAL_FRAGMENTS
+ .get(random.nextInt(SPECIAL_FRAGMENTS.size()));
+ }
+ }
+
+ @SuppressWarnings("serial")
+ public static class CgitFatalException extends Exception {
+
+ public CgitFatalException(int cgitExitCode, String pattern, String path,
+ String cgitStdError) {
+ super("CgitFatalException (" + cgitExitCode + ") for pattern:["
+ + pattern + "] and path:[" + path + "]\n" + cgitStdError);
+ }
+
+ }
+
+ public static class CGitIgnoreRule {
+
+ private File gitDir;
+
+ private String pattern;
+
+ public CGitIgnoreRule(File gitDir, String pattern)
+ throws UnsupportedEncodingException, IOException {
+ this.gitDir = gitDir;
+ this.pattern = pattern;
+ Files.write(new File(gitDir, ".gitignore").toPath(),
+ (pattern + "\n").getBytes("UTF-8"),
+ StandardOpenOption.CREATE,
+ StandardOpenOption.TRUNCATE_EXISTING,
+ StandardOpenOption.WRITE);
+ }
+
+ public boolean isMatch(String path)
+ throws IOException, InterruptedException, CgitFatalException {
+ Process proc = startCgitCheckIgnore(path);
+
+ String cgitStdOutput = readProcessStream(proc.getInputStream());
+ String cgitStdError = readProcessStream(proc.getErrorStream());
+
+ int cgitExitCode = proc.waitFor();
+
+ if (cgitExitCode == 128) {
+ throw new CgitFatalException(cgitExitCode, pattern, path,
+ cgitStdError);
+ }
+ return !cgitStdOutput.startsWith("::");
+ }
+
+ private Process startCgitCheckIgnore(String path) throws IOException {
+ // Use --stdin instead of using argument otherwise paths starting
+ // with "-" were interpreted as
+ // options by git check-ignore
+ String[] command = new String[] { "git", "check-ignore",
+ "--no-index", "-v", "-n", "--stdin" };
+ Process proc = Runtime.getRuntime().exec(command, new String[0],
+ gitDir);
+ OutputStream out = proc.getOutputStream();
+ out.write((path + "\n").getBytes("UTF-8"));
+ out.flush();
+ out.close();
+ return proc;
+ }
+
+ private String readProcessStream(InputStream processStream)
+ throws IOException {
+ try (BufferedReader stdOut = new BufferedReader(
+ new InputStreamReader(processStream))) {
+
+ StringBuilder out = new StringBuilder();
+ String s;
+ while ((s = stdOut.readLine()) != null) {
+ out.append(s);
+ }
+ return out.toString();
+ }
+ }
+ }
+
+ private static final int NB_PATTERN = 1000;
+
+ private static final int PATH_PER_PATTERN = 1000;
+
+ @Test
+ public void testRandomPatterns() throws Exception {
+ // Initialize new git repo
+ File gitDir = Files.createTempDirectory("jgit").toFile();
+ Git.init().setDirectory(gitDir).call();
+ PseudoRandomPatternGenerator generator = new PseudoRandomPatternGenerator();
+
+ // Generate random patterns and paths
+ for (int i = 0; i < NB_PATTERN; i++) {
+ String pattern = generator.nextRandomString();
+
+ FastIgnoreRule jgitIgnoreRule = new FastIgnoreRule(pattern);
+ CGitIgnoreRule cgitIgnoreRule = new CGitIgnoreRule(gitDir, pattern);
+
+ // Test path with pattern as path
+ assertCgitAndJgitMatch(pattern, jgitIgnoreRule, cgitIgnoreRule,
+ pattern);
+
+ for (int p = 0; p < PATH_PER_PATTERN; p++) {
+ String path = generator.nextRandomString();
+ assertCgitAndJgitMatch(pattern, jgitIgnoreRule, cgitIgnoreRule,
+ path);
+ }
+ }
+ }
+
+ @SuppressWarnings({ "boxing" })
+ private void assertCgitAndJgitMatch(String pattern,
+ FastIgnoreRule jgitIgnoreRule, CGitIgnoreRule cgitIgnoreRule,
+ String pathToTest) throws IOException, InterruptedException {
+
+ try {
+ boolean cgitMatch = cgitIgnoreRule.isMatch(pathToTest);
+ boolean jgitMatch = jgitIgnoreRule.isMatch(pathToTest,
+ pathToTest.endsWith("/"));
+ if (cgitMatch != jgitMatch) {
+ System.err.println(
+ buildAssertionToAdd(pattern, pathToTest, cgitMatch));
+ }
+ Assert.assertEquals("jgit:" + jgitMatch + " <> cgit:" + cgitMatch
+ + " for pattern:[" + pattern + "] and path:[" + pathToTest
+ + "]", cgitMatch, jgitMatch);
+ } catch (CgitFatalException e) {
+ // Lots of generated patterns or path are rejected by Cgit with a
+ // fatal error. We want to ignore them.
+ }
+ }
+
+ private String buildAssertionToAdd(String pattern, String pathToTest,
+ boolean cgitMatch) {
+ return "assertMatch(" + toJavaString(pattern) + ", "
+ + toJavaString(pathToTest) + ", " + cgitMatch
+ + " /*cgit result*/);";
+ }
+
+ private String toJavaString(String pattern2) {
+ return "\"" + pattern2.replace("\\", "\\\\").replace("\"", "\\\"")
+ + "\"";
+ }
+}
diff --git a/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 7).launch b/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 7).launch
index af009c0..a83fabb 100644
--- a/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 7).launch
+++ b/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 7).launch
@@ -18,7 +18,6 @@
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6" path="1" type="4"/>
"/>
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath">
<memento exportedEntriesOnly="false" project="org.eclipse.jgit.test"/>
</runtimeClasspathEntry>
"/>
-<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry path="3" projectName="org.eclipse.jgit.java7" type="1"/>
"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
diff --git a/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 8).launch b/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 8) (de).launch
similarity index 90%
copy from org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 8).launch
copy to org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 8) (de).launch
index 04aa3ea..f12a529 100644
--- a/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 8).launch
+++ b/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 8) (de).launch
@@ -7,6 +7,9 @@
<listEntry value="2"/>
</listAttribute>
<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+<mapAttribute key="org.eclipse.debug.core.environmentVariables">
+<mapEntry key="LANG" value="de_DE.UTF-8"/>
+</mapAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
@@ -19,7 +22,6 @@
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6" path="1" type="4"/>
"/>
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath">
<memento exportedEntriesOnly="false" project="org.eclipse.jgit.test"/>
</runtimeClasspathEntry>
"/>
-<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry path="3" projectName="org.eclipse.jgit.java7" type="1"/>
"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
diff --git a/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 8).launch b/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 8).launch
index 04aa3ea..b221a11 100644
--- a/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 8).launch
+++ b/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 8).launch
@@ -19,7 +19,6 @@
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6" path="1" type="4"/>
"/>
<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath">
<memento exportedEntriesOnly="false" project="org.eclipse.jgit.test"/>
</runtimeClasspathEntry>
"/>
-<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<runtimeClasspathEntry path="3" projectName="org.eclipse.jgit.java7" type="1"/>
"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
diff --git a/org.eclipse.jgit.test/org.eclipse.jgit.test-WalkEncryptionTest-Proxy.launch b/org.eclipse.jgit.test/org.eclipse.jgit.test-WalkEncryptionTest-Proxy.launch
new file mode 100644
index 0000000..fe3a013
--- /dev/null
+++ b/org.eclipse.jgit.test/org.eclipse.jgit.test-WalkEncryptionTest-Proxy.launch
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.m2e.Maven2LaunchConfigurationType">
+<booleanAttribute key="M2_DEBUG_OUTPUT" value="false"/>
+<stringAttribute key="M2_GOALS" value="test --define test=WalkEncryptionTest --define http_proxy=http://proxy:3128"/>
+<booleanAttribute key="M2_NON_RECURSIVE" value="false"/>
+<booleanAttribute key="M2_OFFLINE" value="false"/>
+<stringAttribute key="M2_PROFILES" value=""/>
+<listAttribute key="M2_PROPERTIES"/>
+<stringAttribute key="M2_RUNTIME" value="EMBEDDED"/>
+<booleanAttribute key="M2_SKIP_TESTS" value="false"/>
+<intAttribute key="M2_THREADS" value="1"/>
+<booleanAttribute key="M2_UPDATE_SNAPSHOTS" value="false"/>
+<stringAttribute key="M2_USER_SETTINGS" value=""/>
+<booleanAttribute key="M2_WORKSPACE_RESOLUTION" value="false"/>
+<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
+<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/java-8-oracle"/>
+<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${workspace_loc:/org.eclipse.jgit.test}"/>
+</launchConfiguration>
diff --git a/org.eclipse.jgit.test/org.eclipse.jgit.test-WalkEncryptionTest.launch b/org.eclipse.jgit.test/org.eclipse.jgit.test-WalkEncryptionTest.launch
new file mode 100644
index 0000000..3b4a5a2
--- /dev/null
+++ b/org.eclipse.jgit.test/org.eclipse.jgit.test-WalkEncryptionTest.launch
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.m2e.Maven2LaunchConfigurationType">
+<booleanAttribute key="M2_DEBUG_OUTPUT" value="false"/>
+<stringAttribute key="M2_GOALS" value="test --define test=WalkEncryptionTest --activate-profiles test.long"/>
+<booleanAttribute key="M2_NON_RECURSIVE" value="false"/>
+<booleanAttribute key="M2_OFFLINE" value="false"/>
+<stringAttribute key="M2_PROFILES" value=""/>
+<listAttribute key="M2_PROPERTIES"/>
+<stringAttribute key="M2_RUNTIME" value="EMBEDDED"/>
+<booleanAttribute key="M2_SKIP_TESTS" value="false"/>
+<intAttribute key="M2_THREADS" value="1"/>
+<booleanAttribute key="M2_UPDATE_SNAPSHOTS" value="false"/>
+<stringAttribute key="M2_USER_SETTINGS" value=""/>
+<booleanAttribute key="M2_WORKSPACE_RESOLUTION" value="false"/>
+<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
+<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/java-8-oracle"/>
+<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${workspace_loc:/org.eclipse.jgit.test}"/>
+</launchConfiguration>
diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml
index 7c72b65..6065b5c 100644
--- a/org.eclipse.jgit.test/pom.xml
+++ b/org.eclipse.jgit.test/pom.xml
@@ -52,7 +52,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>3.7.1.201504261725-r</version>
+ <version>4.2.0.201601211800-r</version>
</parent>
<artifactId>org.eclipse.jgit.test</artifactId>
@@ -62,22 +62,6 @@
JUnit tests for the core library.
</description>
- <profiles>
- <profile>
- <id>jgit.java7</id>
- <activation>
- <jdk>[1.7,)</jdk>
- </activation>
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jgit</groupId>
- <artifactId>org.eclipse.jgit.java7</artifactId>
- <version>${project.version}</version>
- </dependency>
- </dependencies>
- </profile>
- </profiles>
-
<dependencies>
<dependency>
<groupId>junit</groupId>
@@ -85,6 +69,16 @@
<scope>test</scope>
</dependency>
+ <!-- Optional security provider for encryption tests. -->
+ <!-- See https://dev.eclipse.org/ipzilla/show_bug.cgi?id=9554 -->
+ <!-- See https://bugs.eclipse.org/bugs/show_bug.cgi?id=467064 -->
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ <version>1.52</version>
+ <scope>test</scope>
+ </dependency>
+
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
@@ -112,18 +106,31 @@
<dependency>
<groupId>org.eclipse.jgit</groupId>
- <artifactId>org.eclipse.jgit.console</artifactId>
- <version>${project.version}</version>
- </dependency>
-
- <dependency>
- <groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.pgm</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
+ <profiles>
+ <!-- Profile provides a property which enables long running tests. -->
+ <profile>
+ <id>test.long</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <argLine>-Djgit.test.long=true</argLine>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
<build>
+ <sourceDirectory>src/</sourceDirectory>
<testSourceDirectory>tst/</testSourceDirectory>
<testResources>
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/Sets.java b/org.eclipse.jgit.test/src/org/eclipse/jgit/lib/Sets.java
similarity index 95%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/api/Sets.java
rename to org.eclipse.jgit.test/src/org/eclipse/jgit/lib/Sets.java
index edfab55..7316857 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/Sets.java
+++ b/org.eclipse.jgit.test/src/org/eclipse/jgit/lib/Sets.java
@@ -41,13 +41,13 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.api;
+package org.eclipse.jgit.lib;
import java.util.HashSet;
import java.util.Set;
-class Sets {
- static <T> Set<T> of(T... elements) {
+public class Sets {
+ public static <T> Set<T> of(T... elements) {
Set<T> ret = new HashSet<T>();
for (T element : elements)
ret.add(element);
diff --git a/org.eclipse.jgit.test/tst-rsrc/jgit-s3-config.disabled.properties b/org.eclipse.jgit.test/tst-rsrc/jgit-s3-config.disabled.properties
new file mode 100644
index 0000000..d540977
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/jgit-s3-config.disabled.properties
@@ -0,0 +1,48 @@
+#
+# See WalkEncryptionTest.java
+#
+# This file is a template for test configuration file used by WalkEncryptionTest.
+# To be active, this file must have the following hard coded name: jgit-s3-config.properties
+# To be active, this file must be discovered by WalkEncryptionTest from one of these locations:
+# * ${user.home}/jgit-s3-config.properties
+# * ${user.dir}/jgit-s3-config.properties
+# * ${user.dir}/tst-rsrc/jgit-s3-config.properties
+# When this file is missing, tests in WalkEncryptionTest will not run, only report a warning.
+#
+
+#
+# WalkEncryptionTest requires amazon s3 test bucket setup.
+#
+# Test bucket setup instructions:
+#
+# Create IAM user:
+# http://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html
+# * user name: jgit.eclipse.org
+#
+# Configure IAM user S3 bucket access
+# http://docs.aws.amazon.com/AmazonS3/latest/dev/example-policies-s3.html
+# * attach S3 user policy to user account: jgit-s3-config.policy.user.json
+#
+# Create S3 bucket:
+# http://docs.aws.amazon.com/AmazonS3/latest/gsg/CreatingABucket.html
+# * bucket name: jgit.eclipse.org
+#
+# Configure S3 bucket source address/mask access:
+# http://docs.aws.amazon.com/AmazonS3/latest/dev/example-bucket-policies.html
+# * attach bucket policy to the test bucket: jgit-s3-config.policy.bucket.json
+# * verify that any required source address/mask is included in the bucket policy:
+# * see https://wiki.eclipse.org/Hudson
+# * see http://www.tcpiputils.com/browse/ip-address/198.41.30.200
+# * proxy.eclipse.org 198.41.30.0/24
+# * Andrei Pozolotin 67.175.188.187/32
+#
+# Configure bucket 1 day expiration in object life cycle management:
+# * https://docs.aws.amazon.com/AmazonS3/latest/dev/manage-lifecycle-using-console.html
+#
+
+# Test bucket name
+test.bucket=jgit.eclipse.org
+
+# IAM credentials for user jgit.eclipse.org
+accesskey=AKIAIYWXB4ETREBRMZDQ
+secretkey=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
diff --git a/org.eclipse.jgit.test/tst-rsrc/jgit-s3-config.policy.bucket.json b/org.eclipse.jgit.test/tst-rsrc/jgit-s3-config.policy.bucket.json
new file mode 100644
index 0000000..3020b09
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/jgit-s3-config.policy.bucket.json
@@ -0,0 +1,20 @@
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Sid": "DenyAllButKnownSourceAddressWithMask",
+ "Effect": "Deny",
+ "Principal": "*",
+ "Action": "s3:*",
+ "Resource": "arn:aws:s3:::jgit.eclipse.org/*",
+ "Condition": {
+ "NotIpAddress": {
+ "aws:SourceIp": [
+ "198.41.30.0/24",
+ "67.175.188.187/32"
+ ]
+ }
+ }
+ }
+ ]
+}
diff --git a/org.eclipse.jgit.test/tst-rsrc/jgit-s3-config.policy.user.json b/org.eclipse.jgit.test/tst-rsrc/jgit-s3-config.policy.user.json
new file mode 100644
index 0000000..830d088
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/jgit-s3-config.policy.user.json
@@ -0,0 +1,24 @@
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Sid": "BucketList",
+ "Effect": "Allow",
+ "Action": "s3:ListAllMyBuckets",
+ "Resource": [
+ "arn:aws:s3:::jgit.eclipse.org"
+ ]
+ },
+ {
+ "Sid": "BucketFullControl",
+ "Effect": "Allow",
+ "Action": [
+ "s3:*"
+ ],
+ "Resource": [
+ "arn:aws:s3:::jgit.eclipse.org",
+ "arn:aws:s3:::jgit.eclipse.org/*"
+ ]
+ }
+ ]
+}
diff --git a/org.eclipse.jgit.test/tst-rsrc/jgit-s3-connection-v-0.properties b/org.eclipse.jgit.test/tst-rsrc/jgit-s3-connection-v-0.properties
new file mode 100644
index 0000000..2402a49
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/jgit-s3-connection-v-0.properties
@@ -0,0 +1,11 @@
+#
+# Sample Amazon S3 connection configuration file, Version 0.
+# Version 0 (or lack of version) will produce JetS3tV2 compatible encryption.
+# JetS3tV2 supports only PBE algorithms, with partially compromised AES mode.
+#
+
+accesskey = AKIAIYWXB4ETREBRM123
+secretkey = ozCuIsqxsARoPe3FFyv3F/jiMSc3Yqay7B9UF234
+
+crypto.algorithm = PBEWithMD5AndDES
+password = secret
diff --git a/org.eclipse.jgit.test/tst-rsrc/jgit-s3-connection-v-1.properties b/org.eclipse.jgit.test/tst-rsrc/jgit-s3-connection-v-1.properties
new file mode 100644
index 0000000..d0d1611
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/jgit-s3-connection-v-1.properties
@@ -0,0 +1,14 @@
+#
+# Sample Amazon S3 connection configuration file, Version 1.
+# Version 1 will produce JGitV1 compatible encryption.
+# It is JetS3tV2-like mode with proper AES support.
+# JGitV1 uses hard coded encryption parameters.
+# JGitV1 supports only PBE algorithms.
+#
+
+accesskey = AKIAIYWXB4ETREBRM123
+secretkey = ozCuIsqxsARoPe3FFyv3F/jiMSc3Yqay7B9UF234
+
+crypto.algorithm = PBEWithHmacSHA1AndAES_128
+crypto.version = 1
+password = secret
diff --git a/org.eclipse.jgit.test/tst-rsrc/jgit-s3-connection-v-2.properties b/org.eclipse.jgit.test/tst-rsrc/jgit-s3-connection-v-2.properties
new file mode 100644
index 0000000..731b324
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/jgit-s3-connection-v-2.properties
@@ -0,0 +1,48 @@
+#
+# Sample Amazon S3 connection configuration file, Version 2.
+# Version 2 will produce JGitV2 compatible encryption.
+# JGitV2 introduces more flexible control over cipher and key factory parameters.
+# JGitV2 hides actual cipher/key algorithms inside the encryption profile.
+# JGitV2 does not use any hard coded encryption parameters.
+# JGitV2 supports both PBE and Non-PBE algorithms.
+
+accesskey = AKIAIYWXB4ETREBRM123
+secretkey = ozCuIsqxsARoPe3FFyv3F/jiMSc3Yqay7B9UF234
+
+# In Version 2 "crypto.algorithm" is a reference to the encryption "profile".
+crypto.algorithm = custom
+crypto.version = 2
+password = secret
+
+#
+# Encryption profile is a collection of related properties,
+# all having common property root name, or prefix:
+#
+# Cipher algorithm.
+custom.algo = AES/CBC/PKCS5Padding
+# Key factory algorithm.
+custom.key.algo = PBKDF2WithHmacSHA512
+# Key size, bits.
+custom.key.size = 256
+# Number of key generation iterations.
+custom.key.iter = 50000
+# Salt used in key generation (hex value, white space OK).
+custom.key.salt = e2 55 89 67 8e 8d e8 4c
+
+# Same file can store multiple profiles.
+# Only one profile can be active at a time.
+# Active profile is selected via "crypto.algorithm"
+
+#
+# Here is how to create V1 encryption in V2 format:
+#
+# Cipher algorithm.
+legacy.algo = PBEWithHmacSHA1AndAES_128
+# Key factory algorithm.
+legacy.key.algo = PBEWithHmacSHA1AndAES_128
+# Key size, bits.
+legacy.key.size = 32
+# Number of key generation iterations.
+legacy.key.iter = 5000
+# Salt used in key generation (hex value, white space OK).
+legacy.key.salt = A40BC834D695F313
diff --git a/org.eclipse.jgit.test/tst-rsrc/log4j.properties b/org.eclipse.jgit.test/tst-rsrc/log4j.properties
new file mode 100644
index 0000000..14620ff
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/log4j.properties
@@ -0,0 +1,9 @@
+
+# Root logger option
+log4j.rootLogger=INFO, stdout
+
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AbstractRemoteCommandTest.java
similarity index 55%
copy from org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java
copy to org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AbstractRemoteCommandTest.java
index a0a5d95..d6a6342 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AbstractRemoteCommandTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, Ketan Padegaonkar <ketanpadegaonkar at gmail.com>
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r at zend.com>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -42,60 +42,55 @@
*/
package org.eclipse.jgit.api;
+import static org.junit.Assert.assertEquals;
+
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Map;
+import java.net.URISyntaxException;
-import org.eclipse.jgit.api.errors.GitAPIException;
-import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.URIish;
-/**
- * Used to obtain a list of tags.
- *
- * @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-tag.html"
- * >Git documentation about Tag</a>
- */
-public class ListTagCommand extends GitCommand<List<Ref>> {
+public class AbstractRemoteCommandTest extends RepositoryTestCase {
+
+ protected static final String REMOTE_NAME = "test";
+
+ protected RemoteConfig setupRemote()
+ throws IOException, URISyntaxException {
+ // create another repository
+ Repository remoteRepository = createWorkRepository();
+
+ // set it up as a remote to this repository
+ final StoredConfig config = db.getConfig();
+ RemoteConfig remoteConfig = new RemoteConfig(config, REMOTE_NAME);
+
+ RefSpec refSpec = new RefSpec();
+ refSpec = refSpec.setForceUpdate(true);
+ refSpec = refSpec.setSourceDestination(Constants.R_HEADS + "*",
+ Constants.R_REMOTES + REMOTE_NAME + "/*");
+ remoteConfig.addFetchRefSpec(refSpec);
+
+ URIish uri = new URIish(
+ remoteRepository.getDirectory().toURI().toURL());
+ remoteConfig.addURI(uri);
+
+ remoteConfig.update(config);
+ config.save();
- /**
- * @param repo
- */
- protected ListTagCommand(Repository repo) {
- super(repo);
+ return remoteConfig;
}
- /**
- * @return the tags available
- */
- public List<Ref> call() throws GitAPIException {
- checkCallable();
- Map<String, Ref> refList;
- List<Ref> tags = new ArrayList<Ref>();
- RevWalk revWalk = new RevWalk(repo);
- try {
- refList = repo.getRefDatabase().getRefs(Constants.R_TAGS);
- for (Ref ref : refList.values()) {
- tags.add(ref);
- }
- } catch (IOException e) {
- throw new JGitInternalException(e.getMessage(), e);
- } finally {
- revWalk.release();
- }
- Collections.sort(tags, new Comparator<Ref>() {
- public int compare(Ref o1, Ref o2) {
- return o1.getName().compareTo(o2.getName());
- }
- });
- setCallable(false);
- return tags;
+ protected void assertRemoteConfigEquals(RemoteConfig expected,
+ RemoteConfig actual) {
+ assertEquals(expected.getName(), actual.getName());
+ assertEquals(expected.getURIs(), actual.getURIs());
+ assertEquals(expected.getPushURIs(), actual.getPushURIs());
+ assertEquals(expected.getFetchRefSpecs(), actual.getFetchRefSpecs());
+ assertEquals(expected.getPushRefSpecs(), actual.getPushRefSpecs());
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
index 275b7ad..4fefdfd 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
@@ -43,8 +43,10 @@
*/
package org.eclipse.jgit.api;
+import static org.eclipse.jgit.util.FileUtils.RECURSIVE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
@@ -52,11 +54,13 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import org.eclipse.jgit.api.errors.FilterFailedException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.NoFilepatternException;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
@@ -74,9 +78,7 @@ public class AddCommandTest extends RepositoryTestCase {
@Test
public void testAddNothing() throws GitAPIException {
- Git git = new Git(db);
-
- try {
+ try (Git git = new Git(db)) {
git.add().call();
fail("Expected IllegalArgumentException");
} catch (NoFilepatternException e) {
@@ -87,11 +89,10 @@ public class AddCommandTest extends RepositoryTestCase {
@Test
public void testAddNonExistingSingleFile() throws GitAPIException {
- Git git = new Git(db);
-
- DirCache dc = git.add().addFilepattern("a.txt").call();
- assertEquals(0, dc.getEntryCount());
-
+ try (Git git = new Git(db)) {
+ DirCache dc = git.add().addFilepattern("a.txt").call();
+ assertEquals(0, dc.getEntryCount());
+ }
}
@Test
@@ -102,13 +103,207 @@ public class AddCommandTest extends RepositoryTestCase {
writer.print("content");
writer.close();
- Git git = new Git(db);
+ try (Git git = new Git(db)) {
+ git.add().addFilepattern("a.txt").call();
- git.add().addFilepattern("a.txt").call();
+ assertEquals(
+ "[a.txt, mode:100644, content:content]",
+ indexState(CONTENT));
+ }
+ }
- assertEquals(
- "[a.txt, mode:100644, content:content]",
- indexState(CONTENT));
+ @Test
+ public void testCleanFilter() throws IOException,
+ GitAPIException {
+ writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
+ writeTrashFile("src/a.tmp", "foo");
+ // Caution: we need a trailing '\n' since sed on mac always appends
+ // linefeeds if missing
+ writeTrashFile("src/a.txt", "foo\n");
+ File script = writeTempFile("sed s/o/e/g");
+
+ try (Git git = new Git(db)) {
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("filter", "tstFilter", "clean",
+ "sh " + slashify(script.getPath()));
+ config.save();
+
+ git.add().addFilepattern("src/a.txt").addFilepattern("src/a.tmp")
+ .call();
+
+ assertEquals(
+ "[src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:fee\n]",
+ indexState(CONTENT));
+ }
+ }
+
+ @Test
+ public void testCleanFilterEnvironment()
+ throws IOException, GitAPIException {
+ writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
+ writeTrashFile("src/a.txt", "foo");
+ File script = writeTempFile("echo $GIT_DIR; echo 1 >xyz");
+
+ try (Git git = new Git(db)) {
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("filter", "tstFilter", "clean",
+ "sh " + slashify(script.getPath()));
+ config.save();
+ git.add().addFilepattern("src/a.txt").call();
+
+ String gitDir = db.getDirectory().getAbsolutePath();
+ assertEquals("[src/a.txt, mode:100644, content:" + gitDir
+ + "\n]", indexState(CONTENT));
+ assertTrue(new File(db.getWorkTree(), "xyz").exists());
+ }
+ }
+
+ @Test
+ public void testMultipleCleanFilter() throws IOException, GitAPIException {
+ writeTrashFile(".gitattributes",
+ "*.txt filter=tstFilter\n*.tmp filter=tstFilter2");
+ // Caution: we need a trailing '\n' since sed on mac always appends
+ // linefeeds if missing
+ writeTrashFile("src/a.tmp", "foo\n");
+ writeTrashFile("src/a.txt", "foo\n");
+ File script = writeTempFile("sed s/o/e/g");
+ File script2 = writeTempFile("sed s/f/x/g");
+
+ try (Git git = new Git(db)) {
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("filter", "tstFilter", "clean",
+ "sh " + slashify(script.getPath()));
+ config.setString("filter", "tstFilter2", "clean",
+ "sh " + slashify(script2.getPath()));
+ config.save();
+
+ git.add().addFilepattern("src/a.txt").addFilepattern("src/a.tmp")
+ .call();
+
+ assertEquals(
+ "[src/a.tmp, mode:100644, content:xoo\n][src/a.txt, mode:100644, content:fee\n]",
+ indexState(CONTENT));
+
+ // TODO: multiple clean filters for one file???
+ }
+ }
+
+ /**
+ * The path of an added file name contains ';' and afterwards malicious
+ * commands. Make sure when calling filter commands to properly escape the
+ * filenames
+ *
+ * @throws IOException
+ * @throws GitAPIException
+ */
+ @Test
+ public void testCommandInjection() throws IOException, GitAPIException {
+ // Caution: we need a trailing '\n' since sed on mac always appends
+ // linefeeds if missing
+ writeTrashFile("; echo virus", "foo\n");
+ File script = writeTempFile("sed s/o/e/g");
+
+ try (Git git = new Git(db)) {
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("filter", "tstFilter", "clean",
+ "sh " + slashify(script.getPath()) + " %f");
+ writeTrashFile(".gitattributes", "* filter=tstFilter");
+
+ git.add().addFilepattern("; echo virus").call();
+ // Without proper escaping the content would be "feovirus". The sed
+ // command and the "echo virus" would contribute to the content
+ assertEquals("[; echo virus, mode:100644, content:fee\n]",
+ indexState(CONTENT));
+ }
+ }
+
+ @Test
+ public void testBadCleanFilter() throws IOException, GitAPIException {
+ writeTrashFile("a.txt", "foo");
+ File script = writeTempFile("sedfoo s/o/e/g");
+
+ try (Git git = new Git(db)) {
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("filter", "tstFilter", "clean",
+ "sh " + script.getPath());
+ config.save();
+ writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
+
+ try {
+ git.add().addFilepattern("a.txt").call();
+ fail("Didn't received the expected exception");
+ } catch (FilterFailedException e) {
+ assertEquals(127, e.getReturnCode());
+ }
+ }
+ }
+
+ @Test
+ public void testBadCleanFilter2() throws IOException, GitAPIException {
+ writeTrashFile("a.txt", "foo");
+ File script = writeTempFile("sed s/o/e/g");
+
+ try (Git git = new Git(db)) {
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("filter", "tstFilter", "clean",
+ "shfoo " + script.getPath());
+ config.save();
+ writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
+
+ try {
+ git.add().addFilepattern("a.txt").call();
+ fail("Didn't received the expected exception");
+ } catch (FilterFailedException e) {
+ assertEquals(127, e.getReturnCode());
+ }
+ }
+ }
+
+ @Test
+ public void testCleanFilterReturning12() throws IOException,
+ GitAPIException {
+ writeTrashFile("a.txt", "foo");
+ File script = writeTempFile("exit 12");
+
+ try (Git git = new Git(db)) {
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("filter", "tstFilter", "clean",
+ "sh " + slashify(script.getPath()));
+ config.save();
+ writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
+
+ try {
+ git.add().addFilepattern("a.txt").call();
+ fail("Didn't received the expected exception");
+ } catch (FilterFailedException e) {
+ assertEquals(12, e.getReturnCode());
+ }
+ }
+ }
+
+ @Test
+ public void testNotApplicableFilter() throws IOException, GitAPIException {
+ writeTrashFile("a.txt", "foo");
+ File script = writeTempFile("sed s/o/e/g");
+
+ try (Git git = new Git(db)) {
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("filter", "tstFilter", "something",
+ "sh " + script.getPath());
+ config.save();
+ writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
+
+ git.add().addFilepattern("a.txt").call();
+
+ assertEquals("[a.txt, mode:100644, content:foo]",
+ indexState(CONTENT));
+ }
+ }
+
+ private File writeTempFile(String body) throws IOException {
+ File f = File.createTempFile("AddCommandTest_", "");
+ JGitTestUtil.write(f, body);
+ return f;
}
@Test
@@ -120,19 +315,20 @@ public class AddCommandTest extends RepositoryTestCase {
writer.print("row1\r\nrow2");
writer.close();
- Git git = new Git(db);
- db.getConfig().setString("core", null, "autocrlf", "false");
- git.add().addFilepattern("a.txt").call();
- assertEquals("[a.txt, mode:100644, content:row1\r\nrow2]",
- indexState(CONTENT));
- db.getConfig().setString("core", null, "autocrlf", "true");
- git.add().addFilepattern("a.txt").call();
- assertEquals("[a.txt, mode:100644, content:row1\nrow2]",
- indexState(CONTENT));
- db.getConfig().setString("core", null, "autocrlf", "input");
- git.add().addFilepattern("a.txt").call();
- assertEquals("[a.txt, mode:100644, content:row1\nrow2]",
- indexState(CONTENT));
+ try (Git git = new Git(db)) {
+ db.getConfig().setString("core", null, "autocrlf", "false");
+ git.add().addFilepattern("a.txt").call();
+ assertEquals("[a.txt, mode:100644, content:row1\r\nrow2]",
+ indexState(CONTENT));
+ db.getConfig().setString("core", null, "autocrlf", "true");
+ git.add().addFilepattern("a.txt").call();
+ assertEquals("[a.txt, mode:100644, content:row1\nrow2]",
+ indexState(CONTENT));
+ db.getConfig().setString("core", null, "autocrlf", "input");
+ git.add().addFilepattern("a.txt").call();
+ assertEquals("[a.txt, mode:100644, content:row1\nrow2]",
+ indexState(CONTENT));
+ }
}
@Test
@@ -149,19 +345,20 @@ public class AddCommandTest extends RepositoryTestCase {
writer.print(crData);
writer.close();
String lfData = data.toString().replaceAll("\r", "");
- Git git = new Git(db);
- db.getConfig().setString("core", null, "autocrlf", "false");
- git.add().addFilepattern("a.txt").call();
- assertEquals("[a.txt, mode:100644, content:" + data + "]",
- indexState(CONTENT));
- db.getConfig().setString("core", null, "autocrlf", "true");
- git.add().addFilepattern("a.txt").call();
- assertEquals("[a.txt, mode:100644, content:" + lfData + "]",
- indexState(CONTENT));
- db.getConfig().setString("core", null, "autocrlf", "input");
- git.add().addFilepattern("a.txt").call();
- assertEquals("[a.txt, mode:100644, content:" + lfData + "]",
- indexState(CONTENT));
+ try (Git git = new Git(db)) {
+ db.getConfig().setString("core", null, "autocrlf", "false");
+ git.add().addFilepattern("a.txt").call();
+ assertEquals("[a.txt, mode:100644, content:" + data + "]",
+ indexState(CONTENT));
+ db.getConfig().setString("core", null, "autocrlf", "true");
+ git.add().addFilepattern("a.txt").call();
+ assertEquals("[a.txt, mode:100644, content:" + lfData + "]",
+ indexState(CONTENT));
+ db.getConfig().setString("core", null, "autocrlf", "input");
+ git.add().addFilepattern("a.txt").call();
+ assertEquals("[a.txt, mode:100644, content:" + lfData + "]",
+ indexState(CONTENT));
+ }
}
@Test
@@ -173,19 +370,20 @@ public class AddCommandTest extends RepositoryTestCase {
writer.print("row1\r\nrow2\u0000");
writer.close();
- Git git = new Git(db);
- db.getConfig().setString("core", null, "autocrlf", "false");
- git.add().addFilepattern("a.txt").call();
- assertEquals("[a.txt, mode:100644, content:row1\r\nrow2\u0000]",
- indexState(CONTENT));
- db.getConfig().setString("core", null, "autocrlf", "true");
- git.add().addFilepattern("a.txt").call();
- assertEquals("[a.txt, mode:100644, content:row1\r\nrow2\u0000]",
- indexState(CONTENT));
- db.getConfig().setString("core", null, "autocrlf", "input");
- git.add().addFilepattern("a.txt").call();
- assertEquals("[a.txt, mode:100644, content:row1\r\nrow2\u0000]",
- indexState(CONTENT));
+ try (Git git = new Git(db)) {
+ db.getConfig().setString("core", null, "autocrlf", "false");
+ git.add().addFilepattern("a.txt").call();
+ assertEquals("[a.txt, mode:100644, content:row1\r\nrow2\u0000]",
+ indexState(CONTENT));
+ db.getConfig().setString("core", null, "autocrlf", "true");
+ git.add().addFilepattern("a.txt").call();
+ assertEquals("[a.txt, mode:100644, content:row1\r\nrow2\u0000]",
+ indexState(CONTENT));
+ db.getConfig().setString("core", null, "autocrlf", "input");
+ git.add().addFilepattern("a.txt").call();
+ assertEquals("[a.txt, mode:100644, content:row1\r\nrow2\u0000]",
+ indexState(CONTENT));
+ }
}
@Test
@@ -198,13 +396,13 @@ public class AddCommandTest extends RepositoryTestCase {
writer.print("content");
writer.close();
- Git git = new Git(db);
-
- git.add().addFilepattern("sub/a.txt").call();
+ try (Git git = new Git(db)) {
+ git.add().addFilepattern("sub/a.txt").call();
- assertEquals(
- "[sub/a.txt, mode:100644, content:content]",
- indexState(CONTENT));
+ assertEquals(
+ "[sub/a.txt, mode:100644, content:content]",
+ indexState(CONTENT));
+ }
}
@Test
@@ -216,20 +414,21 @@ public class AddCommandTest extends RepositoryTestCase {
writer.print("content");
writer.close();
- Git git = new Git(db);
- DirCache dc = git.add().addFilepattern("a.txt").call();
+ try (Git git = new Git(db)) {
+ DirCache dc = git.add().addFilepattern("a.txt").call();
- dc.getEntry(0).getObjectId();
+ dc.getEntry(0).getObjectId();
- writer = new PrintWriter(file);
- writer.print("other content");
- writer.close();
+ writer = new PrintWriter(file);
+ writer.print("other content");
+ writer.close();
- dc = git.add().addFilepattern("a.txt").call();
+ dc = git.add().addFilepattern("a.txt").call();
- assertEquals(
- "[a.txt, mode:100644, content:other content]",
- indexState(CONTENT));
+ assertEquals(
+ "[a.txt, mode:100644, content:other content]",
+ indexState(CONTENT));
+ }
}
@Test
@@ -240,22 +439,23 @@ public class AddCommandTest extends RepositoryTestCase {
writer.print("content");
writer.close();
- Git git = new Git(db);
- DirCache dc = git.add().addFilepattern("a.txt").call();
+ try (Git git = new Git(db)) {
+ DirCache dc = git.add().addFilepattern("a.txt").call();
- dc.getEntry(0).getObjectId();
+ dc.getEntry(0).getObjectId();
- git.commit().setMessage("commit a.txt").call();
+ git.commit().setMessage("commit a.txt").call();
- writer = new PrintWriter(file);
- writer.print("other content");
- writer.close();
+ writer = new PrintWriter(file);
+ writer.print("other content");
+ writer.close();
- dc = git.add().addFilepattern("a.txt").call();
+ dc = git.add().addFilepattern("a.txt").call();
- assertEquals(
- "[a.txt, mode:100644, content:other content]",
- indexState(CONTENT));
+ assertEquals(
+ "[a.txt, mode:100644, content:other content]",
+ indexState(CONTENT));
+ }
}
@Test
@@ -266,18 +466,19 @@ public class AddCommandTest extends RepositoryTestCase {
writer.print("content");
writer.close();
- Git git = new Git(db);
- DirCache dc = git.add().addFilepattern("a.txt").call();
+ try (Git git = new Git(db)) {
+ DirCache dc = git.add().addFilepattern("a.txt").call();
- dc.getEntry(0).getObjectId();
- FileUtils.delete(file);
+ dc.getEntry(0).getObjectId();
+ FileUtils.delete(file);
- // is supposed to do nothing
- dc = git.add().addFilepattern("a.txt").call();
+ // is supposed to do nothing
+ dc = git.add().addFilepattern("a.txt").call();
- assertEquals(
- "[a.txt, mode:100644, content:content]",
- indexState(CONTENT));
+ assertEquals(
+ "[a.txt, mode:100644, content:content]",
+ indexState(CONTENT));
+ }
}
@Test
@@ -288,20 +489,21 @@ public class AddCommandTest extends RepositoryTestCase {
writer.print("content");
writer.close();
- Git git = new Git(db);
- DirCache dc = git.add().addFilepattern("a.txt").call();
+ try (Git git = new Git(db)) {
+ DirCache dc = git.add().addFilepattern("a.txt").call();
- git.commit().setMessage("commit a.txt").call();
+ git.commit().setMessage("commit a.txt").call();
- dc.getEntry(0).getObjectId();
- FileUtils.delete(file);
+ dc.getEntry(0).getObjectId();
+ FileUtils.delete(file);
- // is supposed to do nothing
- dc = git.add().addFilepattern("a.txt").call();
+ // is supposed to do nothing
+ dc = git.add().addFilepattern("a.txt").call();
- assertEquals(
- "[a.txt, mode:100644, content:content]",
- indexState(CONTENT));
+ assertEquals(
+ "[a.txt, mode:100644, content:content]",
+ indexState(CONTENT));
+ }
}
@Test
@@ -349,13 +551,14 @@ public class AddCommandTest extends RepositoryTestCase {
// now the test begins
- Git git = new Git(db);
- dc = git.add().addFilepattern("a.txt").call();
+ try (Git git = new Git(db)) {
+ dc = git.add().addFilepattern("a.txt").call();
- assertEquals(
- "[a.txt, mode:100644, content:our content]" +
- "[b.txt, mode:100644, content:content b]",
- indexState(CONTENT));
+ assertEquals(
+ "[a.txt, mode:100644, content:our content]" +
+ "[b.txt, mode:100644, content:content b]",
+ indexState(CONTENT));
+ }
}
@Test
@@ -372,12 +575,13 @@ public class AddCommandTest extends RepositoryTestCase {
writer.print("content b");
writer.close();
- Git git = new Git(db);
- git.add().addFilepattern("a.txt").addFilepattern("b.txt").call();
- assertEquals(
- "[a.txt, mode:100644, content:content]" +
- "[b.txt, mode:100644, content:content b]",
- indexState(CONTENT));
+ try (Git git = new Git(db)) {
+ git.add().addFilepattern("a.txt").addFilepattern("b.txt").call();
+ assertEquals(
+ "[a.txt, mode:100644, content:content]" +
+ "[b.txt, mode:100644, content:content b]",
+ indexState(CONTENT));
+ }
}
@Test
@@ -395,12 +599,13 @@ public class AddCommandTest extends RepositoryTestCase {
writer.print("content b");
writer.close();
- Git git = new Git(db);
- git.add().addFilepattern("sub").call();
- assertEquals(
- "[sub/a.txt, mode:100644, content:content]" +
- "[sub/b.txt, mode:100644, content:content b]",
- indexState(CONTENT));
+ try (Git git = new Git(db)) {
+ git.add().addFilepattern("sub").call();
+ assertEquals(
+ "[sub/a.txt, mode:100644, content:content]" +
+ "[sub/b.txt, mode:100644, content:content b]",
+ indexState(CONTENT));
+ }
}
@Test
@@ -424,12 +629,13 @@ public class AddCommandTest extends RepositoryTestCase {
writer.print("content b");
writer.close();
- Git git = new Git(db);
- git.add().addFilepattern("sub").call();
+ try (Git git = new Git(db)) {
+ git.add().addFilepattern("sub").call();
- assertEquals(
- "[sub/a.txt, mode:100644, content:content]",
- indexState(CONTENT));
+ assertEquals(
+ "[sub/a.txt, mode:100644, content:content]",
+ indexState(CONTENT));
+ }
}
@Test
@@ -447,12 +653,13 @@ public class AddCommandTest extends RepositoryTestCase {
writer.print("content b");
writer.close();
- Git git = new Git(db);
- git.add().addFilepattern(".").call();
- assertEquals(
- "[sub/a.txt, mode:100644, content:content]" +
- "[sub/b.txt, mode:100644, content:content b]",
- indexState(CONTENT));
+ try (Git git = new Git(db)) {
+ git.add().addFilepattern(".").call();
+ assertEquals(
+ "[sub/a.txt, mode:100644, content:content]" +
+ "[sub/b.txt, mode:100644, content:content b]",
+ indexState(CONTENT));
+ }
}
// the same three cases as in testAddWithParameterUpdate
@@ -474,40 +681,41 @@ public class AddCommandTest extends RepositoryTestCase {
writer.print("content b");
writer.close();
- Git git = new Git(db);
- git.add().addFilepattern("sub").call();
-
- assertEquals(
- "[sub/a.txt, mode:100644, content:content]" +
- "[sub/b.txt, mode:100644, content:content b]",
- indexState(CONTENT));
-
- git.commit().setMessage("commit").call();
-
- // new unstaged file sub/c.txt
- File file3 = new File(db.getWorkTree(), "sub/c.txt");
- FileUtils.createNewFile(file3);
- writer = new PrintWriter(file3);
- writer.print("content c");
- writer.close();
-
- // file sub/a.txt is modified
- writer = new PrintWriter(file);
- writer.print("modified content");
- writer.close();
-
- // file sub/b.txt is deleted
- FileUtils.delete(file2);
-
- git.add().addFilepattern("sub").call();
- // change in sub/a.txt is staged
- // deletion of sub/b.txt is not staged
- // sub/c.txt is staged
- assertEquals(
- "[sub/a.txt, mode:100644, content:modified content]" +
- "[sub/b.txt, mode:100644, content:content b]" +
- "[sub/c.txt, mode:100644, content:content c]",
- indexState(CONTENT));
+ try (Git git = new Git(db)) {
+ git.add().addFilepattern("sub").call();
+
+ assertEquals(
+ "[sub/a.txt, mode:100644, content:content]" +
+ "[sub/b.txt, mode:100644, content:content b]",
+ indexState(CONTENT));
+
+ git.commit().setMessage("commit").call();
+
+ // new unstaged file sub/c.txt
+ File file3 = new File(db.getWorkTree(), "sub/c.txt");
+ FileUtils.createNewFile(file3);
+ writer = new PrintWriter(file3);
+ writer.print("content c");
+ writer.close();
+
+ // file sub/a.txt is modified
+ writer = new PrintWriter(file);
+ writer.print("modified content");
+ writer.close();
+
+ // file sub/b.txt is deleted
+ FileUtils.delete(file2);
+
+ git.add().addFilepattern("sub").call();
+ // change in sub/a.txt is staged
+ // deletion of sub/b.txt is not staged
+ // sub/c.txt is staged
+ assertEquals(
+ "[sub/a.txt, mode:100644, content:modified content]" +
+ "[sub/b.txt, mode:100644, content:content b]" +
+ "[sub/c.txt, mode:100644, content:content c]",
+ indexState(CONTENT));
+ }
}
// file a exists in workdir and in index -> added
@@ -528,71 +736,168 @@ public class AddCommandTest extends RepositoryTestCase {
writer.print("content b");
writer.close();
- Git git = new Git(db);
- git.add().addFilepattern("sub").call();
+ try (Git git = new Git(db)) {
+ git.add().addFilepattern("sub").call();
+
+ assertEquals(
+ "[sub/a.txt, mode:100644, content:content]" +
+ "[sub/b.txt, mode:100644, content:content b]",
+ indexState(CONTENT));
+
+ git.commit().setMessage("commit").call();
+
+ // new unstaged file sub/c.txt
+ File file3 = new File(db.getWorkTree(), "sub/c.txt");
+ FileUtils.createNewFile(file3);
+ writer = new PrintWriter(file3);
+ writer.print("content c");
+ writer.close();
+
+ // file sub/a.txt is modified
+ writer = new PrintWriter(file);
+ writer.print("modified content");
+ writer.close();
+
+ FileUtils.delete(file2);
+
+ // change in sub/a.txt is staged
+ // deletion of sub/b.txt is staged
+ // sub/c.txt is not staged
+ git.add().addFilepattern("sub").setUpdate(true).call();
+ // change in sub/a.txt is staged
+ assertEquals(
+ "[sub/a.txt, mode:100644, content:modified content]",
+ indexState(CONTENT));
+ }
+ }
- assertEquals(
- "[sub/a.txt, mode:100644, content:content]" +
- "[sub/b.txt, mode:100644, content:content b]",
- indexState(CONTENT));
+ @Test
+ public void testAssumeUnchanged() throws Exception {
+ try (Git git = new Git(db)) {
+ String path = "a.txt";
+ writeTrashFile(path, "content");
+ git.add().addFilepattern(path).call();
+ String path2 = "b.txt";
+ writeTrashFile(path2, "content");
+ git.add().addFilepattern(path2).call();
+ git.commit().setMessage("commit").call();
+ assertEquals("[a.txt, mode:100644, content:"
+ + "content, assume-unchanged:false]"
+ + "[b.txt, mode:100644, content:content, "
+ + "assume-unchanged:false]", indexState(CONTENT
+ | ASSUME_UNCHANGED));
+ assumeUnchanged(path2);
+ assertEquals("[a.txt, mode:100644, content:content, "
+ + "assume-unchanged:false][b.txt, mode:100644, "
+ + "content:content, assume-unchanged:true]", indexState(CONTENT
+ | ASSUME_UNCHANGED));
+ writeTrashFile(path, "more content");
+ writeTrashFile(path2, "more content");
+
+ git.add().addFilepattern(".").call();
+
+ assertEquals("[a.txt, mode:100644, content:more content,"
+ + " assume-unchanged:false][b.txt, mode:100644,"
+ + " content:content, assume-unchanged:true]",
+ indexState(CONTENT
+ | ASSUME_UNCHANGED));
+ }
+ }
- git.commit().setMessage("commit").call();
+ @Test
+ public void testReplaceFileWithDirectory()
+ throws IOException, NoFilepatternException, GitAPIException {
+ try (Git git = new Git(db)) {
+ writeTrashFile("df", "before replacement");
+ git.add().addFilepattern("df").call();
+ assertEquals("[df, mode:100644, content:before replacement]",
+ indexState(CONTENT));
+ FileUtils.delete(new File(db.getWorkTree(), "df"));
+ writeTrashFile("df/f", "after replacement");
+ git.add().addFilepattern("df").call();
+ assertEquals("[df/f, mode:100644, content:after replacement]",
+ indexState(CONTENT));
+ }
+ }
- // new unstaged file sub/c.txt
- File file3 = new File(db.getWorkTree(), "sub/c.txt");
- FileUtils.createNewFile(file3);
- writer = new PrintWriter(file3);
- writer.print("content c");
- writer.close();
+ @Test
+ public void testReplaceDirectoryWithFile()
+ throws IOException, NoFilepatternException, GitAPIException {
+ try (Git git = new Git(db)) {
+ writeTrashFile("df/f", "before replacement");
+ git.add().addFilepattern("df").call();
+ assertEquals("[df/f, mode:100644, content:before replacement]",
+ indexState(CONTENT));
+ FileUtils.delete(new File(db.getWorkTree(), "df"), RECURSIVE);
+ writeTrashFile("df", "after replacement");
+ git.add().addFilepattern("df").call();
+ assertEquals("[df, mode:100644, content:after replacement]",
+ indexState(CONTENT));
+ }
+ }
- // file sub/a.txt is modified
- writer = new PrintWriter(file);
- writer.print("modified content");
- writer.close();
+ @Test
+ public void testReplaceFileByPartOfDirectory()
+ throws IOException, NoFilepatternException, GitAPIException {
+ try (Git git = new Git(db)) {
+ writeTrashFile("src/main", "df", "before replacement");
+ writeTrashFile("src/main", "z", "z");
+ writeTrashFile("z", "z2");
+ git.add().addFilepattern("src/main/df")
+ .addFilepattern("src/main/z")
+ .addFilepattern("z")
+ .call();
+ assertEquals(
+ "[src/main/df, mode:100644, content:before replacement]" +
+ "[src/main/z, mode:100644, content:z]" +
+ "[z, mode:100644, content:z2]",
+ indexState(CONTENT));
+ FileUtils.delete(new File(db.getWorkTree(), "src/main/df"));
+ writeTrashFile("src/main/df", "a", "after replacement");
+ writeTrashFile("src/main/df", "b", "unrelated file");
+ git.add().addFilepattern("src/main/df/a").call();
+ assertEquals(
+ "[src/main/df/a, mode:100644, content:after replacement]" +
+ "[src/main/z, mode:100644, content:z]" +
+ "[z, mode:100644, content:z2]",
+ indexState(CONTENT));
+ }
+ }
+
+ @Test
+ public void testReplaceDirectoryConflictsWithFile()
+ throws IOException, NoFilepatternException, GitAPIException {
+ DirCache dc = db.lockDirCache();
+ try (ObjectInserter oi = db.newObjectInserter()) {
+ DirCacheBuilder builder = dc.builder();
+ File f = writeTrashFile("a", "df", "content");
+ addEntryToBuilder("a", f, oi, builder, 1);
- FileUtils.delete(file2);
+ f = writeTrashFile("a", "df", "other content");
+ addEntryToBuilder("a/df", f, oi, builder, 3);
- // change in sub/a.txt is staged
- // deletion of sub/b.txt is staged
- // sub/c.txt is not staged
- git.add().addFilepattern("sub").setUpdate(true).call();
- // change in sub/a.txt is staged
+ f = writeTrashFile("a", "df", "our content");
+ addEntryToBuilder("a/df", f, oi, builder, 2);
+
+ f = writeTrashFile("z", "z");
+ addEntryToBuilder("z", f, oi, builder, 0);
+ builder.commit();
+ }
assertEquals(
- "[sub/a.txt, mode:100644, content:modified content]",
+ "[a, mode:100644, stage:1, content:content]" +
+ "[a/df, mode:100644, stage:2, content:our content]" +
+ "[a/df, mode:100644, stage:3, content:other content]" +
+ "[z, mode:100644, content:z]",
indexState(CONTENT));
- }
- @Test
- public void testAssumeUnchanged() throws Exception {
- Git git = new Git(db);
- String path = "a.txt";
- writeTrashFile(path, "content");
- git.add().addFilepattern(path).call();
- String path2 = "b.txt";
- writeTrashFile(path2, "content");
- git.add().addFilepattern(path2).call();
- git.commit().setMessage("commit").call();
- assertEquals("[a.txt, mode:100644, content:"
- + "content, assume-unchanged:false]"
- + "[b.txt, mode:100644, content:content, "
- + "assume-unchanged:false]", indexState(CONTENT
- | ASSUME_UNCHANGED));
- assumeUnchanged(path2);
- assertEquals("[a.txt, mode:100644, content:content, "
- + "assume-unchanged:false][b.txt, mode:100644, "
- + "content:content, assume-unchanged:true]", indexState(CONTENT
- | ASSUME_UNCHANGED));
- writeTrashFile(path, "more content");
- writeTrashFile(path2, "more content");
-
- git.add().addFilepattern(".").call();
-
- assertEquals("[a.txt, mode:100644, content:more content,"
- + " assume-unchanged:false][b.txt, mode:100644,"
- + "" + ""
- + " content:content, assume-unchanged:true]",
- indexState(CONTENT
- | ASSUME_UNCHANGED));
+ try (Git git = new Git(db)) {
+ FileUtils.delete(new File(db.getWorkTree(), "a"), RECURSIVE);
+ writeTrashFile("a", "merged");
+ git.add().addFilepattern("a").call();
+ assertEquals("[a, mode:100644, content:merged]" +
+ "[z, mode:100644, content:z]",
+ indexState(CONTENT));
+ }
}
@Test
@@ -624,7 +929,7 @@ public class AddCommandTest extends RepositoryTestCase {
return this;
}
- protected File discoverGitPrefix() {
+ protected File discoverGitExe() {
return null;
}
@@ -669,7 +974,7 @@ public class AddCommandTest extends RepositoryTestCase {
return this;
}
- protected File discoverGitPrefix() {
+ protected File discoverGitExe() {
return null;
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java
index 8cdf6a6..fc8df42 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java
@@ -45,6 +45,7 @@ package org.eclipse.jgit.api;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
+import java.beans.Statement;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
@@ -58,6 +59,7 @@ import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.util.StringUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -83,96 +85,107 @@ public class ArchiveCommandTest extends RepositoryTestCase {
@Test
public void archiveHeadAllFiles() throws IOException, GitAPIException {
- Git git = new Git(db);
- writeTrashFile("file_1.txt", "content_1_1");
- git.add().addFilepattern("file_1.txt").call();
- git.commit().setMessage("create file").call();
-
- writeTrashFile("file_1.txt", "content_1_2");
- writeTrashFile("file_2.txt", "content_2_2");
- git.add().addFilepattern(".").call();
- git.commit().setMessage("updated file").call();
-
- git.archive().setOutputStream(new MockOutputStream())
- .setFormat(format.SUFFIXES.get(0))
- .setTree(git.getRepository().resolve("HEAD")).call();
-
- assertEquals(UNEXPECTED_ARCHIVE_SIZE, 2, format.size());
- assertEquals(UNEXPECTED_FILE_CONTENTS, "content_1_2", format.getByPath("file_1.txt"));
- assertEquals(UNEXPECTED_FILE_CONTENTS, "content_2_2", format.getByPath("file_2.txt"));
+ try (Git git = new Git(db)) {
+ writeTrashFile("file_1.txt", "content_1_1");
+ git.add().addFilepattern("file_1.txt").call();
+ git.commit().setMessage("create file").call();
+
+ writeTrashFile("file_1.txt", "content_1_2");
+ writeTrashFile("file_2.txt", "content_2_2");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("updated file").call();
+
+ git.archive().setOutputStream(new MockOutputStream())
+ .setFormat(format.SUFFIXES.get(0))
+ .setTree(git.getRepository().resolve("HEAD")).call();
+
+ assertEquals(UNEXPECTED_ARCHIVE_SIZE, 2, format.size());
+ assertEquals(UNEXPECTED_FILE_CONTENTS, "content_1_2", format.getByPath("file_1.txt"));
+ assertEquals(UNEXPECTED_FILE_CONTENTS, "content_2_2", format.getByPath("file_2.txt"));
+ }
}
@Test
public void archiveHeadSpecificPath() throws IOException, GitAPIException {
- Git git = new Git(db);
- writeTrashFile("file_1.txt", "content_1_1");
- git.add().addFilepattern("file_1.txt").call();
- git.commit().setMessage("create file").call();
-
- writeTrashFile("file_1.txt", "content_1_2");
- String expectedFilePath = "some_directory/file_2.txt";
- writeTrashFile(expectedFilePath, "content_2_2");
- git.add().addFilepattern(".").call();
- git.commit().setMessage("updated file").call();
-
- git.archive().setOutputStream(new MockOutputStream())
- .setFormat(format.SUFFIXES.get(0))
- .setTree(git.getRepository().resolve("HEAD"))
- .setPaths(expectedFilePath).call();
-
- assertEquals(UNEXPECTED_ARCHIVE_SIZE, 2, format.size());
- assertEquals(UNEXPECTED_FILE_CONTENTS, "content_2_2", format.getByPath(expectedFilePath));
- assertNull(UNEXPECTED_TREE_CONTENTS, format.getByPath("some_directory"));
+ try (Git git = new Git(db)) {
+ writeTrashFile("file_1.txt", "content_1_1");
+ git.add().addFilepattern("file_1.txt").call();
+ git.commit().setMessage("create file").call();
+
+ writeTrashFile("file_1.txt", "content_1_2");
+ String expectedFilePath = "some_directory/file_2.txt";
+ writeTrashFile(expectedFilePath, "content_2_2");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("updated file").call();
+
+ git.archive().setOutputStream(new MockOutputStream())
+ .setFormat(format.SUFFIXES.get(0))
+ .setTree(git.getRepository().resolve("HEAD"))
+ .setPaths(expectedFilePath).call();
+
+ assertEquals(UNEXPECTED_ARCHIVE_SIZE, 2, format.size());
+ assertEquals(UNEXPECTED_FILE_CONTENTS, "content_2_2", format.getByPath(expectedFilePath));
+ assertNull(UNEXPECTED_TREE_CONTENTS, format.getByPath("some_directory"));
+ }
}
@Test
public void archiveByIdSpecificFile() throws IOException, GitAPIException {
- Git git = new Git(db);
- writeTrashFile("file_1.txt", "content_1_1");
- git.add().addFilepattern("file_1.txt").call();
- RevCommit first = git.commit().setMessage("create file").call();
-
- writeTrashFile("file_1.txt", "content_1_2");
- String expectedFilePath = "some_directory/file_2.txt";
- writeTrashFile(expectedFilePath, "content_2_2");
- git.add().addFilepattern(".").call();
- git.commit().setMessage("updated file").call();
-
- git.archive().setOutputStream(new MockOutputStream())
- .setFormat(format.SUFFIXES.get(0)).setTree(first)
- .setPaths("file_1.txt").call();
-
- assertEquals(UNEXPECTED_ARCHIVE_SIZE, 1, format.size());
- assertEquals(UNEXPECTED_FILE_CONTENTS, "content_1_1", format.getByPath("file_1.txt"));
+ try (Git git = new Git(db)) {
+ writeTrashFile("file_1.txt", "content_1_1");
+ git.add().addFilepattern("file_1.txt").call();
+ RevCommit first = git.commit().setMessage("create file").call();
+
+ writeTrashFile("file_1.txt", "content_1_2");
+ String expectedFilePath = "some_directory/file_2.txt";
+ writeTrashFile(expectedFilePath, "content_2_2");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("updated file").call();
+
+ Map<String, Object> options = new HashMap<>();
+ Integer opt = Integer.valueOf(42);
+ options.put("foo", opt);
+ MockOutputStream out = new MockOutputStream();
+ git.archive().setOutputStream(out)
+ .setFormat(format.SUFFIXES.get(0))
+ .setFormatOptions(options)
+ .setTree(first)
+ .setPaths("file_1.txt").call();
+
+ assertEquals(opt.intValue(), out.getFoo());
+ assertEquals(UNEXPECTED_ARCHIVE_SIZE, 1, format.size());
+ assertEquals(UNEXPECTED_FILE_CONTENTS, "content_1_1", format.getByPath("file_1.txt"));
+ }
}
@Test
public void archiveByDirectoryPath() throws GitAPIException, IOException {
- Git git = new Git(db);
- writeTrashFile("file_0.txt", "content_0_1");
- git.add().addFilepattern("file_0.txt").call();
- git.commit().setMessage("commit_1").call();
-
- writeTrashFile("file_0.txt", "content_0_2");
- String expectedFilePath1 = "some_directory/file_1.txt";
- writeTrashFile(expectedFilePath1, "content_1_2");
- String expectedFilePath2 = "some_directory/file_2.txt";
- writeTrashFile(expectedFilePath2, "content_2_2");
- String expectedFilePath3 = "some_directory/nested_directory/file_3.txt";
- writeTrashFile(expectedFilePath3, "content_3_2");
- git.add().addFilepattern(".").call();
- git.commit().setMessage("commit_2").call();
- git.archive().setOutputStream(new MockOutputStream())
- .setFormat(format.SUFFIXES.get(0))
- .setTree(git.getRepository().resolve("HEAD"))
- .setPaths("some_directory/").call();
-
- assertEquals(UNEXPECTED_ARCHIVE_SIZE, 5, format.size());
- assertEquals(UNEXPECTED_FILE_CONTENTS, "content_1_2", format.getByPath(expectedFilePath1));
- assertEquals(UNEXPECTED_FILE_CONTENTS, "content_2_2", format.getByPath(expectedFilePath2));
- assertEquals(UNEXPECTED_FILE_CONTENTS, "content_3_2", format.getByPath(expectedFilePath3));
- assertNull(UNEXPECTED_TREE_CONTENTS, format.getByPath("some_directory"));
- assertNull(UNEXPECTED_TREE_CONTENTS, format.getByPath("some_directory/nested_directory"));
+ try (Git git = new Git(db)) {
+ writeTrashFile("file_0.txt", "content_0_1");
+ git.add().addFilepattern("file_0.txt").call();
+ git.commit().setMessage("commit_1").call();
+
+ writeTrashFile("file_0.txt", "content_0_2");
+ String expectedFilePath1 = "some_directory/file_1.txt";
+ writeTrashFile(expectedFilePath1, "content_1_2");
+ String expectedFilePath2 = "some_directory/file_2.txt";
+ writeTrashFile(expectedFilePath2, "content_2_2");
+ String expectedFilePath3 = "some_directory/nested_directory/file_3.txt";
+ writeTrashFile(expectedFilePath3, "content_3_2");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("commit_2").call();
+ git.archive().setOutputStream(new MockOutputStream())
+ .setFormat(format.SUFFIXES.get(0))
+ .setTree(git.getRepository().resolve("HEAD"))
+ .setPaths("some_directory/").call();
+
+ assertEquals(UNEXPECTED_ARCHIVE_SIZE, 5, format.size());
+ assertEquals(UNEXPECTED_FILE_CONTENTS, "content_1_2", format.getByPath(expectedFilePath1));
+ assertEquals(UNEXPECTED_FILE_CONTENTS, "content_2_2", format.getByPath(expectedFilePath2));
+ assertEquals(UNEXPECTED_FILE_CONTENTS, "content_3_2", format.getByPath(expectedFilePath3));
+ assertNull(UNEXPECTED_TREE_CONTENTS, format.getByPath("some_directory"));
+ assertNull(UNEXPECTED_TREE_CONTENTS, format.getByPath("some_directory/nested_directory"));
+ }
}
private class MockFormat implements ArchiveCommand.Format<MockOutputStream> {
@@ -192,6 +205,22 @@ public class ArchiveCommandTest extends RepositoryTestCase {
public MockOutputStream createArchiveOutputStream(OutputStream s)
throws IOException {
+ return createArchiveOutputStream(s,
+ Collections.<String, Object> emptyMap());
+ }
+
+ public MockOutputStream createArchiveOutputStream(OutputStream s,
+ Map<String, Object> o) throws IOException {
+ for (Map.Entry<String, Object> p : o.entrySet()) {
+ try {
+ String methodName = "set"
+ + StringUtils.capitalize(p.getKey());
+ new Statement(s, methodName, new Object[] { p.getValue() })
+ .execute();
+ } catch (Exception e) {
+ throw new IOException("cannot set option: " + p.getKey(), e);
+ }
+ }
return new MockOutputStream();
}
@@ -205,7 +234,17 @@ public class ArchiveCommandTest extends RepositoryTestCase {
}
}
- private class MockOutputStream extends OutputStream {
+ public class MockOutputStream extends OutputStream {
+
+ private int foo;
+
+ public void setFoo(int foo) {
+ this.foo = foo;
+ }
+
+ public int getFoo() {
+ return foo;
+ }
@Override
public void write(int b) throws IOException {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BlameCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BlameCommandTest.java
index 0745eb6..f37aa13 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BlameCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BlameCommandTest.java
@@ -72,53 +72,53 @@ public class BlameCommandTest extends RepositoryTestCase {
@Test
public void testSingleRevision() throws Exception {
- Git git = new Git(db);
-
- String[] content = new String[] { "first", "second", "third" };
-
- writeTrashFile("file.txt", join(content));
- git.add().addFilepattern("file.txt").call();
- RevCommit commit = git.commit().setMessage("create file").call();
-
- BlameCommand command = new BlameCommand(db);
- command.setFilePath("file.txt");
- BlameResult lines = command.call();
- assertNotNull(lines);
- assertEquals(3, lines.getResultContents().size());
-
- for (int i = 0; i < 3; i++) {
- assertEquals(commit, lines.getSourceCommit(i));
- assertEquals(i, lines.getSourceLine(i));
+ try (Git git = new Git(db)) {
+ String[] content = new String[] { "first", "second", "third" };
+
+ writeTrashFile("file.txt", join(content));
+ git.add().addFilepattern("file.txt").call();
+ RevCommit commit = git.commit().setMessage("create file").call();
+
+ BlameCommand command = new BlameCommand(db);
+ command.setFilePath("file.txt");
+ BlameResult lines = command.call();
+ assertNotNull(lines);
+ assertEquals(3, lines.getResultContents().size());
+
+ for (int i = 0; i < 3; i++) {
+ assertEquals(commit, lines.getSourceCommit(i));
+ assertEquals(i, lines.getSourceLine(i));
+ }
}
}
@Test
public void testTwoRevisions() throws Exception {
- Git git = new Git(db);
-
- String[] content1 = new String[] { "first", "second" };
- writeTrashFile("file.txt", join(content1));
- git.add().addFilepattern("file.txt").call();
- RevCommit commit1 = git.commit().setMessage("create file").call();
-
- String[] content2 = new String[] { "first", "second", "third" };
- writeTrashFile("file.txt", join(content2));
- git.add().addFilepattern("file.txt").call();
- RevCommit commit2 = git.commit().setMessage("create file").call();
-
- BlameCommand command = new BlameCommand(db);
- command.setFilePath("file.txt");
- BlameResult lines = command.call();
- assertEquals(3, lines.getResultContents().size());
-
- assertEquals(commit1, lines.getSourceCommit(0));
- assertEquals(0, lines.getSourceLine(0));
-
- assertEquals(commit1, lines.getSourceCommit(1));
- assertEquals(1, lines.getSourceLine(1));
-
- assertEquals(commit2, lines.getSourceCommit(2));
- assertEquals(2, lines.getSourceLine(2));
+ try (Git git = new Git(db)) {
+ String[] content1 = new String[] { "first", "second" };
+ writeTrashFile("file.txt", join(content1));
+ git.add().addFilepattern("file.txt").call();
+ RevCommit commit1 = git.commit().setMessage("create file").call();
+
+ String[] content2 = new String[] { "first", "second", "third" };
+ writeTrashFile("file.txt", join(content2));
+ git.add().addFilepattern("file.txt").call();
+ RevCommit commit2 = git.commit().setMessage("create file").call();
+
+ BlameCommand command = new BlameCommand(db);
+ command.setFilePath("file.txt");
+ BlameResult lines = command.call();
+ assertEquals(3, lines.getResultContents().size());
+
+ assertEquals(commit1, lines.getSourceCommit(0));
+ assertEquals(0, lines.getSourceLine(0));
+
+ assertEquals(commit1, lines.getSourceCommit(1));
+ assertEquals(1, lines.getSourceLine(1));
+
+ assertEquals(commit2, lines.getSourceCommit(2));
+ assertEquals(2, lines.getSourceLine(2));
+ }
}
@Test
@@ -138,200 +138,200 @@ public class BlameCommandTest extends RepositoryTestCase {
private void testRename(final String sourcePath, final String destPath)
throws Exception {
- Git git = new Git(db);
-
- String[] content1 = new String[] { "a", "b", "c" };
- writeTrashFile(sourcePath, join(content1));
- git.add().addFilepattern(sourcePath).call();
- RevCommit commit1 = git.commit().setMessage("create file").call();
-
- writeTrashFile(destPath, join(content1));
- git.add().addFilepattern(destPath).call();
- git.rm().addFilepattern(sourcePath).call();
- git.commit().setMessage("moving file").call();
-
- String[] content2 = new String[] { "a", "b", "c2" };
- writeTrashFile(destPath, join(content2));
- git.add().addFilepattern(destPath).call();
- RevCommit commit3 = git.commit().setMessage("editing file").call();
-
- BlameCommand command = new BlameCommand(db);
- command.setFollowFileRenames(true);
- command.setFilePath(destPath);
- BlameResult lines = command.call();
-
- assertEquals(commit1, lines.getSourceCommit(0));
- assertEquals(0, lines.getSourceLine(0));
- assertEquals(sourcePath, lines.getSourcePath(0));
-
- assertEquals(commit1, lines.getSourceCommit(1));
- assertEquals(1, lines.getSourceLine(1));
- assertEquals(sourcePath, lines.getSourcePath(1));
-
- assertEquals(commit3, lines.getSourceCommit(2));
- assertEquals(2, lines.getSourceLine(2));
- assertEquals(destPath, lines.getSourcePath(2));
+ try (Git git = new Git(db)) {
+ String[] content1 = new String[] { "a", "b", "c" };
+ writeTrashFile(sourcePath, join(content1));
+ git.add().addFilepattern(sourcePath).call();
+ RevCommit commit1 = git.commit().setMessage("create file").call();
+
+ writeTrashFile(destPath, join(content1));
+ git.add().addFilepattern(destPath).call();
+ git.rm().addFilepattern(sourcePath).call();
+ git.commit().setMessage("moving file").call();
+
+ String[] content2 = new String[] { "a", "b", "c2" };
+ writeTrashFile(destPath, join(content2));
+ git.add().addFilepattern(destPath).call();
+ RevCommit commit3 = git.commit().setMessage("editing file").call();
+
+ BlameCommand command = new BlameCommand(db);
+ command.setFollowFileRenames(true);
+ command.setFilePath(destPath);
+ BlameResult lines = command.call();
+
+ assertEquals(commit1, lines.getSourceCommit(0));
+ assertEquals(0, lines.getSourceLine(0));
+ assertEquals(sourcePath, lines.getSourcePath(0));
+
+ assertEquals(commit1, lines.getSourceCommit(1));
+ assertEquals(1, lines.getSourceLine(1));
+ assertEquals(sourcePath, lines.getSourcePath(1));
+
+ assertEquals(commit3, lines.getSourceCommit(2));
+ assertEquals(2, lines.getSourceLine(2));
+ assertEquals(destPath, lines.getSourcePath(2));
+ }
}
@Test
public void testTwoRenames() throws Exception {
- Git git = new Git(db);
-
- // Commit 1: Add file.txt
- String[] content1 = new String[] { "a" };
- writeTrashFile("file.txt", join(content1));
- git.add().addFilepattern("file.txt").call();
- RevCommit commit1 = git.commit().setMessage("create file").call();
-
- // Commit 2: Rename to file1.txt
- writeTrashFile("file1.txt", join(content1));
- git.add().addFilepattern("file1.txt").call();
- git.rm().addFilepattern("file.txt").call();
- git.commit().setMessage("moving file").call();
-
- // Commit 3: Edit file1.txt
- String[] content2 = new String[] { "a", "b" };
- writeTrashFile("file1.txt", join(content2));
- git.add().addFilepattern("file1.txt").call();
- RevCommit commit3 = git.commit().setMessage("editing file").call();
-
- // Commit 4: Rename to file2.txt
- writeTrashFile("file2.txt", join(content2));
- git.add().addFilepattern("file2.txt").call();
- git.rm().addFilepattern("file1.txt").call();
- git.commit().setMessage("moving file again").call();
-
- BlameCommand command = new BlameCommand(db);
- command.setFollowFileRenames(true);
- command.setFilePath("file2.txt");
- BlameResult lines = command.call();
-
- assertEquals(commit1, lines.getSourceCommit(0));
- assertEquals(0, lines.getSourceLine(0));
- assertEquals("file.txt", lines.getSourcePath(0));
-
- assertEquals(commit3, lines.getSourceCommit(1));
- assertEquals(1, lines.getSourceLine(1));
- assertEquals("file1.txt", lines.getSourcePath(1));
+ try (Git git = new Git(db)) {
+ // Commit 1: Add file.txt
+ String[] content1 = new String[] { "a" };
+ writeTrashFile("file.txt", join(content1));
+ git.add().addFilepattern("file.txt").call();
+ RevCommit commit1 = git.commit().setMessage("create file").call();
+
+ // Commit 2: Rename to file1.txt
+ writeTrashFile("file1.txt", join(content1));
+ git.add().addFilepattern("file1.txt").call();
+ git.rm().addFilepattern("file.txt").call();
+ git.commit().setMessage("moving file").call();
+
+ // Commit 3: Edit file1.txt
+ String[] content2 = new String[] { "a", "b" };
+ writeTrashFile("file1.txt", join(content2));
+ git.add().addFilepattern("file1.txt").call();
+ RevCommit commit3 = git.commit().setMessage("editing file").call();
+
+ // Commit 4: Rename to file2.txt
+ writeTrashFile("file2.txt", join(content2));
+ git.add().addFilepattern("file2.txt").call();
+ git.rm().addFilepattern("file1.txt").call();
+ git.commit().setMessage("moving file again").call();
+
+ BlameCommand command = new BlameCommand(db);
+ command.setFollowFileRenames(true);
+ command.setFilePath("file2.txt");
+ BlameResult lines = command.call();
+
+ assertEquals(commit1, lines.getSourceCommit(0));
+ assertEquals(0, lines.getSourceLine(0));
+ assertEquals("file.txt", lines.getSourcePath(0));
+
+ assertEquals(commit3, lines.getSourceCommit(1));
+ assertEquals(1, lines.getSourceLine(1));
+ assertEquals("file1.txt", lines.getSourcePath(1));
+ }
}
@Test
public void testDeleteTrailingLines() throws Exception {
- Git git = new Git(db);
+ try (Git git = new Git(db)) {
+ String[] content1 = new String[] { "a", "b", "c", "d" };
+ String[] content2 = new String[] { "a", "b" };
- String[] content1 = new String[] { "a", "b", "c", "d" };
- String[] content2 = new String[] { "a", "b" };
+ writeTrashFile("file.txt", join(content2));
+ git.add().addFilepattern("file.txt").call();
+ RevCommit commit1 = git.commit().setMessage("create file").call();
- writeTrashFile("file.txt", join(content2));
- git.add().addFilepattern("file.txt").call();
- RevCommit commit1 = git.commit().setMessage("create file").call();
+ writeTrashFile("file.txt", join(content1));
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("edit file").call();
- writeTrashFile("file.txt", join(content1));
- git.add().addFilepattern("file.txt").call();
- git.commit().setMessage("edit file").call();
+ writeTrashFile("file.txt", join(content2));
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("edit file").call();
- writeTrashFile("file.txt", join(content2));
- git.add().addFilepattern("file.txt").call();
- git.commit().setMessage("edit file").call();
+ BlameCommand command = new BlameCommand(db);
- BlameCommand command = new BlameCommand(db);
+ command.setFilePath("file.txt");
+ BlameResult lines = command.call();
+ assertEquals(content2.length, lines.getResultContents().size());
- command.setFilePath("file.txt");
- BlameResult lines = command.call();
- assertEquals(content2.length, lines.getResultContents().size());
+ assertEquals(commit1, lines.getSourceCommit(0));
+ assertEquals(commit1, lines.getSourceCommit(1));
- assertEquals(commit1, lines.getSourceCommit(0));
- assertEquals(commit1, lines.getSourceCommit(1));
-
- assertEquals(0, lines.getSourceLine(0));
- assertEquals(1, lines.getSourceLine(1));
+ assertEquals(0, lines.getSourceLine(0));
+ assertEquals(1, lines.getSourceLine(1));
+ }
}
@Test
public void testDeleteMiddleLines() throws Exception {
- Git git = new Git(db);
+ try (Git git = new Git(db)) {
+ String[] content1 = new String[] { "a", "b", "c", "d", "e" };
+ String[] content2 = new String[] { "a", "c", "e" };
- String[] content1 = new String[] { "a", "b", "c", "d", "e" };
- String[] content2 = new String[] { "a", "c", "e" };
+ writeTrashFile("file.txt", join(content2));
+ git.add().addFilepattern("file.txt").call();
+ RevCommit commit1 = git.commit().setMessage("edit file").call();
- writeTrashFile("file.txt", join(content2));
- git.add().addFilepattern("file.txt").call();
- RevCommit commit1 = git.commit().setMessage("edit file").call();
+ writeTrashFile("file.txt", join(content1));
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("edit file").call();
- writeTrashFile("file.txt", join(content1));
- git.add().addFilepattern("file.txt").call();
- git.commit().setMessage("edit file").call();
+ writeTrashFile("file.txt", join(content2));
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("edit file").call();
- writeTrashFile("file.txt", join(content2));
- git.add().addFilepattern("file.txt").call();
- git.commit().setMessage("edit file").call();
+ BlameCommand command = new BlameCommand(db);
- BlameCommand command = new BlameCommand(db);
+ command.setFilePath("file.txt");
+ BlameResult lines = command.call();
+ assertEquals(content2.length, lines.getResultContents().size());
- command.setFilePath("file.txt");
- BlameResult lines = command.call();
- assertEquals(content2.length, lines.getResultContents().size());
+ assertEquals(commit1, lines.getSourceCommit(0));
+ assertEquals(0, lines.getSourceLine(0));
- assertEquals(commit1, lines.getSourceCommit(0));
- assertEquals(0, lines.getSourceLine(0));
+ assertEquals(commit1, lines.getSourceCommit(1));
+ assertEquals(1, lines.getSourceLine(1));
- assertEquals(commit1, lines.getSourceCommit(1));
- assertEquals(1, lines.getSourceLine(1));
-
- assertEquals(commit1, lines.getSourceCommit(2));
- assertEquals(2, lines.getSourceLine(2));
+ assertEquals(commit1, lines.getSourceCommit(2));
+ assertEquals(2, lines.getSourceLine(2));
+ }
}
@Test
public void testEditAllLines() throws Exception {
- Git git = new Git(db);
-
- String[] content1 = new String[] { "a", "1" };
- String[] content2 = new String[] { "b", "2" };
+ try (Git git = new Git(db)) {
+ String[] content1 = new String[] { "a", "1" };
+ String[] content2 = new String[] { "b", "2" };
- writeTrashFile("file.txt", join(content1));
- git.add().addFilepattern("file.txt").call();
- git.commit().setMessage("edit file").call();
+ writeTrashFile("file.txt", join(content1));
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("edit file").call();
- writeTrashFile("file.txt", join(content2));
- git.add().addFilepattern("file.txt").call();
- RevCommit commit2 = git.commit().setMessage("create file").call();
+ writeTrashFile("file.txt", join(content2));
+ git.add().addFilepattern("file.txt").call();
+ RevCommit commit2 = git.commit().setMessage("create file").call();
- BlameCommand command = new BlameCommand(db);
+ BlameCommand command = new BlameCommand(db);
- command.setFilePath("file.txt");
- BlameResult lines = command.call();
- assertEquals(content2.length, lines.getResultContents().size());
- assertEquals(commit2, lines.getSourceCommit(0));
- assertEquals(commit2, lines.getSourceCommit(1));
+ command.setFilePath("file.txt");
+ BlameResult lines = command.call();
+ assertEquals(content2.length, lines.getResultContents().size());
+ assertEquals(commit2, lines.getSourceCommit(0));
+ assertEquals(commit2, lines.getSourceCommit(1));
+ }
}
@Test
public void testMiddleClearAllLines() throws Exception {
- Git git = new Git(db);
-
- String[] content1 = new String[] { "a", "b", "c" };
+ try (Git git = new Git(db)) {
+ String[] content1 = new String[] { "a", "b", "c" };
- writeTrashFile("file.txt", join(content1));
- git.add().addFilepattern("file.txt").call();
- git.commit().setMessage("edit file").call();
+ writeTrashFile("file.txt", join(content1));
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("edit file").call();
- writeTrashFile("file.txt", "");
- git.add().addFilepattern("file.txt").call();
- git.commit().setMessage("create file").call();
+ writeTrashFile("file.txt", "");
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("create file").call();
- writeTrashFile("file.txt", join(content1));
- git.add().addFilepattern("file.txt").call();
- RevCommit commit3 = git.commit().setMessage("edit file").call();
+ writeTrashFile("file.txt", join(content1));
+ git.add().addFilepattern("file.txt").call();
+ RevCommit commit3 = git.commit().setMessage("edit file").call();
- BlameCommand command = new BlameCommand(db);
+ BlameCommand command = new BlameCommand(db);
- command.setFilePath("file.txt");
- BlameResult lines = command.call();
- assertEquals(content1.length, lines.getResultContents().size());
- assertEquals(commit3, lines.getSourceCommit(0));
- assertEquals(commit3, lines.getSourceCommit(1));
- assertEquals(commit3, lines.getSourceCommit(2));
+ command.setFilePath("file.txt");
+ BlameResult lines = command.call();
+ assertEquals(content1.length, lines.getResultContents().size());
+ assertEquals(commit3, lines.getSourceCommit(0));
+ assertEquals(commit3, lines.getSourceCommit(1));
+ assertEquals(commit3, lines.getSourceCommit(2));
+ }
}
@Test
@@ -361,130 +361,132 @@ public class BlameCommandTest extends RepositoryTestCase {
private void testCoreAutoCrlf(AutoCRLF modeForCommitting,
AutoCRLF modeForReset) throws Exception {
- Git git = new Git(db);
- FileBasedConfig config = db.getConfig();
- config.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_AUTOCRLF, modeForCommitting);
- config.save();
-
- String joinedCrlf = "a\r\nb\r\nc\r\n";
- File trashFile = writeTrashFile("file.txt", joinedCrlf);
- git.add().addFilepattern("file.txt").call();
- RevCommit commit = git.commit().setMessage("create file").call();
-
- // re-create file from the repo
- trashFile.delete();
- config.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_AUTOCRLF, modeForReset);
- config.save();
- git.reset().setMode(ResetType.HARD).call();
-
- BlameCommand command = new BlameCommand(db);
- command.setFilePath("file.txt");
- BlameResult lines = command.call();
-
- assertEquals(3, lines.getResultContents().size());
- assertEquals(commit, lines.getSourceCommit(0));
- assertEquals(commit, lines.getSourceCommit(1));
- assertEquals(commit, lines.getSourceCommit(2));
+ try (Git git = new Git(db)) {
+ FileBasedConfig config = db.getConfig();
+ config.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_AUTOCRLF, modeForCommitting);
+ config.save();
+
+ String joinedCrlf = "a\r\nb\r\nc\r\n";
+ File trashFile = writeTrashFile("file.txt", joinedCrlf);
+ git.add().addFilepattern("file.txt").call();
+ RevCommit commit = git.commit().setMessage("create file").call();
+
+ // re-create file from the repo
+ trashFile.delete();
+ config.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_AUTOCRLF, modeForReset);
+ config.save();
+ git.reset().setMode(ResetType.HARD).call();
+
+ BlameCommand command = new BlameCommand(db);
+ command.setFilePath("file.txt");
+ BlameResult lines = command.call();
+
+ assertEquals(3, lines.getResultContents().size());
+ assertEquals(commit, lines.getSourceCommit(0));
+ assertEquals(commit, lines.getSourceCommit(1));
+ assertEquals(commit, lines.getSourceCommit(2));
+ }
}
@Test
public void testConflictingMerge1() throws Exception {
- Git git = new Git(db);
-
- RevCommit base = commitFile("file.txt", join("0", "1", "2", "3", "4"),
- "master");
-
- git.checkout().setName("side").setCreateBranch(true)
- .setStartPoint(base).call();
- RevCommit side = commitFile("file.txt",
- join("0", "1 side", "2", "3 on side", "4"), "side");
-
- commitFile("file.txt", join("0", "1", "2"), "master");
-
- checkoutBranch("refs/heads/master");
- git.merge().include(side).call();
-
- // The merge results in a conflict, which we resolve using mostly the
- // side branch contents. Especially the "4" survives.
- RevCommit merge = commitFile("file.txt",
- join("0", "1 side", "2", "3 resolved", "4"), "master");
-
- BlameCommand command = new BlameCommand(db);
- command.setFilePath("file.txt");
- BlameResult lines = command.call();
-
- assertEquals(5, lines.getResultContents().size());
- assertEquals(base, lines.getSourceCommit(0));
- assertEquals(side, lines.getSourceCommit(1));
- assertEquals(base, lines.getSourceCommit(2));
- assertEquals(merge, lines.getSourceCommit(3));
- assertEquals(base, lines.getSourceCommit(4));
+ try (Git git = new Git(db)) {
+ RevCommit base = commitFile("file.txt", join("0", "1", "2", "3", "4"),
+ "master");
+
+ git.checkout().setName("side").setCreateBranch(true)
+ .setStartPoint(base).call();
+ RevCommit side = commitFile("file.txt",
+ join("0", "1 side", "2", "3 on side", "4"), "side");
+
+ commitFile("file.txt", join("0", "1", "2"), "master");
+
+ checkoutBranch("refs/heads/master");
+ git.merge().include(side).call();
+
+ // The merge results in a conflict, which we resolve using mostly the
+ // side branch contents. Especially the "4" survives.
+ RevCommit merge = commitFile("file.txt",
+ join("0", "1 side", "2", "3 resolved", "4"), "master");
+
+ BlameCommand command = new BlameCommand(db);
+ command.setFilePath("file.txt");
+ BlameResult lines = command.call();
+
+ assertEquals(5, lines.getResultContents().size());
+ assertEquals(base, lines.getSourceCommit(0));
+ assertEquals(side, lines.getSourceCommit(1));
+ assertEquals(base, lines.getSourceCommit(2));
+ assertEquals(merge, lines.getSourceCommit(3));
+ assertEquals(base, lines.getSourceCommit(4));
+ }
}
// this test inverts the order of the master and side commit and is
// otherwise identical to testConflictingMerge1
@Test
public void testConflictingMerge2() throws Exception {
- Git git = new Git(db);
-
- RevCommit base = commitFile("file.txt", join("0", "1", "2", "3", "4"),
- "master");
-
- commitFile("file.txt", join("0", "1", "2"), "master");
-
- git.checkout().setName("side").setCreateBranch(true)
- .setStartPoint(base).call();
- RevCommit side = commitFile("file.txt",
- join("0", "1 side", "2", "3 on side", "4"), "side");
-
- checkoutBranch("refs/heads/master");
- git.merge().include(side).call();
-
- // The merge results in a conflict, which we resolve using mostly the
- // side branch contents. Especially the "4" survives.
- RevCommit merge = commitFile("file.txt",
- join("0", "1 side", "2", "3 resolved", "4"), "master");
-
- BlameCommand command = new BlameCommand(db);
- command.setFilePath("file.txt");
- BlameResult lines = command.call();
-
- assertEquals(5, lines.getResultContents().size());
- assertEquals(base, lines.getSourceCommit(0));
- assertEquals(side, lines.getSourceCommit(1));
- assertEquals(base, lines.getSourceCommit(2));
- assertEquals(merge, lines.getSourceCommit(3));
- assertEquals(base, lines.getSourceCommit(4));
+ try (Git git = new Git(db)) {
+ RevCommit base = commitFile("file.txt", join("0", "1", "2", "3", "4"),
+ "master");
+
+ commitFile("file.txt", join("0", "1", "2"), "master");
+
+ git.checkout().setName("side").setCreateBranch(true)
+ .setStartPoint(base).call();
+ RevCommit side = commitFile("file.txt",
+ join("0", "1 side", "2", "3 on side", "4"), "side");
+
+ checkoutBranch("refs/heads/master");
+ git.merge().include(side).call();
+
+ // The merge results in a conflict, which we resolve using mostly the
+ // side branch contents. Especially the "4" survives.
+ RevCommit merge = commitFile("file.txt",
+ join("0", "1 side", "2", "3 resolved", "4"), "master");
+
+ BlameCommand command = new BlameCommand(db);
+ command.setFilePath("file.txt");
+ BlameResult lines = command.call();
+
+ assertEquals(5, lines.getResultContents().size());
+ assertEquals(base, lines.getSourceCommit(0));
+ assertEquals(side, lines.getSourceCommit(1));
+ assertEquals(base, lines.getSourceCommit(2));
+ assertEquals(merge, lines.getSourceCommit(3));
+ assertEquals(base, lines.getSourceCommit(4));
+ }
}
@Test
public void testWhitespaceMerge() throws Exception {
- Git git = new Git(db);
- RevCommit base = commitFile("file.txt", join("0", "1", "2"), "master");
- RevCommit side = commitFile("file.txt", join("0", "1", " 2 side "),
- "side");
-
- checkoutBranch("refs/heads/master");
- git.merge().setFastForward(FastForwardMode.NO_FF).include(side).call();
-
- // change whitespace, so the merge content is not identical to side, but
- // is the same when ignoring whitespace
- writeTrashFile("file.txt", join("0", "1", "2 side"));
- RevCommit merge = git.commit().setAll(true).setMessage("merge")
- .setAmend(true)
- .call();
-
- BlameCommand command = new BlameCommand(db);
- command.setFilePath("file.txt")
- .setTextComparator(RawTextComparator.WS_IGNORE_ALL)
- .setStartCommit(merge.getId());
- BlameResult lines = command.call();
-
- assertEquals(3, lines.getResultContents().size());
- assertEquals(base, lines.getSourceCommit(0));
- assertEquals(base, lines.getSourceCommit(1));
- assertEquals(side, lines.getSourceCommit(2));
+ try (Git git = new Git(db)) {
+ RevCommit base = commitFile("file.txt", join("0", "1", "2"), "master");
+ RevCommit side = commitFile("file.txt", join("0", "1", " 2 side "),
+ "side");
+
+ checkoutBranch("refs/heads/master");
+ git.merge().setFastForward(FastForwardMode.NO_FF).include(side).call();
+
+ // change whitespace, so the merge content is not identical to side, but
+ // is the same when ignoring whitespace
+ writeTrashFile("file.txt", join("0", "1", "2 side"));
+ RevCommit merge = git.commit().setAll(true).setMessage("merge")
+ .setAmend(true)
+ .call();
+
+ BlameCommand command = new BlameCommand(db);
+ command.setFilePath("file.txt")
+ .setTextComparator(RawTextComparator.WS_IGNORE_ALL)
+ .setStartCommit(merge.getId());
+ BlameResult lines = command.call();
+
+ assertEquals(3, lines.getResultContents().size());
+ assertEquals(base, lines.getSourceCommit(0));
+ assertEquals(base, lines.getSourceCommit(1));
+ assertEquals(side, lines.getSourceCommit(2));
+ }
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BranchCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BranchCommandTest.java
index 910a645..2fe40b9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BranchCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BranchCommandTest.java
@@ -104,37 +104,38 @@ public class BranchCommandTest extends RepositoryTestCase {
private Git setUpRepoWithRemote() throws Exception {
Repository remoteRepository = createWorkRepository();
- Git remoteGit = new Git(remoteRepository);
- // commit something
- writeTrashFile("Test.txt", "Hello world");
- remoteGit.add().addFilepattern("Test.txt").call();
- initialCommit = remoteGit.commit().setMessage("Initial commit").call();
- writeTrashFile("Test.txt", "Some change");
- remoteGit.add().addFilepattern("Test.txt").call();
- secondCommit = remoteGit.commit().setMessage("Second commit").call();
- // create a master branch
- RefUpdate rup = remoteRepository.updateRef("refs/heads/master");
- rup.setNewObjectId(initialCommit.getId());
- rup.forceUpdate();
-
- Repository localRepository = createWorkRepository();
- Git localGit = new Git(localRepository);
- StoredConfig config = localRepository.getConfig();
- RemoteConfig rc = new RemoteConfig(config, "origin");
- rc.addURI(new URIish(remoteRepository.getDirectory().getAbsolutePath()));
- rc.addFetchRefSpec(new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
- rc.update(config);
- config.save();
- FetchResult res = localGit.fetch().setRemote("origin").call();
- assertFalse(res.getTrackingRefUpdates().isEmpty());
- rup = localRepository.updateRef("refs/heads/master");
- rup.setNewObjectId(initialCommit.getId());
- rup.forceUpdate();
- rup = localRepository.updateRef(Constants.HEAD);
- rup.link("refs/heads/master");
- rup.setNewObjectId(initialCommit.getId());
- rup.update();
- return localGit;
+ try (Git remoteGit = new Git(remoteRepository)) {
+ // commit something
+ writeTrashFile("Test.txt", "Hello world");
+ remoteGit.add().addFilepattern("Test.txt").call();
+ initialCommit = remoteGit.commit().setMessage("Initial commit").call();
+ writeTrashFile("Test.txt", "Some change");
+ remoteGit.add().addFilepattern("Test.txt").call();
+ secondCommit = remoteGit.commit().setMessage("Second commit").call();
+ // create a master branch
+ RefUpdate rup = remoteRepository.updateRef("refs/heads/master");
+ rup.setNewObjectId(initialCommit.getId());
+ rup.forceUpdate();
+
+ Repository localRepository = createWorkRepository();
+ Git localGit = new Git(localRepository);
+ StoredConfig config = localRepository.getConfig();
+ RemoteConfig rc = new RemoteConfig(config, "origin");
+ rc.addURI(new URIish(remoteRepository.getDirectory().getAbsolutePath()));
+ rc.addFetchRefSpec(new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
+ rc.update(config);
+ config.save();
+ FetchResult res = localGit.fetch().setRemote("origin").call();
+ assertFalse(res.getTrackingRefUpdates().isEmpty());
+ rup = localRepository.updateRef("refs/heads/master");
+ rup.setNewObjectId(initialCommit.getId());
+ rup.forceUpdate();
+ rup = localRepository.updateRef(Constants.HEAD);
+ rup.link("refs/heads/master");
+ rup.setNewObjectId(initialCommit.getId());
+ rup.update();
+ return localGit;
+ }
}
@Test
@@ -192,8 +193,7 @@ public class BranchCommandTest extends RepositoryTestCase {
@Test
public void testListAllBranchesShouldNotDie() throws Exception {
- Git git = setUpRepoWithRemote();
- git.branchList().setListMode(ListMode.ALL).call();
+ setUpRepoWithRemote().branchList().setListMode(ListMode.ALL).call();
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java
index f7a50df..362d7ac 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java
@@ -43,6 +43,8 @@
*/
package org.eclipse.jgit.api;
+import static org.eclipse.jgit.lib.Constants.MASTER;
+import static org.eclipse.jgit.lib.Constants.R_HEADS;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -70,12 +72,14 @@ import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.api.errors.TransportException;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.Sets;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.storage.file.FileBasedConfig;
@@ -84,6 +88,7 @@ import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.util.FileUtils;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
public class CheckoutCommandTest extends RepositoryTestCase {
@@ -134,7 +139,7 @@ public class CheckoutCommandTest extends RepositoryTestCase {
@Test
public void testCreateBranchOnCheckout() throws Exception {
git.checkout().setCreateBranch(true).setName("test2").call();
- assertNotNull(db.getRef("test2"));
+ assertNotNull(db.exactRef("refs/heads/test2"));
}
@Test
@@ -237,8 +242,8 @@ public class CheckoutCommandTest extends RepositoryTestCase {
.setStartPoint("origin/test")
.setUpstreamMode(SetupUpstreamMode.TRACK).call();
- assertEquals("refs/heads/test", db2.getRef(Constants.HEAD).getTarget()
- .getName());
+ assertEquals("refs/heads/test",
+ db2.exactRef(Constants.HEAD).getTarget().getName());
StoredConfig config = db2.getConfig();
assertEquals("origin", config.getString(
ConfigConstants.CONFIG_BRANCH_SECTION, "test",
@@ -345,7 +350,7 @@ public class CheckoutCommandTest extends RepositoryTestCase {
CheckoutCommand co = git.checkout();
co.setName("master").call();
- String commitId = db.getRef(Constants.MASTER).getObjectId().name();
+ String commitId = db.exactRef(R_HEADS + MASTER).getObjectId().name();
co = git.checkout();
co.setName(commitId).call();
@@ -412,20 +417,20 @@ public class CheckoutCommandTest extends RepositoryTestCase {
InvalidRemoteException, TransportException {
// create second repository
Repository db2 = createWorkRepository();
- Git git2 = new Git(db2);
-
- // setup the second repository to fetch from the first repository
- final StoredConfig config = db2.getConfig();
- RemoteConfig remoteConfig = new RemoteConfig(config, "origin");
- URIish uri = new URIish(db.getDirectory().toURI().toURL());
- remoteConfig.addURI(uri);
- remoteConfig.update(config);
- config.save();
-
- // fetch from first repository
- RefSpec spec = new RefSpec("+refs/heads/*:refs/remotes/origin/*");
- git2.fetch().setRemote("origin").setRefSpecs(spec).call();
- return db2;
+ try (Git git2 = new Git(db2)) {
+ // setup the second repository to fetch from the first repository
+ final StoredConfig config = db2.getConfig();
+ RemoteConfig remoteConfig = new RemoteConfig(config, "origin");
+ URIish uri = new URIish(db.getDirectory().toURI().toURL());
+ remoteConfig.addURI(uri);
+ remoteConfig.update(config);
+ config.save();
+
+ // fetch from first repository
+ RefSpec spec = new RefSpec("+refs/heads/*:refs/remotes/origin/*");
+ git2.fetch().setRemote("origin").setRefSpecs(spec).call();
+ return db2;
+ }
}
private CheckoutCommand newOrphanBranchCommand() {
@@ -443,7 +448,7 @@ public class CheckoutCommandTest extends RepositoryTestCase {
}
private void assertHeadDetached() throws IOException {
- Ref head = db.getRef(Constants.HEAD);
+ Ref head = db.exactRef(Constants.HEAD);
assertFalse(head.isSymbolic());
assertSame(head, head.getTarget());
}
@@ -554,4 +559,126 @@ public class CheckoutCommandTest extends RepositoryTestCase {
}
org.junit.Assume.assumeTrue(foundUnsmudged);
}
+
+ @Test
+ public void testSmudgeFilter_modifyExisting() throws IOException, GitAPIException {
+ File script = writeTempFile("sed s/o/e/g");
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("filter", "tstFilter", "smudge",
+ "sh " + slashify(script.getPath()));
+ config.save();
+
+ writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
+ git.add().addFilepattern(".gitattributes").call();
+ git.commit().setMessage("add filter").call();
+
+ writeTrashFile("src/a.tmp", "x");
+ // Caution: we need a trailing '\n' since sed on mac always appends
+ // linefeeds if missing
+ writeTrashFile("src/a.txt", "x\n");
+ git.add().addFilepattern("src/a.tmp").addFilepattern("src/a.txt")
+ .call();
+ RevCommit content1 = git.commit().setMessage("add content").call();
+
+ writeTrashFile("src/a.tmp", "foo");
+ writeTrashFile("src/a.txt", "foo\n");
+ git.add().addFilepattern("src/a.tmp").addFilepattern("src/a.txt")
+ .call();
+ RevCommit content2 = git.commit().setMessage("changed content").call();
+
+ git.checkout().setName(content1.getName()).call();
+ git.checkout().setName(content2.getName()).call();
+
+ assertEquals(
+ "[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some change][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:foo\n]",
+ indexState(CONTENT));
+ assertEquals(Sets.of("src/a.txt"), git.status().call().getModified());
+ assertEquals("foo", read("src/a.tmp"));
+ assertEquals("fee\n", read("src/a.txt"));
+ }
+
+ @Test
+ public void testSmudgeFilter_createNew()
+ throws IOException, GitAPIException {
+ File script = writeTempFile("sed s/o/e/g");
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("filter", "tstFilter", "smudge",
+ "sh " + slashify(script.getPath()));
+ config.save();
+
+ writeTrashFile("foo", "foo");
+ git.add().addFilepattern("foo").call();
+ RevCommit initial = git.commit().setMessage("initial").call();
+
+ writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
+ git.add().addFilepattern(".gitattributes").call();
+ git.commit().setMessage("add filter").call();
+
+ writeTrashFile("src/a.tmp", "foo");
+ // Caution: we need a trailing '\n' since sed on mac always appends
+ // linefeeds if missing
+ writeTrashFile("src/a.txt", "foo\n");
+ git.add().addFilepattern("src/a.tmp").addFilepattern("src/a.txt")
+ .call();
+ RevCommit content = git.commit().setMessage("added content").call();
+
+ git.checkout().setName(initial.getName()).call();
+ git.checkout().setName(content.getName()).call();
+
+ assertEquals(
+ "[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some change][foo, mode:100644, content:foo][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:foo\n]",
+ indexState(CONTENT));
+ assertEquals("foo", read("src/a.tmp"));
+ assertEquals("fee\n", read("src/a.txt"));
+ }
+
+ @Test
+ @Ignore
+ public void testSmudgeAndClean() throws IOException, GitAPIException {
+ // @TODO: fix this test
+ File clean_filter = writeTempFile("sed s/V1/@version/g -");
+ File smudge_filter = writeTempFile("sed s/@version/V1/g -");
+
+ try (Git git2 = new Git(db)) {
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("filter", "tstFilter", "smudge",
+ "sh " + slashify(smudge_filter.getPath()));
+ config.setString("filter", "tstFilter", "clean",
+ "sh " + slashify(clean_filter.getPath()));
+ config.save();
+ writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
+ git2.add().addFilepattern(".gitattributes").call();
+ git2.commit().setMessage("add attributes").call();
+
+ writeTrashFile("filterTest.txt", "hello world, V1");
+ git2.add().addFilepattern("filterTest.txt").call();
+ git2.commit().setMessage("add filterText.txt").call();
+ assertEquals(
+ "[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some other change][filterTest.txt, mode:100644, content:hello world, @version]",
+ indexState(CONTENT));
+
+ git2.checkout().setCreateBranch(true).setName("test2").call();
+ writeTrashFile("filterTest.txt", "bon giorno world, V1");
+ git2.add().addFilepattern("filterTest.txt").call();
+ git2.commit().setMessage("modified filterText.txt").call();
+
+ assertTrue(git2.status().call().isClean());
+ assertEquals(
+ "[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some other change][filterTest.txt, mode:100644, content:bon giorno world, @version]",
+ indexState(CONTENT));
+
+ git2.checkout().setName("refs/heads/test").call();
+ assertTrue(git2.status().call().isClean());
+ assertEquals(
+ "[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some other change][filterTest.txt, mode:100644, content:hello world, @version]",
+ indexState(CONTENT));
+ assertEquals("hello world, V1", read("filterTest.txt"));
+ }
+ }
+
+ private File writeTempFile(String body) throws IOException {
+ File f = File.createTempFile("AddCommandTest_", "");
+ JGitTestUtil.write(f, body);
+ return f;
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java
index a999337..354b9c6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CherryPickCommandTest.java
@@ -88,136 +88,139 @@ public class CherryPickCommandTest extends RepositoryTestCase {
private void doTestCherryPick(boolean noCommit) throws IOException,
JGitInternalException,
GitAPIException {
- Git git = new Git(db);
-
- writeTrashFile("a", "first line\nsec. line\nthird line\n");
- git.add().addFilepattern("a").call();
- RevCommit firstCommit = git.commit().setMessage("create a").call();
-
- writeTrashFile("b", "content\n");
- git.add().addFilepattern("b").call();
- git.commit().setMessage("create b").call();
-
- writeTrashFile("a", "first line\nsec. line\nthird line\nfourth line\n");
- git.add().addFilepattern("a").call();
- git.commit().setMessage("enlarged a").call();
-
- writeTrashFile("a",
- "first line\nsecond line\nthird line\nfourth line\n");
- git.add().addFilepattern("a").call();
- RevCommit fixingA = git.commit().setMessage("fixed a").call();
-
- git.branchCreate().setName("side").setStartPoint(firstCommit).call();
- checkoutBranch("refs/heads/side");
-
- writeTrashFile("a", "first line\nsec. line\nthird line\nfeature++\n");
- git.add().addFilepattern("a").call();
- git.commit().setMessage("enhanced a").call();
-
- CherryPickResult pickResult = git.cherryPick().include(fixingA)
- .setNoCommit(noCommit).call();
-
- assertEquals(CherryPickStatus.OK, pickResult.getStatus());
- assertFalse(new File(db.getWorkTree(), "b").exists());
- checkFile(new File(db.getWorkTree(), "a"),
- "first line\nsecond line\nthird line\nfeature++\n");
- Iterator<RevCommit> history = git.log().call().iterator();
- if (!noCommit)
- assertEquals("fixed a", history.next().getFullMessage());
- assertEquals("enhanced a", history.next().getFullMessage());
- assertEquals("create a", history.next().getFullMessage());
- assertFalse(history.hasNext());
+ try (Git git = new Git(db)) {
+ writeTrashFile("a", "first line\nsec. line\nthird line\n");
+ git.add().addFilepattern("a").call();
+ RevCommit firstCommit = git.commit().setMessage("create a").call();
+
+ writeTrashFile("b", "content\n");
+ git.add().addFilepattern("b").call();
+ git.commit().setMessage("create b").call();
+
+ writeTrashFile("a", "first line\nsec. line\nthird line\nfourth line\n");
+ git.add().addFilepattern("a").call();
+ git.commit().setMessage("enlarged a").call();
+
+ writeTrashFile("a",
+ "first line\nsecond line\nthird line\nfourth line\n");
+ git.add().addFilepattern("a").call();
+ RevCommit fixingA = git.commit().setMessage("fixed a").call();
+
+ git.branchCreate().setName("side").setStartPoint(firstCommit).call();
+ checkoutBranch("refs/heads/side");
+
+ writeTrashFile("a", "first line\nsec. line\nthird line\nfeature++\n");
+ git.add().addFilepattern("a").call();
+ git.commit().setMessage("enhanced a").call();
+
+ CherryPickResult pickResult = git.cherryPick().include(fixingA)
+ .setNoCommit(noCommit).call();
+
+ assertEquals(CherryPickStatus.OK, pickResult.getStatus());
+ assertFalse(new File(db.getWorkTree(), "b").exists());
+ checkFile(new File(db.getWorkTree(), "a"),
+ "first line\nsecond line\nthird line\nfeature++\n");
+ Iterator<RevCommit> history = git.log().call().iterator();
+ if (!noCommit)
+ assertEquals("fixed a", history.next().getFullMessage());
+ assertEquals("enhanced a", history.next().getFullMessage());
+ assertEquals("create a", history.next().getFullMessage());
+ assertFalse(history.hasNext());
+ }
}
@Test
public void testSequentialCherryPick() throws IOException, JGitInternalException,
GitAPIException {
- Git git = new Git(db);
-
- writeTrashFile("a", "first line\nsec. line\nthird line\n");
- git.add().addFilepattern("a").call();
- RevCommit firstCommit = git.commit().setMessage("create a").call();
-
- writeTrashFile("a", "first line\nsec. line\nthird line\nfourth line\n");
- git.add().addFilepattern("a").call();
- RevCommit enlargingA = git.commit().setMessage("enlarged a").call();
-
- writeTrashFile("a",
- "first line\nsecond line\nthird line\nfourth line\n");
- git.add().addFilepattern("a").call();
- RevCommit fixingA = git.commit().setMessage("fixed a").call();
-
- git.branchCreate().setName("side").setStartPoint(firstCommit).call();
- checkoutBranch("refs/heads/side");
-
- writeTrashFile("b", "nothing to do with a");
- git.add().addFilepattern("b").call();
- git.commit().setMessage("create b").call();
-
- CherryPickResult result = git.cherryPick().include(enlargingA).include(fixingA).call();
- assertEquals(CherryPickResult.CherryPickStatus.OK, result.getStatus());
-
- Iterator<RevCommit> history = git.log().call().iterator();
- assertEquals("fixed a", history.next().getFullMessage());
- assertEquals("enlarged a", history.next().getFullMessage());
- assertEquals("create b", history.next().getFullMessage());
- assertEquals("create a", history.next().getFullMessage());
- assertFalse(history.hasNext());
+ try (Git git = new Git(db)) {
+ writeTrashFile("a", "first line\nsec. line\nthird line\n");
+ git.add().addFilepattern("a").call();
+ RevCommit firstCommit = git.commit().setMessage("create a").call();
+
+ writeTrashFile("a", "first line\nsec. line\nthird line\nfourth line\n");
+ git.add().addFilepattern("a").call();
+ RevCommit enlargingA = git.commit().setMessage("enlarged a").call();
+
+ writeTrashFile("a",
+ "first line\nsecond line\nthird line\nfourth line\n");
+ git.add().addFilepattern("a").call();
+ RevCommit fixingA = git.commit().setMessage("fixed a").call();
+
+ git.branchCreate().setName("side").setStartPoint(firstCommit).call();
+ checkoutBranch("refs/heads/side");
+
+ writeTrashFile("b", "nothing to do with a");
+ git.add().addFilepattern("b").call();
+ git.commit().setMessage("create b").call();
+
+ CherryPickResult result = git.cherryPick().include(enlargingA).include(fixingA).call();
+ assertEquals(CherryPickResult.CherryPickStatus.OK, result.getStatus());
+
+ Iterator<RevCommit> history = git.log().call().iterator();
+ assertEquals("fixed a", history.next().getFullMessage());
+ assertEquals("enlarged a", history.next().getFullMessage());
+ assertEquals("create b", history.next().getFullMessage());
+ assertEquals("create a", history.next().getFullMessage());
+ assertFalse(history.hasNext());
+ }
}
@Test
public void testCherryPickDirtyIndex() throws Exception {
- Git git = new Git(db);
- RevCommit sideCommit = prepareCherryPick(git);
+ try (Git git = new Git(db)) {
+ RevCommit sideCommit = prepareCherryPick(git);
- // modify and add file a
- writeTrashFile("a", "a(modified)");
- git.add().addFilepattern("a").call();
- // do not commit
+ // modify and add file a
+ writeTrashFile("a", "a(modified)");
+ git.add().addFilepattern("a").call();
+ // do not commit
- doCherryPickAndCheckResult(git, sideCommit,
- MergeFailureReason.DIRTY_INDEX);
+ doCherryPickAndCheckResult(git, sideCommit,
+ MergeFailureReason.DIRTY_INDEX);
+ }
}
@Test
public void testCherryPickDirtyWorktree() throws Exception {
- Git git = new Git(db);
- RevCommit sideCommit = prepareCherryPick(git);
+ try (Git git = new Git(db)) {
+ RevCommit sideCommit = prepareCherryPick(git);
- // modify file a
- writeTrashFile("a", "a(modified)");
- // do not add and commit
+ // modify file a
+ writeTrashFile("a", "a(modified)");
+ // do not add and commit
- doCherryPickAndCheckResult(git, sideCommit,
- MergeFailureReason.DIRTY_WORKTREE);
+ doCherryPickAndCheckResult(git, sideCommit,
+ MergeFailureReason.DIRTY_WORKTREE);
+ }
}
@Test
public void testCherryPickConflictResolution() throws Exception {
- Git git = new Git(db);
- RevCommit sideCommit = prepareCherryPick(git);
+ try (Git git = new Git(db)) {
+ RevCommit sideCommit = prepareCherryPick(git);
- CherryPickResult result = git.cherryPick().include(sideCommit.getId())
- .call();
+ CherryPickResult result = git.cherryPick().include(sideCommit.getId())
+ .call();
- assertEquals(CherryPickStatus.CONFLICTING, result.getStatus());
- assertTrue(new File(db.getDirectory(), Constants.MERGE_MSG).exists());
- assertEquals("side\n\nConflicts:\n\ta\n", db.readMergeCommitMsg());
- assertTrue(new File(db.getDirectory(), Constants.CHERRY_PICK_HEAD)
- .exists());
- assertEquals(sideCommit.getId(), db.readCherryPickHead());
- assertEquals(RepositoryState.CHERRY_PICKING, db.getRepositoryState());
+ assertEquals(CherryPickStatus.CONFLICTING, result.getStatus());
+ assertTrue(new File(db.getDirectory(), Constants.MERGE_MSG).exists());
+ assertEquals("side\n\nConflicts:\n\ta\n", db.readMergeCommitMsg());
+ assertTrue(new File(db.getDirectory(), Constants.CHERRY_PICK_HEAD)
+ .exists());
+ assertEquals(sideCommit.getId(), db.readCherryPickHead());
+ assertEquals(RepositoryState.CHERRY_PICKING, db.getRepositoryState());
- // Resolve
- writeTrashFile("a", "a");
- git.add().addFilepattern("a").call();
+ // Resolve
+ writeTrashFile("a", "a");
+ git.add().addFilepattern("a").call();
- assertEquals(RepositoryState.CHERRY_PICKING_RESOLVED,
- db.getRepositoryState());
+ assertEquals(RepositoryState.CHERRY_PICKING_RESOLVED,
+ db.getRepositoryState());
- git.commit().setOnly("a").setMessage("resolve").call();
+ git.commit().setOnly("a").setMessage("resolve").call();
- assertEquals(RepositoryState.SAFE, db.getRepositoryState());
+ assertEquals(RepositoryState.SAFE, db.getRepositoryState());
+ }
}
@Test
@@ -251,85 +254,88 @@ public class CherryPickCommandTest extends RepositoryTestCase {
@Test
public void testCherryPickConflictReset() throws Exception {
- Git git = new Git(db);
-
- RevCommit sideCommit = prepareCherryPick(git);
+ try (Git git = new Git(db)) {
+ RevCommit sideCommit = prepareCherryPick(git);
- CherryPickResult result = git.cherryPick().include(sideCommit.getId())
- .call();
+ CherryPickResult result = git.cherryPick().include(sideCommit.getId())
+ .call();
- assertEquals(CherryPickStatus.CONFLICTING, result.getStatus());
- assertEquals(RepositoryState.CHERRY_PICKING, db.getRepositoryState());
- assertTrue(new File(db.getDirectory(), Constants.CHERRY_PICK_HEAD)
- .exists());
+ assertEquals(CherryPickStatus.CONFLICTING, result.getStatus());
+ assertEquals(RepositoryState.CHERRY_PICKING, db.getRepositoryState());
+ assertTrue(new File(db.getDirectory(), Constants.CHERRY_PICK_HEAD)
+ .exists());
- git.reset().setMode(ResetType.MIXED).setRef("HEAD").call();
+ git.reset().setMode(ResetType.MIXED).setRef("HEAD").call();
- assertEquals(RepositoryState.SAFE, db.getRepositoryState());
- assertFalse(new File(db.getDirectory(), Constants.CHERRY_PICK_HEAD)
- .exists());
+ assertEquals(RepositoryState.SAFE, db.getRepositoryState());
+ assertFalse(new File(db.getDirectory(), Constants.CHERRY_PICK_HEAD)
+ .exists());
+ }
}
@Test
public void testCherryPickOverExecutableChangeOnNonExectuableFileSystem()
throws Exception {
- Git git = new Git(db);
- File file = writeTrashFile("test.txt", "a");
- assertNotNull(git.add().addFilepattern("test.txt").call());
- assertNotNull(git.commit().setMessage("commit1").call());
+ try (Git git = new Git(db)) {
+ File file = writeTrashFile("test.txt", "a");
+ assertNotNull(git.add().addFilepattern("test.txt").call());
+ assertNotNull(git.commit().setMessage("commit1").call());
- assertNotNull(git.checkout().setCreateBranch(true).setName("a").call());
+ assertNotNull(git.checkout().setCreateBranch(true).setName("a").call());
- writeTrashFile("test.txt", "b");
- assertNotNull(git.add().addFilepattern("test.txt").call());
- RevCommit commit2 = git.commit().setMessage("commit2").call();
- assertNotNull(commit2);
+ writeTrashFile("test.txt", "b");
+ assertNotNull(git.add().addFilepattern("test.txt").call());
+ RevCommit commit2 = git.commit().setMessage("commit2").call();
+ assertNotNull(commit2);
- assertNotNull(git.checkout().setName(Constants.MASTER).call());
+ assertNotNull(git.checkout().setName(Constants.MASTER).call());
- DirCache cache = db.lockDirCache();
- cache.getEntry("test.txt").setFileMode(FileMode.EXECUTABLE_FILE);
- cache.write();
- assertTrue(cache.commit());
- cache.unlock();
+ DirCache cache = db.lockDirCache();
+ cache.getEntry("test.txt").setFileMode(FileMode.EXECUTABLE_FILE);
+ cache.write();
+ assertTrue(cache.commit());
+ cache.unlock();
- assertNotNull(git.commit().setMessage("commit3").call());
+ assertNotNull(git.commit().setMessage("commit3").call());
- db.getFS().setExecute(file, false);
- git.getRepository()
- .getConfig()
- .setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_FILEMODE, false);
+ db.getFS().setExecute(file, false);
+ git.getRepository()
+ .getConfig()
+ .setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_FILEMODE, false);
- CherryPickResult result = git.cherryPick().include(commit2).call();
- assertNotNull(result);
- assertEquals(CherryPickStatus.OK, result.getStatus());
+ CherryPickResult result = git.cherryPick().include(commit2).call();
+ assertNotNull(result);
+ assertEquals(CherryPickStatus.OK, result.getStatus());
+ }
}
@Test
public void testCherryPickConflictMarkers() throws Exception {
- Git git = new Git(db);
- RevCommit sideCommit = prepareCherryPick(git);
+ try (Git git = new Git(db)) {
+ RevCommit sideCommit = prepareCherryPick(git);
- CherryPickResult result = git.cherryPick().include(sideCommit.getId())
- .call();
- assertEquals(CherryPickStatus.CONFLICTING, result.getStatus());
+ CherryPickResult result = git.cherryPick().include(sideCommit.getId())
+ .call();
+ assertEquals(CherryPickStatus.CONFLICTING, result.getStatus());
- String expected = "<<<<<<< master\na(master)\n=======\na(side)\n>>>>>>> 527460a side\n";
- checkFile(new File(db.getWorkTree(), "a"), expected);
+ String expected = "<<<<<<< master\na(master)\n=======\na(side)\n>>>>>>> 527460a side\n";
+ checkFile(new File(db.getWorkTree(), "a"), expected);
+ }
}
@Test
public void testCherryPickOurCommitName() throws Exception {
- Git git = new Git(db);
- RevCommit sideCommit = prepareCherryPick(git);
+ try (Git git = new Git(db)) {
+ RevCommit sideCommit = prepareCherryPick(git);
- CherryPickResult result = git.cherryPick().include(sideCommit.getId())
- .setOurCommitName("custom name").call();
- assertEquals(CherryPickStatus.CONFLICTING, result.getStatus());
+ CherryPickResult result = git.cherryPick().include(sideCommit.getId())
+ .setOurCommitName("custom name").call();
+ assertEquals(CherryPickStatus.CONFLICTING, result.getStatus());
- String expected = "<<<<<<< custom name\na(master)\n=======\na(side)\n>>>>>>> 527460a side\n";
- checkFile(new File(db.getWorkTree(), "a"), expected);
+ String expected = "<<<<<<< custom name\na(master)\n=======\na(side)\n>>>>>>> 527460a side\n";
+ checkFile(new File(db.getWorkTree(), "a"), expected);
+ }
}
private RevCommit prepareCherryPick(final Git git) throws Exception {
@@ -399,43 +405,43 @@ public class CherryPickCommandTest extends RepositoryTestCase {
*/
@Test
public void testCherryPickMerge() throws Exception {
- Git git = new Git(db);
-
- commitFile("file", "1\n2\n3\n", "master");
- commitFile("file", "1\n2\n3\n", "side");
- checkoutBranch("refs/heads/side");
- RevCommit commitD = commitFile("file", "1\n2\n3\n4\n5\n", "side2");
- commitFile("file", "a\n2\n3\n", "side");
- MergeResult mergeResult = git.merge().include(commitD).call();
- ObjectId commitM = mergeResult.getNewHead();
- checkoutBranch("refs/heads/master");
- RevCommit commitT = commitFile("another", "t", "master");
-
- try {
- git.cherryPick().include(commitM).call();
- fail("merges should not be cherry-picked by default");
- } catch (MultipleParentsNotAllowedException e) {
- // expected
+ try (Git git = new Git(db)) {
+ commitFile("file", "1\n2\n3\n", "master");
+ commitFile("file", "1\n2\n3\n", "side");
+ checkoutBranch("refs/heads/side");
+ RevCommit commitD = commitFile("file", "1\n2\n3\n4\n5\n", "side2");
+ commitFile("file", "a\n2\n3\n", "side");
+ MergeResult mergeResult = git.merge().include(commitD).call();
+ ObjectId commitM = mergeResult.getNewHead();
+ checkoutBranch("refs/heads/master");
+ RevCommit commitT = commitFile("another", "t", "master");
+
+ try {
+ git.cherryPick().include(commitM).call();
+ fail("merges should not be cherry-picked by default");
+ } catch (MultipleParentsNotAllowedException e) {
+ // expected
+ }
+ try {
+ git.cherryPick().include(commitM).setMainlineParentNumber(3).call();
+ fail("specifying a non-existent parent should fail");
+ } catch (JGitInternalException e) {
+ // expected
+ assertTrue(e.getMessage().endsWith(
+ "does not have a parent number 3."));
+ }
+
+ CherryPickResult result = git.cherryPick().include(commitM)
+ .setMainlineParentNumber(1).call();
+ assertEquals(CherryPickStatus.OK, result.getStatus());
+ checkFile(new File(db.getWorkTree(), "file"), "1\n2\n3\n4\n5\n");
+
+ git.reset().setMode(ResetType.HARD).setRef(commitT.getName()).call();
+
+ CherryPickResult result2 = git.cherryPick().include(commitM)
+ .setMainlineParentNumber(2).call();
+ assertEquals(CherryPickStatus.OK, result2.getStatus());
+ checkFile(new File(db.getWorkTree(), "file"), "a\n2\n3\n");
}
- try {
- git.cherryPick().include(commitM).setMainlineParentNumber(3).call();
- fail("specifying a non-existent parent should fail");
- } catch (JGitInternalException e) {
- // expected
- assertTrue(e.getMessage().endsWith(
- "does not have a parent number 3."));
- }
-
- CherryPickResult result = git.cherryPick().include(commitM)
- .setMainlineParentNumber(1).call();
- assertEquals(CherryPickStatus.OK, result.getStatus());
- checkFile(new File(db.getWorkTree(), "file"), "1\n2\n3\n4\n5\n");
-
- git.reset().setMode(ResetType.HARD).setRef(commitT.getName()).call();
-
- CherryPickResult result2 = git.cherryPick().include(commitM)
- .setMainlineParentNumber(2).call();
- assertEquals(CherryPickStatus.OK, result2.getStatus());
- checkFile(new File(db.getWorkTree(), "file"), "a\n2\n3\n");
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
index ea07bee..ce11e1b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
@@ -202,6 +202,66 @@ public class CloneCommandTest extends RepositoryTestCase {
fetchRefSpec(git2.getRepository()));
}
+ @Test
+ public void testCloneRepositoryCustomRemote() throws Exception {
+ File directory = createTempDirectory("testCloneRemoteUpstream");
+ CloneCommand command = Git.cloneRepository();
+ command.setDirectory(directory);
+ command.setRemote("upstream");
+ command.setURI(fileUri());
+ Git git2 = command.call();
+ addRepoToClose(git2.getRepository());
+ assertEquals("+refs/heads/*:refs/remotes/upstream/*",
+ git2.getRepository()
+ .getConfig()
+ .getStringList("remote", "upstream",
+ "fetch")[0]);
+ assertEquals("upstream",
+ git2.getRepository()
+ .getConfig()
+ .getString("branch", "test", "remote"));
+ assertEquals(db.resolve("test"),
+ git2.getRepository().resolve("upstream/test"));
+ }
+
+ @Test
+ public void testBareCloneRepositoryCustomRemote() throws Exception {
+ File directory = createTempDirectory("testCloneRemoteUpstream_bare");
+ CloneCommand command = Git.cloneRepository();
+ command.setBare(true);
+ command.setDirectory(directory);
+ command.setRemote("upstream");
+ command.setURI(fileUri());
+ Git git2 = command.call();
+ addRepoToClose(git2.getRepository());
+ assertEquals("+refs/heads/*:refs/heads/*",
+ git2.getRepository()
+ .getConfig()
+ .getStringList("remote", "upstream",
+ "fetch")[0]);
+ assertEquals("upstream",
+ git2.getRepository()
+ .getConfig()
+ .getString("branch", "test", "remote"));
+ assertNull(git2.getRepository().resolve("upstream/test"));
+ }
+
+ @Test
+ public void testBareCloneRepositoryNullRemote() throws Exception {
+ File directory = createTempDirectory("testCloneRemoteNull_bare");
+ CloneCommand command = Git.cloneRepository();
+ command.setBare(true);
+ command.setDirectory(directory);
+ command.setRemote(null);
+ command.setURI(fileUri());
+ Git git2 = command.call();
+ addRepoToClose(git2.getRepository());
+ assertEquals("+refs/heads/*:refs/heads/*", git2.getRepository()
+ .getConfig().getStringList("remote", "origin", "fetch")[0]);
+ assertEquals("origin", git2.getRepository().getConfig()
+ .getString("branch", "test", "remote"));
+ }
+
public static RefSpec fetchRefSpec(Repository r) throws URISyntaxException {
RemoteConfig remoteConfig =
new RemoteConfig(r.getConfig(), Constants.DEFAULT_REMOTE_NAME);
@@ -391,6 +451,17 @@ public class CloneCommandTest extends RepositoryTestCase {
git.add().addFilepattern(path)
.addFilepattern(Constants.DOT_GIT_MODULES).call();
git.commit().setMessage("adding submodule").call();
+ try (SubmoduleWalk walk = SubmoduleWalk.forIndex(git.getRepository())) {
+ assertTrue(walk.next());
+ Repository subRepo = walk.getRepository();
+ addRepoToClose(subRepo);
+ assertNotNull(subRepo);
+ assertEquals(
+ new File(git.getRepository().getWorkTree(), walk.getPath()),
+ subRepo.getWorkTree());
+ assertEquals(new File(new File(git.getRepository().getDirectory(),
+ "modules"), walk.getPath()), subRepo.getDirectory());
+ }
File directory = createTempDirectory("testCloneRepositoryWithSubmodules");
CloneCommand clone = Git.cloneRepository();
@@ -414,17 +485,19 @@ public class CloneCommandTest extends RepositoryTestCase {
assertEquals(commit, pathStatus.getHeadId());
assertEquals(commit, pathStatus.getIndexId());
- SubmoduleWalk walk = SubmoduleWalk.forIndex(git2.getRepository());
- assertTrue(walk.next());
- Repository clonedSub1 = walk.getRepository();
- addRepoToClose(clonedSub1);
- assertNotNull(clonedSub1);
- assertEquals(
- new File(git2.getRepository().getWorkTree(), walk.getPath()),
- clonedSub1.getWorkTree());
- assertEquals(new File(new File(git2.getRepository().getDirectory(),
- "modules"), walk.getPath()), clonedSub1.getDirectory());
- walk.release();
+ try (SubmoduleWalk walk = SubmoduleWalk
+ .forIndex(git2.getRepository())) {
+ assertTrue(walk.next());
+ Repository clonedSub1 = walk.getRepository();
+ addRepoToClose(clonedSub1);
+ assertNotNull(clonedSub1);
+ assertEquals(new File(git2.getRepository().getWorkTree(),
+ walk.getPath()), clonedSub1.getWorkTree());
+ assertEquals(
+ new File(new File(git2.getRepository().getDirectory(),
+ "modules"), walk.getPath()),
+ clonedSub1.getDirectory());
+ }
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java
index 7b3d4f6..1d5c674 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitAndLogCommandTest.java
@@ -46,6 +46,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
import java.io.File;
import java.io.IOException;
@@ -78,96 +79,96 @@ public class CommitAndLogCommandTest extends RepositoryTestCase {
GitAPIException {
// do 4 commits
- Git git = new Git(db);
- git.commit().setMessage("initial commit").call();
- git.commit().setMessage("second commit").setCommitter(committer).call();
- git.commit().setMessage("third commit").setAuthor(author).call();
- git.commit().setMessage("fourth commit").setAuthor(author)
- .setCommitter(committer).call();
- Iterable<RevCommit> commits = git.log().call();
-
- // check that all commits came in correctly
- PersonIdent defaultCommitter = new PersonIdent(db);
- PersonIdent expectedAuthors[] = new PersonIdent[] { defaultCommitter,
- committer, author, author };
- PersonIdent expectedCommitters[] = new PersonIdent[] {
- defaultCommitter, committer, defaultCommitter, committer };
- String expectedMessages[] = new String[] { "initial commit",
- "second commit", "third commit", "fourth commit" };
- int l = expectedAuthors.length - 1;
- for (RevCommit c : commits) {
- assertEquals(expectedAuthors[l].getName(), c.getAuthorIdent()
- .getName());
- assertEquals(expectedCommitters[l].getName(), c.getCommitterIdent()
- .getName());
- assertEquals(c.getFullMessage(), expectedMessages[l]);
- l--;
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("initial commit").call();
+ git.commit().setMessage("second commit").setCommitter(committer).call();
+ git.commit().setMessage("third commit").setAuthor(author).call();
+ git.commit().setMessage("fourth commit").setAuthor(author)
+ .setCommitter(committer).call();
+ Iterable<RevCommit> commits = git.log().call();
+
+ // check that all commits came in correctly
+ PersonIdent defaultCommitter = new PersonIdent(db);
+ PersonIdent expectedAuthors[] = new PersonIdent[] { defaultCommitter,
+ committer, author, author };
+ PersonIdent expectedCommitters[] = new PersonIdent[] {
+ defaultCommitter, committer, defaultCommitter, committer };
+ String expectedMessages[] = new String[] { "initial commit",
+ "second commit", "third commit", "fourth commit" };
+ int l = expectedAuthors.length - 1;
+ for (RevCommit c : commits) {
+ assertEquals(expectedAuthors[l].getName(), c.getAuthorIdent()
+ .getName());
+ assertEquals(expectedCommitters[l].getName(), c.getCommitterIdent()
+ .getName());
+ assertEquals(c.getFullMessage(), expectedMessages[l]);
+ l--;
+ }
+ assertEquals(l, -1);
+ ReflogReader reader = db.getReflogReader(Constants.HEAD);
+ assertTrue(reader.getLastEntry().getComment().startsWith("commit:"));
+ reader = db.getReflogReader(db.getBranch());
+ assertTrue(reader.getLastEntry().getComment().startsWith("commit:"));
}
- assertEquals(l, -1);
- ReflogReader reader = db.getReflogReader(Constants.HEAD);
- assertTrue(reader.getLastEntry().getComment().startsWith("commit:"));
- reader = db.getReflogReader(db.getBranch());
- assertTrue(reader.getLastEntry().getComment().startsWith("commit:"));
}
@Test
public void testLogWithFilter() throws IOException, JGitInternalException,
GitAPIException {
- Git git = new Git(db);
-
- // create first file
- File file = new File(db.getWorkTree(), "a.txt");
- FileUtils.createNewFile(file);
- PrintWriter writer = new PrintWriter(file);
- writer.print("content1");
- writer.close();
-
- // First commit - a.txt file
- git.add().addFilepattern("a.txt").call();
- git.commit().setMessage("commit1").setCommitter(committer).call();
-
- // create second file
- file = new File(db.getWorkTree(), "b.txt");
- FileUtils.createNewFile(file);
- writer = new PrintWriter(file);
- writer.print("content2");
- writer.close();
-
- // Second commit - b.txt file
- git.add().addFilepattern("b.txt").call();
- git.commit().setMessage("commit2").setCommitter(committer).call();
-
- // First log - a.txt filter
- int count = 0;
- for (RevCommit c : git.log().addPath("a.txt").call()) {
- assertEquals("commit1", c.getFullMessage());
- count++;
- }
- assertEquals(1, count);
-
- // Second log - b.txt filter
- count = 0;
- for (RevCommit c : git.log().addPath("b.txt").call()) {
- assertEquals("commit2", c.getFullMessage());
- count++;
- }
- assertEquals(1, count);
-
- // Third log - without filter
- count = 0;
- for (RevCommit c : git.log().call()) {
- assertEquals(committer, c.getCommitterIdent());
- count++;
+ try (Git git = new Git(db)) {
+ // create first file
+ File file = new File(db.getWorkTree(), "a.txt");
+ FileUtils.createNewFile(file);
+ PrintWriter writer = new PrintWriter(file);
+ writer.print("content1");
+ writer.close();
+
+ // First commit - a.txt file
+ git.add().addFilepattern("a.txt").call();
+ git.commit().setMessage("commit1").setCommitter(committer).call();
+
+ // create second file
+ file = new File(db.getWorkTree(), "b.txt");
+ FileUtils.createNewFile(file);
+ writer = new PrintWriter(file);
+ writer.print("content2");
+ writer.close();
+
+ // Second commit - b.txt file
+ git.add().addFilepattern("b.txt").call();
+ git.commit().setMessage("commit2").setCommitter(committer).call();
+
+ // First log - a.txt filter
+ int count = 0;
+ for (RevCommit c : git.log().addPath("a.txt").call()) {
+ assertEquals("commit1", c.getFullMessage());
+ count++;
+ }
+ assertEquals(1, count);
+
+ // Second log - b.txt filter
+ count = 0;
+ for (RevCommit c : git.log().addPath("b.txt").call()) {
+ assertEquals("commit2", c.getFullMessage());
+ count++;
+ }
+ assertEquals(1, count);
+
+ // Third log - without filter
+ count = 0;
+ for (RevCommit c : git.log().call()) {
+ assertEquals(committer, c.getCommitterIdent());
+ count++;
+ }
+ assertEquals(2, count);
}
- assertEquals(2, count);
}
// try to do a commit without specifying a message. Should fail!
@Test
public void testWrongParams() throws GitAPIException {
- Git git = new Git(db);
- try {
+ try (Git git = new Git(db)) {
git.commit().setAuthor(author).call();
fail("Didn't get the expected exception");
} catch (NoMessageException e) {
@@ -179,48 +180,50 @@ public class CommitAndLogCommandTest extends RepositoryTestCase {
// exceptions
@Test
public void testMultipleInvocations() throws GitAPIException {
- Git git = new Git(db);
- CommitCommand commitCmd = git.commit();
- commitCmd.setMessage("initial commit").call();
- try {
- // check that setters can't be called after invocation
- commitCmd.setAuthor(author);
- fail("didn't catch the expected exception");
- } catch (IllegalStateException e) {
- // expected
- }
- LogCommand logCmd = git.log();
- logCmd.call();
- try {
- // check that call can't be called twice
+ try (Git git = new Git(db)) {
+ CommitCommand commitCmd = git.commit();
+ commitCmd.setMessage("initial commit").call();
+ try {
+ // check that setters can't be called after invocation
+ commitCmd.setAuthor(author);
+ fail("didn't catch the expected exception");
+ } catch (IllegalStateException e) {
+ // expected
+ }
+ LogCommand logCmd = git.log();
logCmd.call();
- fail("didn't catch the expected exception");
- } catch (IllegalStateException e) {
- // expected
+ try {
+ // check that call can't be called twice
+ logCmd.call();
+ fail("didn't catch the expected exception");
+ } catch (IllegalStateException e) {
+ // expected
+ }
}
}
@Test
public void testMergeEmptyBranches() throws IOException,
JGitInternalException, GitAPIException {
- Git git = new Git(db);
- git.commit().setMessage("initial commit").call();
- RefUpdate r = db.updateRef("refs/heads/side");
- r.setNewObjectId(db.resolve(Constants.HEAD));
- assertEquals(r.forceUpdate(), RefUpdate.Result.NEW);
- RevCommit second = git.commit().setMessage("second commit").setCommitter(committer).call();
- db.updateRef(Constants.HEAD).link("refs/heads/side");
- RevCommit firstSide = git.commit().setMessage("first side commit").setAuthor(author).call();
-
- write(new File(db.getDirectory(), Constants.MERGE_HEAD), ObjectId
- .toString(db.resolve("refs/heads/master")));
- write(new File(db.getDirectory(), Constants.MERGE_MSG), "merging");
-
- RevCommit commit = git.commit().call();
- RevCommit[] parents = commit.getParents();
- assertEquals(parents[0], firstSide);
- assertEquals(parents[1], second);
- assertEquals(2, parents.length);
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("initial commit").call();
+ RefUpdate r = db.updateRef("refs/heads/side");
+ r.setNewObjectId(db.resolve(Constants.HEAD));
+ assertEquals(r.forceUpdate(), RefUpdate.Result.NEW);
+ RevCommit second = git.commit().setMessage("second commit").setCommitter(committer).call();
+ db.updateRef(Constants.HEAD).link("refs/heads/side");
+ RevCommit firstSide = git.commit().setMessage("first side commit").setAuthor(author).call();
+
+ write(new File(db.getDirectory(), Constants.MERGE_HEAD), ObjectId
+ .toString(db.resolve("refs/heads/master")));
+ write(new File(db.getDirectory(), Constants.MERGE_MSG), "merging");
+
+ RevCommit commit = git.commit().call();
+ RevCommit[] parents = commit.getParents();
+ assertEquals(parents[0], firstSide);
+ assertEquals(parents[1], second);
+ assertEquals(2, parents.length);
+ }
}
@Test
@@ -232,56 +235,56 @@ public class CommitAndLogCommandTest extends RepositoryTestCase {
writer.print("content");
writer.close();
- Git git = new Git(db);
- git.add().addFilepattern("a.txt").call();
- RevCommit commit = git.commit().setMessage("initial commit").call();
- TreeWalk tw = TreeWalk.forPath(db, "a.txt", commit.getTree());
- assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea",
- tw.getObjectId(0).getName());
-
- writer = new PrintWriter(file);
- writer.print("content2");
- writer.close();
- commit = git.commit().setMessage("second commit").call();
- tw = TreeWalk.forPath(db, "a.txt", commit.getTree());
- assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea",
- tw.getObjectId(0).getName());
-
- commit = git.commit().setAll(true).setMessage("third commit")
- .setAll(true).call();
- tw = TreeWalk.forPath(db, "a.txt", commit.getTree());
- assertEquals("db00fd65b218578127ea51f3dffac701f12f486a",
- tw.getObjectId(0).getName());
+ try (Git git = new Git(db)) {
+ git.add().addFilepattern("a.txt").call();
+ RevCommit commit = git.commit().setMessage("initial commit").call();
+ TreeWalk tw = TreeWalk.forPath(db, "a.txt", commit.getTree());
+ assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea",
+ tw.getObjectId(0).getName());
+
+ writer = new PrintWriter(file);
+ writer.print("content2");
+ writer.close();
+ commit = git.commit().setMessage("second commit").call();
+ tw = TreeWalk.forPath(db, "a.txt", commit.getTree());
+ assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea",
+ tw.getObjectId(0).getName());
+
+ commit = git.commit().setAll(true).setMessage("third commit")
+ .setAll(true).call();
+ tw = TreeWalk.forPath(db, "a.txt", commit.getTree());
+ assertEquals("db00fd65b218578127ea51f3dffac701f12f486a",
+ tw.getObjectId(0).getName());
+ }
}
@Test
public void testModeChange() throws IOException, GitAPIException {
- if (System.getProperty("os.name").startsWith("Windows"))
- return; // SKIP
- Git git = new Git(db);
-
- // create file
- File file = new File(db.getWorkTree(), "a.txt");
- FileUtils.createNewFile(file);
- PrintWriter writer = new PrintWriter(file);
- writer.print("content1");
- writer.close();
-
- // First commit - a.txt file
- git.add().addFilepattern("a.txt").call();
- git.commit().setMessage("commit1").setCommitter(committer).call();
-
- // pure mode change should be committable
- FS fs = db.getFS();
- fs.setExecute(file, true);
- git.add().addFilepattern("a.txt").call();
- git.commit().setMessage("mode change").setCommitter(committer).call();
-
- // pure mode change should be committable with -o option
- fs.setExecute(file, false);
- git.add().addFilepattern("a.txt").call();
- git.commit().setMessage("mode change").setCommitter(committer)
- .setOnly("a.txt").call();
+ assumeFalse(System.getProperty("os.name").startsWith("Windows"));// SKIP
+ try (Git git = new Git(db)) {
+ // create file
+ File file = new File(db.getWorkTree(), "a.txt");
+ FileUtils.createNewFile(file);
+ PrintWriter writer = new PrintWriter(file);
+ writer.print("content1");
+ writer.close();
+
+ // First commit - a.txt file
+ git.add().addFilepattern("a.txt").call();
+ git.commit().setMessage("commit1").setCommitter(committer).call();
+
+ // pure mode change should be committable
+ FS fs = db.getFS();
+ fs.setExecute(file, true);
+ git.add().addFilepattern("a.txt").call();
+ git.commit().setMessage("mode change").setCommitter(committer).call();
+
+ // pure mode change should be committable with -o option
+ fs.setExecute(file, false);
+ git.add().addFilepattern("a.txt").call();
+ git.commit().setMessage("mode change").setCommitter(committer)
+ .setOnly("a.txt").call();
+ }
}
@Test
@@ -289,112 +292,115 @@ public class CommitAndLogCommandTest extends RepositoryTestCase {
JGitInternalException, MissingObjectException,
IncorrectObjectTypeException {
// do 4 commits and set the range to the second and fourth one
- Git git = new Git(db);
- git.commit().setMessage("first commit").call();
- RevCommit second = git.commit().setMessage("second commit")
- .setCommitter(committer).call();
- git.commit().setMessage("third commit").setAuthor(author).call();
- RevCommit last = git.commit().setMessage("fourth commit").setAuthor(
- author)
- .setCommitter(committer).call();
- Iterable<RevCommit> commits = git.log().addRange(second.getId(),
- last.getId()).call();
-
- // check that we have the third and fourth commit
- PersonIdent defaultCommitter = new PersonIdent(db);
- PersonIdent expectedAuthors[] = new PersonIdent[] { author, author };
- PersonIdent expectedCommitters[] = new PersonIdent[] {
- defaultCommitter, committer };
- String expectedMessages[] = new String[] { "third commit",
- "fourth commit" };
- int l = expectedAuthors.length - 1;
- for (RevCommit c : commits) {
- assertEquals(expectedAuthors[l].getName(), c.getAuthorIdent()
- .getName());
- assertEquals(expectedCommitters[l].getName(), c.getCommitterIdent()
- .getName());
- assertEquals(c.getFullMessage(), expectedMessages[l]);
- l--;
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("first commit").call();
+ RevCommit second = git.commit().setMessage("second commit")
+ .setCommitter(committer).call();
+ git.commit().setMessage("third commit").setAuthor(author).call();
+ RevCommit last = git.commit().setMessage("fourth commit").setAuthor(
+ author)
+ .setCommitter(committer).call();
+ Iterable<RevCommit> commits = git.log().addRange(second.getId(),
+ last.getId()).call();
+
+ // check that we have the third and fourth commit
+ PersonIdent defaultCommitter = new PersonIdent(db);
+ PersonIdent expectedAuthors[] = new PersonIdent[] { author, author };
+ PersonIdent expectedCommitters[] = new PersonIdent[] {
+ defaultCommitter, committer };
+ String expectedMessages[] = new String[] { "third commit",
+ "fourth commit" };
+ int l = expectedAuthors.length - 1;
+ for (RevCommit c : commits) {
+ assertEquals(expectedAuthors[l].getName(), c.getAuthorIdent()
+ .getName());
+ assertEquals(expectedCommitters[l].getName(), c.getCommitterIdent()
+ .getName());
+ assertEquals(c.getFullMessage(), expectedMessages[l]);
+ l--;
+ }
+ assertEquals(l, -1);
}
- assertEquals(l, -1);
}
@Test
public void testCommitAmend() throws JGitInternalException, IOException,
GitAPIException {
- Git git = new Git(db);
- git.commit().setMessage("first comit").call(); // typo
- git.commit().setAmend(true).setMessage("first commit").call();
-
- Iterable<RevCommit> commits = git.log().call();
- int c = 0;
- for (RevCommit commit : commits) {
- assertEquals("first commit", commit.getFullMessage());
- c++;
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("first comit").call(); // typo
+ git.commit().setAmend(true).setMessage("first commit").call();
+
+ Iterable<RevCommit> commits = git.log().call();
+ int c = 0;
+ for (RevCommit commit : commits) {
+ assertEquals("first commit", commit.getFullMessage());
+ c++;
+ }
+ assertEquals(1, c);
+ ReflogReader reader = db.getReflogReader(Constants.HEAD);
+ assertTrue(reader.getLastEntry().getComment()
+ .startsWith("commit (amend):"));
+ reader = db.getReflogReader(db.getBranch());
+ assertTrue(reader.getLastEntry().getComment()
+ .startsWith("commit (amend):"));
}
- assertEquals(1, c);
- ReflogReader reader = db.getReflogReader(Constants.HEAD);
- assertTrue(reader.getLastEntry().getComment()
- .startsWith("commit (amend):"));
- reader = db.getReflogReader(db.getBranch());
- assertTrue(reader.getLastEntry().getComment()
- .startsWith("commit (amend):"));
}
@Test
public void testInsertChangeId() throws JGitInternalException,
GitAPIException {
- Git git = new Git(db);
- String messageHeader = "Some header line\n\nSome detail explanation\n";
- String changeIdTemplate = "\nChange-Id: I"
- + ObjectId.zeroId().getName() + "\n";
- String messageFooter = "Some foooter lines\nAnother footer line\n";
- RevCommit commit = git.commit().setMessage(
- messageHeader + messageFooter)
- .setInsertChangeId(true).call();
- // we should find a real change id (at the end of the file)
- byte[] chars = commit.getFullMessage().getBytes();
- int lastLineBegin = RawParseUtils.prevLF(chars, chars.length - 2);
- String lastLine = RawParseUtils.decode(chars, lastLineBegin + 1,
- chars.length);
- assertTrue(lastLine.contains("Change-Id:"));
- assertFalse(lastLine.contains(
- "Change-Id: I" + ObjectId.zeroId().getName()));
-
- commit = git.commit().setMessage(
- messageHeader + changeIdTemplate + messageFooter)
- .setInsertChangeId(true).call();
- // we should find a real change id (in the line as dictated by the
- // template)
- chars = commit.getFullMessage().getBytes();
- int lineStart = 0;
- int lineEnd = 0;
- for (int i = 0; i < 4; i++) {
- lineStart = RawParseUtils.nextLF(chars, lineStart);
- }
- lineEnd = RawParseUtils.nextLF(chars, lineStart);
-
- String line = RawParseUtils.decode(chars, lineStart, lineEnd);
-
- assertTrue(line.contains("Change-Id:"));
- assertFalse(line.contains(
- "Change-Id: I" + ObjectId.zeroId().getName()));
-
- commit = git.commit().setMessage(
- messageHeader + changeIdTemplate + messageFooter)
- .setInsertChangeId(false).call();
- // we should find the untouched template
- chars = commit.getFullMessage().getBytes();
- lineStart = 0;
- lineEnd = 0;
- for (int i = 0; i < 4; i++) {
- lineStart = RawParseUtils.nextLF(chars, lineStart);
+ try (Git git = new Git(db)) {
+ String messageHeader = "Some header line\n\nSome detail explanation\n";
+ String changeIdTemplate = "\nChange-Id: I"
+ + ObjectId.zeroId().getName() + "\n";
+ String messageFooter = "Some foooter lines\nAnother footer line\n";
+ RevCommit commit = git.commit().setMessage(
+ messageHeader + messageFooter)
+ .setInsertChangeId(true).call();
+ // we should find a real change id (at the end of the file)
+ byte[] chars = commit.getFullMessage().getBytes();
+ int lastLineBegin = RawParseUtils.prevLF(chars, chars.length - 2);
+ String lastLine = RawParseUtils.decode(chars, lastLineBegin + 1,
+ chars.length);
+ assertTrue(lastLine.contains("Change-Id:"));
+ assertFalse(lastLine.contains(
+ "Change-Id: I" + ObjectId.zeroId().getName()));
+
+ commit = git.commit().setMessage(
+ messageHeader + changeIdTemplate + messageFooter)
+ .setInsertChangeId(true).call();
+ // we should find a real change id (in the line as dictated by the
+ // template)
+ chars = commit.getFullMessage().getBytes();
+ int lineStart = 0;
+ int lineEnd = 0;
+ for (int i = 0; i < 4; i++) {
+ lineStart = RawParseUtils.nextLF(chars, lineStart);
+ }
+ lineEnd = RawParseUtils.nextLF(chars, lineStart);
+
+ String line = RawParseUtils.decode(chars, lineStart, lineEnd);
+
+ assertTrue(line.contains("Change-Id:"));
+ assertFalse(line.contains(
+ "Change-Id: I" + ObjectId.zeroId().getName()));
+
+ commit = git.commit().setMessage(
+ messageHeader + changeIdTemplate + messageFooter)
+ .setInsertChangeId(false).call();
+ // we should find the untouched template
+ chars = commit.getFullMessage().getBytes();
+ lineStart = 0;
+ lineEnd = 0;
+ for (int i = 0; i < 4; i++) {
+ lineStart = RawParseUtils.nextLF(chars, lineStart);
+ }
+ lineEnd = RawParseUtils.nextLF(chars, lineStart);
+
+ line = RawParseUtils.decode(chars, lineStart, lineEnd);
+
+ assertTrue(commit.getFullMessage().contains(
+ "Change-Id: I" + ObjectId.zeroId().getName()));
}
- lineEnd = RawParseUtils.nextLF(chars, lineStart);
-
- line = RawParseUtils.decode(chars, lineStart, lineEnd);
-
- assertTrue(commit.getFullMessage().contains(
- "Change-Id: I" + ObjectId.zeroId().getName()));
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
index 1f71402..b39a68a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
@@ -46,12 +46,15 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.fail;
import java.io.File;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
+import org.eclipse.jgit.api.errors.EmtpyCommitException;
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.dircache.DirCache;
@@ -108,7 +111,7 @@ public class CommitCommandTest extends RepositoryTestCase {
return this;
}
- protected File discoverGitPrefix() {
+ protected File discoverGitExe() {
return null;
}
@@ -153,7 +156,7 @@ public class CommitCommandTest extends RepositoryTestCase {
return this;
}
- protected File discoverGitPrefix() {
+ protected File discoverGitExe() {
return null;
}
@@ -408,8 +411,10 @@ public class CommitCommandTest extends RepositoryTestCase {
checkoutBranch("refs/heads/master");
- MergeResult result = git.merge().include(db.getRef("branch1"))
- .setSquash(true).call();
+ MergeResult result = git.merge()
+ .include(db.exactRef("refs/heads/branch1"))
+ .setSquash(true)
+ .call();
assertTrue(new File(db.getWorkTree(), "file1").exists());
assertTrue(new File(db.getWorkTree(), "file2").exists());
@@ -475,6 +480,34 @@ public class CommitCommandTest extends RepositoryTestCase {
}
@Test
+ public void commitEmptyCommits() throws Exception {
+ try (Git git = new Git(db)) {
+
+ writeTrashFile("file1", "file1");
+ git.add().addFilepattern("file1").call();
+ RevCommit initial = git.commit().setMessage("initial commit")
+ .call();
+
+ RevCommit emptyFollowUp = git.commit()
+ .setAuthor("New Author", "newauthor at example.org")
+ .setMessage("no change").call();
+
+ assertNotEquals(initial.getId(), emptyFollowUp.getId());
+ assertEquals(initial.getTree().getId(),
+ emptyFollowUp.getTree().getId());
+
+ try {
+ git.commit().setAuthor("New Author", "newauthor at example.org")
+ .setMessage("again no change").setAllowEmpty(false)
+ .call();
+ fail("Didn't get the expected EmtpyCommitException");
+ } catch (EmtpyCommitException e) {
+ // expect this exception
+ }
+ }
+ }
+
+ @Test
public void commitOnlyShouldCommitUnmergedPathAndNotAffectOthers()
throws Exception {
DirCache index = db.lockDirCache();
@@ -509,9 +542,20 @@ public class CommitCommandTest extends RepositoryTestCase {
+ "[unmerged2, mode:100644, stage:3]",
indexState(0));
- TreeWalk walk = TreeWalk.forPath(db, "unmerged1", commit.getTree());
- assertEquals(FileMode.REGULAR_FILE, walk.getFileMode(0));
- walk.release();
+ try (TreeWalk walk = TreeWalk.forPath(db, "unmerged1", commit.getTree())) {
+ assertEquals(FileMode.REGULAR_FILE, walk.getFileMode(0));
+ }
+ }
+
+ @Test
+ public void commitOnlyShouldHandleIgnored() throws Exception {
+ try (Git git = new Git(db)) {
+ writeTrashFile("subdir/foo", "Hello World");
+ writeTrashFile("subdir/bar", "Hello World");
+ writeTrashFile(".gitignore", "bar");
+ git.add().addFilepattern("subdir").call();
+ git.commit().setOnly("subdir").setMessage("first commit").call();
+ }
}
private static void addUnmergedEntry(String file, DirCacheBuilder builder) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java
index eb598e8..1e5d3bc 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DescribeCommandTest.java
@@ -49,17 +49,33 @@ import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+ at RunWith(Parameterized.class)
public class DescribeCommandTest extends RepositoryTestCase {
private Git git;
+ @Parameter
+ public boolean useAnnotatedTags;
+
+ @Parameters
+ public static Collection<Boolean[]> getUseAnnotatedTagsValues() {
+ return Arrays.asList(new Boolean[][] { { Boolean.TRUE },
+ { Boolean.FALSE } });
+ }
+
@Override
public void setUp() throws Exception {
super.setUp();
@@ -84,12 +100,15 @@ public class DescribeCommandTest extends RepositoryTestCase {
ObjectId c4 = modify("ddd");
assertNull(describe(c1));
+ assertNull(describe(c1, true));
assertEquals("t1", describe(c2));
assertEquals("t2", describe(c3));
+ assertEquals("t2-0-g44579eb", describe(c3, true));
assertNameStartsWith(c4, "3e563c5");
// the value verified with git-describe(1)
assertEquals("t2-1-g3e563c5", describe(c4));
+ assertEquals("t2-1-g3e563c5", describe(c4, true));
// test default target
assertEquals("t2-1-g3e563c5", git.describe().call());
@@ -122,6 +141,7 @@ public class DescribeCommandTest extends RepositoryTestCase {
assertNameStartsWith(c4, "119892b");
assertEquals("t-2-g119892b", describe(c4)); // 2 commits: c4 and c3
assertNull(describe(c3));
+ assertNull(describe(c3, true));
}
private void branch(String name, ObjectId base) throws GitAPIException {
@@ -229,7 +249,11 @@ public class DescribeCommandTest extends RepositoryTestCase {
}
private void tag(String tag) throws GitAPIException {
- git.tag().setName(tag).setMessage(tag).call();
+ TagCommand tagCommand = git.tag().setName(tag)
+ .setAnnotated(useAnnotatedTags);
+ if (useAnnotatedTags)
+ tagCommand.setMessage(tag);
+ tagCommand.call();
}
private static void touch(File f, String contents) throws Exception {
@@ -238,8 +262,13 @@ public class DescribeCommandTest extends RepositoryTestCase {
w.close();
}
+ private String describe(ObjectId c1, boolean longDesc)
+ throws GitAPIException, IOException {
+ return git.describe().setTarget(c1).setLong(longDesc).call();
+ }
+
private String describe(ObjectId c1) throws GitAPIException, IOException {
- return git.describe().setTarget(c1).call();
+ return describe(c1, false);
}
private static void assertNameStartsWith(ObjectId c4, String prefix) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DiffCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DiffCommandTest.java
index c5608b3..4ad01cf 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DiffCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/DiffCommandTest.java
@@ -247,12 +247,9 @@ public class DiffCommandTest extends RepositoryTestCase {
if (id == null)
throw new IllegalArgumentException(name);
final CanonicalTreeParser p = new CanonicalTreeParser();
- final ObjectReader or = db.newObjectReader();
- try {
+ try (ObjectReader or = db.newObjectReader()) {
p.reset(or, new RevWalk(db).parseTree(id));
return p;
- } finally {
- or.release();
}
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java
index 64bb8bf..2220a53 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/GitConstructionTest.java
@@ -140,7 +140,8 @@ public class GitConstructionTest extends RepositoryTestCase {
public void testClose() throws IOException, JGitInternalException,
GitAPIException {
File workTree = db.getWorkTree();
- Git git = Git.wrap(db);
+ db.close();
+ Git git = Git.open(workTree);
git.gc().setExpire(null).call();
git.checkout().setName(git.getRepository().resolve("HEAD^").getName())
.call();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
index 761aafa..cea8393 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
@@ -43,6 +43,8 @@
*/
package org.eclipse.jgit.api;
+import static org.eclipse.jgit.lib.Constants.MASTER;
+import static org.eclipse.jgit.lib.Constants.R_HEADS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
@@ -62,6 +64,7 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryState;
+import org.eclipse.jgit.lib.Sets;
import org.eclipse.jgit.merge.MergeStrategy;
import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -96,7 +99,7 @@ public class MergeCommandTest extends RepositoryTestCase {
Git git = new Git(db);
git.commit().setMessage("initial commit").call();
- MergeResult result = git.merge().include(db.getRef(Constants.HEAD)).call();
+ MergeResult result = git.merge().include(db.exactRef(Constants.HEAD)).call();
assertEquals(MergeResult.MergeStatus.ALREADY_UP_TO_DATE, result.getMergeStatus());
// no reflog entry written by merge
assertEquals("commit (initial): initial commit",
@@ -114,7 +117,7 @@ public class MergeCommandTest extends RepositoryTestCase {
createBranch(first, "refs/heads/branch1");
RevCommit second = git.commit().setMessage("second commit").call();
- MergeResult result = git.merge().include(db.getRef("refs/heads/branch1")).call();
+ MergeResult result = git.merge().include(db.exactRef("refs/heads/branch1")).call();
assertEquals(MergeResult.MergeStatus.ALREADY_UP_TO_DATE, result.getMergeStatus());
assertEquals(second, result.getNewHead());
// no reflog entry written by merge
@@ -134,7 +137,7 @@ public class MergeCommandTest extends RepositoryTestCase {
checkoutBranch("refs/heads/branch1");
- MergeResult result = git.merge().include(db.getRef(Constants.MASTER)).call();
+ MergeResult result = git.merge().include(db.exactRef(R_HEADS + MASTER)).call();
assertEquals(MergeResult.MergeStatus.FAST_FORWARD, result.getMergeStatus());
assertEquals(second, result.getNewHead());
@@ -154,7 +157,7 @@ public class MergeCommandTest extends RepositoryTestCase {
checkoutBranch("refs/heads/branch1");
- MergeResult result = git.merge().include(db.getRef(Constants.MASTER))
+ MergeResult result = git.merge().include(db.exactRef(R_HEADS + MASTER))
.setCommit(false).call();
assertEquals(MergeResult.MergeStatus.FAST_FORWARD,
@@ -185,7 +188,7 @@ public class MergeCommandTest extends RepositoryTestCase {
checkoutBranch("refs/heads/branch1");
assertFalse(new File(db.getWorkTree(), "file2").exists());
- MergeResult result = git.merge().include(db.getRef(Constants.MASTER)).call();
+ MergeResult result = git.merge().include(db.exactRef(R_HEADS + MASTER)).call();
assertTrue(new File(db.getWorkTree(), "file1").exists());
assertTrue(new File(db.getWorkTree(), "file2").exists());
@@ -220,7 +223,7 @@ public class MergeCommandTest extends RepositoryTestCase {
MergeCommand merge = git.merge();
merge.include(second.getId());
- merge.include(db.getRef(Constants.MASTER));
+ merge.include(db.exactRef(R_HEADS + MASTER));
try {
merge.call();
fail("Expected exception not thrown when merging multiple heads");
@@ -247,7 +250,7 @@ public class MergeCommandTest extends RepositoryTestCase {
git.commit().setMessage("third").call();
MergeResult result = git.merge().setStrategy(mergeStrategy)
- .include(db.getRef(Constants.MASTER)).call();
+ .include(db.exactRef(R_HEADS + MASTER)).call();
assertEquals(MergeStatus.MERGED, result.getMergeStatus());
assertEquals(
"merge refs/heads/master: Merge made by "
@@ -278,9 +281,9 @@ public class MergeCommandTest extends RepositoryTestCase {
MergeResult result = git.merge().setStrategy(mergeStrategy)
.setCommit(false)
- .include(db.getRef(Constants.MASTER)).call();
+ .include(db.exactRef(R_HEADS + MASTER)).call();
assertEquals(MergeStatus.MERGED_NOT_COMMITTED, result.getMergeStatus());
- assertEquals(db.getRef(Constants.HEAD).getTarget().getObjectId(),
+ assertEquals(db.exactRef(Constants.HEAD).getTarget().getObjectId(),
thirdCommit.getId());
}
@@ -377,7 +380,7 @@ public class MergeCommandTest extends RepositoryTestCase {
git.add().addFilepattern("a").call();
git.commit().setMessage("main").call();
- Ref sideBranch = db.getRef("side");
+ Ref sideBranch = db.exactRef("refs/heads/side");
git.merge().include(sideBranch)
.setStrategy(MergeStrategy.RESOLVE).call();
@@ -589,7 +592,7 @@ public class MergeCommandTest extends RepositoryTestCase {
.setCommit(false)
.setStrategy(MergeStrategy.RESOLVE).call();
assertEquals(MergeStatus.MERGED_NOT_COMMITTED, result.getMergeStatus());
- assertEquals(db.getRef(Constants.HEAD).getTarget().getObjectId(),
+ assertEquals(db.exactRef(Constants.HEAD).getTarget().getObjectId(),
thirdCommit.getId());
assertEquals("1(side)\na\n3(main)\n", read(new File(db.getWorkTree(),
@@ -1309,8 +1312,10 @@ public class MergeCommandTest extends RepositoryTestCase {
assertFalse(new File(db.getWorkTree(), "file2").exists());
assertFalse(new File(db.getWorkTree(), "file3").exists());
- MergeResult result = git.merge().include(db.getRef("branch1"))
- .setSquash(true).call();
+ MergeResult result = git.merge()
+ .include(db.exactRef("refs/heads/branch1"))
+ .setSquash(true)
+ .call();
assertTrue(new File(db.getWorkTree(), "file1").exists());
assertTrue(new File(db.getWorkTree(), "file2").exists());
@@ -1374,8 +1379,10 @@ public class MergeCommandTest extends RepositoryTestCase {
assertTrue(new File(db.getWorkTree(), "file2").exists());
assertFalse(new File(db.getWorkTree(), "file3").exists());
- MergeResult result = git.merge().include(db.getRef("branch1"))
- .setSquash(true).call();
+ MergeResult result = git.merge()
+ .include(db.exactRef("refs/heads/branch1"))
+ .setSquash(true)
+ .call();
assertTrue(new File(db.getWorkTree(), "file1").exists());
assertTrue(new File(db.getWorkTree(), "file2").exists());
@@ -1429,8 +1436,10 @@ public class MergeCommandTest extends RepositoryTestCase {
assertTrue(new File(db.getWorkTree(), "file1").exists());
assertTrue(new File(db.getWorkTree(), "file2").exists());
- MergeResult result = git.merge().include(db.getRef("branch1"))
- .setSquash(true).call();
+ MergeResult result = git.merge()
+ .include(db.exactRef("refs/heads/branch1"))
+ .setSquash(true)
+ .call();
assertTrue(new File(db.getWorkTree(), "file1").exists());
assertTrue(new File(db.getWorkTree(), "file2").exists());
@@ -1467,7 +1476,7 @@ public class MergeCommandTest extends RepositoryTestCase {
MergeCommand merge = git.merge();
merge.setFastForward(FastForwardMode.FF_ONLY);
- merge.include(db.getRef(Constants.MASTER));
+ merge.include(db.exactRef(R_HEADS + MASTER));
MergeResult result = merge.call();
assertEquals(MergeStatus.FAST_FORWARD, result.getMergeStatus());
@@ -1484,7 +1493,7 @@ public class MergeCommandTest extends RepositoryTestCase {
MergeCommand merge = git.merge();
merge.setFastForward(FastForwardMode.NO_FF);
- merge.include(db.getRef(Constants.MASTER));
+ merge.include(db.exactRef(R_HEADS + MASTER));
MergeResult result = merge.call();
assertEquals(MergeStatus.MERGED, result.getMergeStatus());
@@ -1504,7 +1513,7 @@ public class MergeCommandTest extends RepositoryTestCase {
// when
MergeCommand merge = git.merge();
merge.setFastForward(FastForwardMode.NO_FF);
- merge.include(db.getRef(Constants.MASTER));
+ merge.include(db.exactRef(R_HEADS + MASTER));
merge.setCommit(false);
MergeResult result = merge.call();
@@ -1530,7 +1539,7 @@ public class MergeCommandTest extends RepositoryTestCase {
git.commit().setMessage("second commit on branch1").call();
MergeCommand merge = git.merge();
merge.setFastForward(FastForwardMode.FF_ONLY);
- merge.include(db.getRef(Constants.MASTER));
+ merge.include(db.exactRef(R_HEADS + MASTER));
MergeResult result = merge.call();
assertEquals(MergeStatus.ABORTED, result.getMergeStatus());
@@ -1587,7 +1596,7 @@ public class MergeCommandTest extends RepositoryTestCase {
git.add().addFilepattern("c").call();
git.commit().setMessage("main").call();
- Ref sideBranch = db.getRef("side");
+ Ref sideBranch = db.exactRef("refs/heads/side");
git.merge().include(sideBranch).setStrategy(MergeStrategy.RESOLVE)
.setMessage("user message").call();
@@ -1620,7 +1629,7 @@ public class MergeCommandTest extends RepositoryTestCase {
git.add().addFilepattern("a").call();
git.commit().setMessage("main").call();
- Ref sideBranch = db.getRef("side");
+ Ref sideBranch = db.exactRef("refs/heads/side");
git.merge().include(sideBranch).setStrategy(MergeStrategy.RESOLVE)
.setMessage("user message").call();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NameRevCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NameRevCommandTest.java
index 4915954..bd62200 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NameRevCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NameRevCommandTest.java
@@ -95,9 +95,9 @@ public class NameRevCommandTest extends RepositoryTestCase {
tr.update("refs/heads/master", c);
tr.update("refs/tags/tag", c);
assertOneResult("master",
- git.nameRev().addRef(db.getRef("refs/heads/master")), c);
+ git.nameRev().addRef(db.exactRef("refs/heads/master")), c);
assertOneResult("tag",
- git.nameRev().addRef(db.getRef("refs/tags/tag")), c);
+ git.nameRev().addRef(db.exactRef("refs/tags/tag")), c);
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PathCheckoutCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PathCheckoutCommandTest.java
index eb092ad..3343af0 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PathCheckoutCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PathCheckoutCommandTest.java
@@ -43,10 +43,12 @@
package org.eclipse.jgit.api;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
+import java.nio.file.Path;
import org.eclipse.jgit.api.CheckoutCommand.Stage;
import org.eclipse.jgit.api.errors.JGitInternalException;
@@ -59,6 +61,9 @@ import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.RepositoryState;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.FileUtils;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
@@ -73,6 +78,8 @@ public class PathCheckoutCommandTest extends RepositoryTestCase {
private static final String FILE3 = "Test3.txt";
+ private static final String LINK = "link";
+
Git git;
RevCommit initialCommit;
@@ -99,6 +106,64 @@ public class PathCheckoutCommandTest extends RepositoryTestCase {
}
@Test
+ public void testUpdateSymLink() throws Exception {
+ Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
+
+ Path path = writeLink(LINK, FILE1);
+ git.add().addFilepattern(LINK).call();
+ git.commit().setMessage("Added link").call();
+ assertEquals("3", read(path.toFile()));
+
+ writeLink(LINK, FILE2);
+ assertEquals("c", read(path.toFile()));
+
+ CheckoutCommand co = git.checkout();
+ co.addPath(LINK).call();
+
+ assertEquals("3", read(path.toFile()));
+ }
+
+ @Test
+ public void testUpdateBrokenSymLinkToDirectory() throws Exception {
+ Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
+
+ Path path = writeLink(LINK, "f");
+ git.add().addFilepattern(LINK).call();
+ git.commit().setMessage("Added link").call();
+ assertEquals("f", FileUtils.readSymLink(path.toFile()));
+ assertTrue(path.toFile().exists());
+
+ writeLink(LINK, "link_to_nowhere");
+ assertFalse(path.toFile().exists());
+ assertEquals("link_to_nowhere", FileUtils.readSymLink(path.toFile()));
+
+ CheckoutCommand co = git.checkout();
+ co.addPath(LINK).call();
+
+ assertEquals("f", FileUtils.readSymLink(path.toFile()));
+ }
+
+ @Test
+ public void testUpdateBrokenSymLink() throws Exception {
+ Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
+
+ Path path = writeLink(LINK, FILE1);
+ git.add().addFilepattern(LINK).call();
+ git.commit().setMessage("Added link").call();
+ assertEquals("3", read(path.toFile()));
+ assertEquals(FILE1, FileUtils.readSymLink(path.toFile()));
+
+ writeLink(LINK, "link_to_nowhere");
+ assertFalse(path.toFile().exists());
+ assertEquals("link_to_nowhere", FileUtils.readSymLink(path.toFile()));
+
+ CheckoutCommand co = git.checkout();
+ co.addPath(LINK).call();
+
+ assertEquals("3", read(path.toFile()));
+ }
+
+ @Test
public void testUpdateWorkingDirectory() throws Exception {
CheckoutCommand co = git.checkout();
File written = writeTrashFile(FILE1, "");
@@ -193,8 +258,8 @@ public class PathCheckoutCommandTest extends RepositoryTestCase {
public static void validateIndex(Git git) throws NoWorkTreeException,
IOException {
DirCache dc = git.getRepository().lockDirCache();
- ObjectReader r = git.getRepository().getObjectDatabase().newReader();
- try {
+ try (ObjectReader r = git.getRepository().getObjectDatabase()
+ .newReader()) {
for (int i = 0; i < dc.getEntryCount(); ++i) {
DirCacheEntry entry = dc.getEntry(i);
if (entry.getLength() > 0)
@@ -203,7 +268,6 @@ public class PathCheckoutCommandTest extends RepositoryTestCase {
}
} finally {
dc.unlock();
- r.release();
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
index d4805d0..57888e7 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandTest.java
@@ -474,34 +474,35 @@ public class PullCommandTest extends RepositoryTestCase {
}
assertFileContentsEqual(sourceFile, "content");
- RevWalk rw = new RevWalk(dbTarget);
- rw.sort(RevSort.TOPO);
- rw.markStart(rw.parseCommit(dbTarget.resolve("refs/heads/master")));
+ try (RevWalk rw = new RevWalk(dbTarget)) {
+ rw.sort(RevSort.TOPO);
+ rw.markStart(rw.parseCommit(dbTarget.resolve("refs/heads/master")));
- RevCommit next;
- if (expectedPullMode == TestPullMode.MERGE) {
- next = rw.next();
- assertEquals(2, next.getParentCount());
- assertEquals(merge, next.getParent(0));
- assertEquals(sourceCommit, next.getParent(1));
- // since both parents are known do no further checks here
- } else {
- if (expectedPullMode == TestPullMode.REBASE_PREASERVE) {
+ RevCommit next;
+ if (expectedPullMode == TestPullMode.MERGE) {
next = rw.next();
assertEquals(2, next.getParentCount());
+ assertEquals(merge, next.getParent(0));
+ assertEquals(sourceCommit, next.getParent(1));
+ // since both parents are known do no further checks here
+ } else {
+ if (expectedPullMode == TestPullMode.REBASE_PREASERVE) {
+ next = rw.next();
+ assertEquals(2, next.getParentCount());
+ }
+ next = rw.next();
+ assertEquals(t2.getShortMessage(), next.getShortMessage());
+ next = rw.next();
+ assertEquals(t1.getShortMessage(), next.getShortMessage());
+ next = rw.next();
+ assertEquals(sourceCommit, next);
+ next = rw.next();
+ assertEquals("Initial commit for source",
+ next.getShortMessage());
+ next = rw.next();
+ assertNull(next);
}
- next = rw.next();
- assertEquals(t2.getShortMessage(), next.getShortMessage());
- next = rw.next();
- assertEquals(t1.getShortMessage(), next.getShortMessage());
- next = rw.next();
- assertEquals(sourceCommit, next);
- next = rw.next();
- assertEquals("Initial commit for source", next.getShortMessage());
- next = rw.next();
- assertNull(next);
}
- rw.release();
}
@Override
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandWithRebaseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandWithRebaseTest.java
index 25534fd..9ad845b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandWithRebaseTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PullCommandWithRebaseTest.java
@@ -180,7 +180,7 @@ public class PullCommandWithRebaseTest extends RepositoryTestCase {
+ remoteUri
+ "\nSource change\n=======\nTarget change\n>>>>>>> 42453fd Target change in local\n";
assertFileContentsEqual(targetFile, result);
- assertEquals(RepositoryState.REBASING_INTERACTIVE, target
+ assertEquals(RepositoryState.REBASING_MERGE, target
.getRepository().getRepositoryState());
}
@@ -225,7 +225,7 @@ public class PullCommandWithRebaseTest extends RepositoryTestCase {
String result = "<<<<<<< Upstream, based on branch 'master' of local repository\n"
+ "Master change\n=======\nSlave change\n>>>>>>> 4049c9e Source change in based on master\n";
assertFileContentsEqual(targetFile, result);
- assertEquals(RepositoryState.REBASING_INTERACTIVE, target
+ assertEquals(RepositoryState.REBASING_MERGE, target
.getRepository().getRepositoryState());
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java
index 19f074e..1fcfef9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java
@@ -47,6 +47,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Properties;
@@ -55,7 +56,10 @@ import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.TransportException;
import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.hooks.PrePushHook;
+import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
@@ -66,6 +70,7 @@ import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.transport.TrackingRefUpdate;
import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.util.FS;
import org.junit.Test;
public class PushCommandTest extends RepositoryTestCase {
@@ -108,6 +113,46 @@ public class PushCommandTest extends RepositoryTestCase {
}
@Test
+ public void testPrePushHook() throws JGitInternalException, IOException,
+ GitAPIException, URISyntaxException {
+
+ // create other repository
+ Repository db2 = createWorkRepository();
+
+ // setup the first repository
+ final StoredConfig config = db.getConfig();
+ RemoteConfig remoteConfig = new RemoteConfig(config, "test");
+ URIish uri = new URIish(db2.getDirectory().toURI().toURL());
+ remoteConfig.addURI(uri);
+ remoteConfig.update(config);
+ config.save();
+
+ File hookOutput = new File(getTemporaryDirectory(), "hookOutput");
+ writeHookFile(PrePushHook.NAME, "#!/bin/sh\necho 1:$1, 2:$2, 3:$3 >\""
+ + hookOutput.toPath() + "\"\ncat - >>\"" + hookOutput.toPath()
+ + "\"\nexit 0");
+
+ Git git1 = new Git(db);
+ // create some refs via commits and tag
+ RevCommit commit = git1.commit().setMessage("initial commit").call();
+
+ RefSpec spec = new RefSpec("refs/heads/master:refs/heads/x");
+ git1.push().setRemote("test").setRefSpecs(spec).call();
+ assertEquals("1:test, 2:" + uri + ", 3:\n" + "refs/heads/master "
+ + commit.getName() + " refs/heads/x "
+ + ObjectId.zeroId().name(), read(hookOutput));
+ }
+
+ private File writeHookFile(final String name, final String data)
+ throws IOException {
+ File path = new File(db.getWorkTree() + "/.git/hooks/", name);
+ JGitTestUtil.write(path, data);
+ FS.DETECTED.setExecute(path, true);
+ return path;
+ }
+
+
+ @Test
public void testTrackingUpdate() throws Exception {
Repository db2 = createBareRepository();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
index 8e64776..8b0ed5f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
@@ -114,13 +114,14 @@ public class RebaseCommandTest extends RepositoryTestCase {
private void checkoutCommit(RevCommit commit) throws IllegalStateException,
IOException {
- RevWalk walk = new RevWalk(db);
- RevCommit head = walk.parseCommit(db.resolve(Constants.HEAD));
- DirCacheCheckout dco = new DirCacheCheckout(db, head.getTree(), db
- .lockDirCache(), commit.getTree());
- dco.setFailOnConflict(true);
- dco.checkout();
- walk.release();
+ RevCommit head;
+ try (RevWalk walk = new RevWalk(db)) {
+ head = walk.parseCommit(db.resolve(Constants.HEAD));
+ DirCacheCheckout dco = new DirCacheCheckout(db, head.getTree(),
+ db.lockDirCache(), commit.getTree());
+ dco.setFailOnConflict(true);
+ dco.checkout();
+ }
// update the HEAD
RefUpdate refUpdate = db.updateRef(Constants.HEAD, true);
refUpdate.setNewObjectId(commit);
@@ -472,11 +473,12 @@ public class RebaseCommandTest extends RepositoryTestCase {
}
private String readFile(String path, RevCommit commit) throws IOException {
- TreeWalk walk = TreeWalk.forPath(db, path, commit.getTree());
- ObjectLoader loader = db.open(walk.getObjectId(0), Constants.OBJ_BLOB);
- String result = RawParseUtils.decode(loader.getCachedBytes());
- walk.release();
- return result;
+ try (TreeWalk walk = TreeWalk.forPath(db, path, commit.getTree())) {
+ ObjectLoader loader = db.open(walk.getObjectId(0),
+ Constants.OBJ_BLOB);
+ String result = RawParseUtils.decode(loader.getCachedBytes());
+ return result;
+ }
}
@Test
@@ -824,7 +826,7 @@ public class RebaseCommandTest extends RepositoryTestCase {
"<<<<<<< Upstream, based on master\n1master\n=======\n1topic",
">>>>>>> e0d1dea change file1 in topic\n2\n3\ntopic4");
- assertEquals(RepositoryState.REBASING_INTERACTIVE, db
+ assertEquals(RepositoryState.REBASING_MERGE, db
.getRepositoryState());
assertTrue(new File(db.getDirectory(), "rebase-merge").exists());
// the first one should be included, so we should have left two picks in
@@ -887,7 +889,7 @@ public class RebaseCommandTest extends RepositoryTestCase {
"<<<<<<< Upstream, based on master\n1master\n=======\n1topic",
">>>>>>> e0d1dea change file1 in topic\n2\n3\ntopic4");
- assertEquals(RepositoryState.REBASING_INTERACTIVE,
+ assertEquals(RepositoryState.REBASING_MERGE,
db.getRepositoryState());
assertTrue(new File(db.getDirectory(), "rebase-merge").exists());
// the first one should be included, so we should have left two picks in
@@ -1009,7 +1011,7 @@ public class RebaseCommandTest extends RepositoryTestCase {
res = git.rebase().setOperation(Operation.CONTINUE).call();
assertNotNull(res);
assertEquals(Status.NOTHING_TO_COMMIT, res.getStatus());
- assertEquals(RepositoryState.REBASING_INTERACTIVE,
+ assertEquals(RepositoryState.REBASING_MERGE,
db.getRepositoryState());
git.rebase().setOperation(Operation.SKIP).call();
@@ -1300,7 +1302,7 @@ public class RebaseCommandTest extends RepositoryTestCase {
// user can decide what to do. if he accidentally committed, reset soft,
// and continue, if he really has nothing to commit, skip.
assertEquals(Status.NOTHING_TO_COMMIT, res.getStatus());
- assertEquals(RepositoryState.REBASING_INTERACTIVE,
+ assertEquals(RepositoryState.REBASING_MERGE,
db.getRepositoryState());
git.rebase().setOperation(Operation.SKIP).call();
@@ -1401,7 +1403,7 @@ public class RebaseCommandTest extends RepositoryTestCase {
assertEquals(Status.STOPPED, res.getStatus());
assertEquals(conflicting, res.getCurrentCommit());
- assertEquals(RepositoryState.REBASING_INTERACTIVE, db
+ assertEquals(RepositoryState.REBASING_MERGE, db
.getRepositoryState());
assertTrue(new File(db.getDirectory(), "rebase-merge").exists());
// the first one should be included, so we should have left two picks in
@@ -2073,14 +2075,11 @@ public class RebaseCommandTest extends RepositoryTestCase {
private List<DiffEntry> diffWorkingAgainstHead(final RevCommit commit,
RevWalk revWalk)
throws IOException {
- TreeWalk walk = createTreeWalk();
RevCommit parentCommit = revWalk.parseCommit(commit.getParent(0));
- try {
+ try (TreeWalk walk = createTreeWalk()) {
walk.addTree(parentCommit.getTree());
walk.addTree(commit.getTree());
return DiffEntry.scan(walk);
- } finally {
- walk.release();
}
}
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/TagTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteAddCommandTest.java
similarity index 61%
copy from org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/TagTest.java
copy to org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteAddCommandTest.java
index ab09db5..ed09446 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/TagTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteAddCommandTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012, Tomasz Zarna <tomasz.zarna at tasktop.com> and others.
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r at zend.com>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -40,34 +40,42 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.pgm;
+package org.eclipse.jgit.api;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.lib.CLIRepositoryTestCase;
-import org.junit.Before;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.URIish;
import org.junit.Test;
-public class TagTest extends CLIRepositoryTestCase {
- private Git git;
-
- @Override
- @Before
- public void setUp() throws Exception {
- super.setUp();
- git = new Git(db);
- git.commit().setMessage("initial commit").call();
- }
+public class RemoteAddCommandTest extends AbstractRemoteCommandTest {
@Test
- public void testTagTwice() throws Exception {
- git.tag().setName("test").call();
- writeTrashFile("file", "content");
- git.add().addFilepattern("file").call();
- git.commit().setMessage("commit").call();
+ public void testAdd() throws Exception {
+ // create another repository
+ Repository remoteRepository = createWorkRepository();
+ URIish uri = new URIish(
+ remoteRepository.getDirectory().toURI().toURL());
+
+ // execute the command to add a new remote
+ RemoteAddCommand cmd = Git.wrap(db).remoteAdd();
+ cmd.setName(REMOTE_NAME);
+ cmd.setUri(uri);
+ RemoteConfig remote = cmd.call();
- assertEquals("fatal: tag 'test' already exists",
- execute("git tag test")[0]);
+ // assert that the added remote represents the remote repository
+ assertEquals(REMOTE_NAME, remote.getName());
+ assertArrayEquals(new URIish[] { uri }, remote.getURIs().toArray());
+ assertEquals(1, remote.getFetchRefSpecs().size());
+ assertEquals(
+ String.format("+refs/heads/*:refs/remotes/%s/*", REMOTE_NAME),
+ remote.getFetchRefSpecs().get(0).toString());
+
+ // assert that the added remote is available in the git configuration
+ assertRemoteConfigEquals(remote,
+ new RemoteConfig(db.getConfig(), REMOTE_NAME));
}
+
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/nls/RootLocaleTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteDeleteCommandTest.java
similarity index 71%
copy from org.eclipse.jgit.test/tst/org/eclipse/jgit/nls/RootLocaleTest.java
copy to org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteDeleteCommandTest.java
index cae006c..7055daf 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/nls/RootLocaleTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteDeleteCommandTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, Sasa Zivkov <sasa.zivkov at sap.com>
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r at zend.com>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -40,39 +40,29 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+package org.eclipse.jgit.api;
-package org.eclipse.jgit.nls;
+import static org.junit.Assert.assertTrue;
-import org.eclipse.jgit.awtui.UIText;
-import org.eclipse.jgit.console.ConsoleText;
-import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.pgm.internal.CLIText;
-import org.junit.Before;
+import org.eclipse.jgit.transport.RemoteConfig;
import org.junit.Test;
-public class RootLocaleTest {
- @Before
- public void setUp() {
- NLS.setLocale(NLS.ROOT_LOCALE);
- }
+public class RemoteDeleteCommandTest extends AbstractRemoteCommandTest {
@Test
- public void testJGitText() {
- NLS.getBundleFor(JGitText.class);
- }
+ public void testDelete() throws Exception {
+ // setup an initial remote
+ RemoteConfig remoteConfig = setupRemote();
- @Test
- public void testConsoleText() {
- NLS.getBundleFor(ConsoleText.class);
- }
+ // execute the command to remove the remote
+ RemoteRemoveCommand cmd = Git.wrap(db).remoteRemove();
+ cmd.setName(REMOTE_NAME);
+ RemoteConfig remote = cmd.call();
- @Test
- public void testCLIText() {
- NLS.getBundleFor(CLIText.class);
+ // assert that the removed remote is the initial remote
+ assertRemoteConfigEquals(remoteConfig, remote);
+ // assert that there are no remotes left
+ assertTrue(RemoteConfig.getAllRemoteConfigs(db.getConfig()).isEmpty());
}
- @Test
- public void testUIText() {
- NLS.getBundleFor(UIText.class);
- }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/nls/RootLocaleTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteListCommandTest.java
similarity index 73%
copy from org.eclipse.jgit.test/tst/org/eclipse/jgit/nls/RootLocaleTest.java
copy to org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteListCommandTest.java
index cae006c..cf522ff 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/nls/RootLocaleTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteListCommandTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, Sasa Zivkov <sasa.zivkov at sap.com>
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r at zend.com>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -40,39 +40,29 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+package org.eclipse.jgit.api;
-package org.eclipse.jgit.nls;
+import static org.junit.Assert.assertEquals;
-import org.eclipse.jgit.awtui.UIText;
-import org.eclipse.jgit.console.ConsoleText;
-import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.pgm.internal.CLIText;
-import org.junit.Before;
+import java.util.List;
+
+import org.eclipse.jgit.transport.RemoteConfig;
import org.junit.Test;
-public class RootLocaleTest {
- @Before
- public void setUp() {
- NLS.setLocale(NLS.ROOT_LOCALE);
- }
+public class RemoteListCommandTest extends AbstractRemoteCommandTest {
@Test
- public void testJGitText() {
- NLS.getBundleFor(JGitText.class);
- }
+ public void testList() throws Exception {
+ // setup an initial remote
+ RemoteConfig remoteConfig = setupRemote();
- @Test
- public void testConsoleText() {
- NLS.getBundleFor(ConsoleText.class);
- }
+ // execute the command to list the remotes
+ List<RemoteConfig> remotes = Git.wrap(db).remoteList().call();
- @Test
- public void testCLIText() {
- NLS.getBundleFor(CLIText.class);
+ // assert that there is only one remote
+ assertEquals(1, remotes.size());
+ // assert that the available remote is the initial remote
+ assertRemoteConfigEquals(remoteConfig, remotes.get(0));
}
- @Test
- public void testUIText() {
- NLS.getBundleFor(UIText.class);
- }
}
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/AddTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteSetUrlCommandTest.java
similarity index 52%
copy from org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/AddTest.java
copy to org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteSetUrlCommandTest.java
index 4253080..6969c3d 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/AddTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteSetUrlCommandTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Google Inc.
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r at zend.com>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -40,76 +40,61 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.pgm;
+package org.eclipse.jgit.api;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import java.lang.Exception;
-import java.lang.String;
-
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.dircache.DirCache;
-import org.eclipse.jgit.lib.CLIRepositoryTestCase;
-import org.junit.Before;
-import org.junit.Ignore;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.URIish;
import org.junit.Test;
-public class AddTest extends CLIRepositoryTestCase {
- private Git git;
-
- @Override
- @Before
- public void setUp() throws Exception {
- super.setUp();
- git = new Git(db);
- }
+public class RemoteSetUrlCommandTest extends AbstractRemoteCommandTest {
- @Ignore("args4j exit()s on error instead of throwing, JVM goes down")
@Test
- public void testAddNothing() throws Exception {
- assertEquals("fatal: Argument \"filepattern\" is required", //
- execute("git add")[0]);
- }
+ public void testSetUrl() throws Exception {
+ // setup an initial remote
+ setupRemote();
- @Ignore("args4j exit()s for --help, too")
- @Test
- public void testAddUsage() throws Exception {
- execute("git add --help");
- }
+ // execute the command to change the fetch url
+ RemoteSetUrlCommand cmd = Git.wrap(db).remoteSetUrl();
+ cmd.setName(REMOTE_NAME);
+ URIish newUri = new URIish("git://test.com/test");
+ cmd.setUri(newUri);
+ RemoteConfig remote = cmd.call();
- @Test
- public void testAddAFile() throws Exception {
- writeTrashFile("greeting", "Hello, world!");
- assertArrayEquals(new String[] { "" }, //
- execute("git add greeting"));
+ // assert that the changed remote has the new fetch url
+ assertEquals(REMOTE_NAME, remote.getName());
+ assertArrayEquals(new URIish[] { newUri }, remote.getURIs().toArray());
- DirCache cache = db.readDirCache();
- assertNotNull(cache.getEntry("greeting"));
- assertEquals(1, cache.getEntryCount());
+ // assert that the changed remote is available in the git configuration
+ assertRemoteConfigEquals(remote,
+ new RemoteConfig(db.getConfig(), REMOTE_NAME));
}
@Test
- public void testAddFileTwice() throws Exception {
- writeTrashFile("greeting", "Hello, world!");
- assertArrayEquals(new String[] { "" }, //
- execute("git add greeting greeting"));
+ public void testSetPushUrl() throws Exception {
+ // setup an initial remote
+ RemoteConfig remoteConfig = setupRemote();
- DirCache cache = db.readDirCache();
- assertNotNull(cache.getEntry("greeting"));
- assertEquals(1, cache.getEntryCount());
- }
+ // execute the command to change the push url
+ RemoteSetUrlCommand cmd = Git.wrap(db).remoteSetUrl();
+ cmd.setName(REMOTE_NAME);
+ URIish newUri = new URIish("git://test.com/test");
+ cmd.setUri(newUri);
+ cmd.setPush(true);
+ RemoteConfig remote = cmd.call();
- @Test
- public void testAddAlreadyAdded() throws Exception {
- writeTrashFile("greeting", "Hello, world!");
- git.add().addFilepattern("greeting").call();
- assertArrayEquals(new String[] { "" }, //
- execute("git add greeting"));
+ // assert that the changed remote has the old fetch url and the new push
+ // url
+ assertEquals(REMOTE_NAME, remote.getName());
+ assertEquals(remoteConfig.getURIs(), remote.getURIs());
+ assertArrayEquals(new URIish[] { newUri },
+ remote.getPushURIs().toArray());
- DirCache cache = db.readDirCache();
- assertNotNull(cache.getEntry("greeting"));
- assertEquals(1, cache.getEntryCount());
+ // assert that the changed remote is available in the git configuration
+ assertRemoteConfigEquals(remote,
+ new RemoteConfig(db.getConfig(), REMOTE_NAME));
}
+
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java
index c48b412..66f25e8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java
@@ -65,6 +65,7 @@ import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.TreeWalk;
@@ -139,8 +140,8 @@ public class ResetCommandTest extends RepositoryTestCase {
AmbiguousObjectException, IOException, GitAPIException {
setupRepository();
ObjectId prevHead = db.resolve(Constants.HEAD);
- git.reset().setMode(ResetType.HARD).setRef(initialCommit.getName())
- .call();
+ assertSameAsHead(git.reset().setMode(ResetType.HARD)
+ .setRef(initialCommit.getName()).call());
// check if HEAD points to initial commit now
ObjectId head = db.resolve(Constants.HEAD);
assertEquals(initialCommit, head);
@@ -176,8 +177,8 @@ public class ResetCommandTest extends RepositoryTestCase {
AmbiguousObjectException, IOException, GitAPIException {
setupRepository();
ObjectId prevHead = db.resolve(Constants.HEAD);
- git.reset().setMode(ResetType.SOFT).setRef(initialCommit.getName())
- .call();
+ assertSameAsHead(git.reset().setMode(ResetType.SOFT)
+ .setRef(initialCommit.getName()).call());
// check if HEAD points to initial commit now
ObjectId head = db.resolve(Constants.HEAD);
assertEquals(initialCommit, head);
@@ -197,8 +198,8 @@ public class ResetCommandTest extends RepositoryTestCase {
AmbiguousObjectException, IOException, GitAPIException {
setupRepository();
ObjectId prevHead = db.resolve(Constants.HEAD);
- git.reset().setMode(ResetType.MIXED).setRef(initialCommit.getName())
- .call();
+ assertSameAsHead(git.reset().setMode(ResetType.MIXED)
+ .setRef(initialCommit.getName()).call());
// check if HEAD points to initial commit now
ObjectId head = db.resolve(Constants.HEAD);
assertEquals(initialCommit, head);
@@ -241,7 +242,8 @@ public class ResetCommandTest extends RepositoryTestCase {
assertTrue(bEntry.getLength() > 0);
assertTrue(bEntry.getLastModified() > 0);
- git.reset().setMode(ResetType.MIXED).setRef(commit2.getName()).call();
+ assertSameAsHead(git.reset().setMode(ResetType.MIXED)
+ .setRef(commit2.getName()).call());
cache = db.readDirCache();
@@ -280,7 +282,7 @@ public class ResetCommandTest extends RepositoryTestCase {
+ "[a.txt, mode:100644, stage:3]",
indexState(0));
- git.reset().setMode(ResetType.MIXED).call();
+ assertSameAsHead(git.reset().setMode(ResetType.MIXED).call());
assertEquals("[a.txt, mode:100644]" + "[b.txt, mode:100644]",
indexState(0));
@@ -298,8 +300,8 @@ public class ResetCommandTest extends RepositoryTestCase {
// 'a.txt' has already been modified in setupRepository
// 'notAddedToIndex.txt' has been added to repository
- git.reset().addPath(indexFile.getName())
- .addPath(untrackedFile.getName()).call();
+ assertSameAsHead(git.reset().addPath(indexFile.getName())
+ .addPath(untrackedFile.getName()).call());
DirCacheEntry postReset = DirCache.read(db.getIndexFile(), db.getFS())
.getEntry(indexFile.getName());
@@ -329,7 +331,7 @@ public class ResetCommandTest extends RepositoryTestCase {
git.add().addFilepattern(untrackedFile.getName()).call();
// 'dir/b.txt' has already been modified in setupRepository
- git.reset().addPath("dir").call();
+ assertSameAsHead(git.reset().addPath("dir").call());
DirCacheEntry postReset = DirCache.read(db.getIndexFile(), db.getFS())
.getEntry("dir/b.txt");
@@ -358,9 +360,9 @@ public class ResetCommandTest extends RepositoryTestCase {
// 'a.txt' has already been modified in setupRepository
// 'notAddedToIndex.txt' has been added to repository
// reset to the inital commit
- git.reset().setRef(initialCommit.getName())
- .addPath(indexFile.getName())
- .addPath(untrackedFile.getName()).call();
+ assertSameAsHead(git.reset().setRef(initialCommit.getName())
+ .addPath(indexFile.getName()).addPath(untrackedFile.getName())
+ .call());
// check that HEAD hasn't moved
ObjectId head = db.resolve(Constants.HEAD);
@@ -397,7 +399,7 @@ public class ResetCommandTest extends RepositoryTestCase {
+ "[b.txt, mode:100644]",
indexState(0));
- git.reset().addPath(file).call();
+ assertSameAsHead(git.reset().addPath(file).call());
assertEquals("[a.txt, mode:100644]" + "[b.txt, mode:100644]",
indexState(0));
@@ -409,7 +411,7 @@ public class ResetCommandTest extends RepositoryTestCase {
writeTrashFile("a.txt", "content");
git.add().addFilepattern("a.txt").call();
// Should assume an empty tree, like in C Git 1.8.2
- git.reset().addPath("a.txt").call();
+ assertSameAsHead(git.reset().addPath("a.txt").call());
DirCache cache = db.readDirCache();
DirCacheEntry aEntry = cache.getEntry("a.txt");
@@ -421,7 +423,8 @@ public class ResetCommandTest extends RepositoryTestCase {
git = new Git(db);
writeTrashFile("a.txt", "content");
git.add().addFilepattern("a.txt").call();
- git.reset().setRef("doesnotexist").addPath("a.txt").call();
+ assertSameAsHead(
+ git.reset().setRef("doesnotexist").addPath("a.txt").call());
}
@Test
@@ -431,7 +434,7 @@ public class ResetCommandTest extends RepositoryTestCase {
git.add().addFilepattern("a.txt").call();
writeTrashFile("a.txt", "modified");
// should use default mode MIXED
- git.reset().call();
+ assertSameAsHead(git.reset().call());
DirCache cache = db.readDirCache();
DirCacheEntry aEntry = cache.getEntry("a.txt");
@@ -452,7 +455,7 @@ public class ResetCommandTest extends RepositoryTestCase {
git.add().addFilepattern(untrackedFile.getName()).call();
- git.reset().setRef(tagName).setMode(HARD).call();
+ assertSameAsHead(git.reset().setRef(tagName).setMode(HARD).call());
ObjectId head = db.resolve(Constants.HEAD);
assertEquals(secondCommit, head);
@@ -477,14 +480,17 @@ public class ResetCommandTest extends RepositoryTestCase {
checkoutBranch("refs/heads/master");
- MergeResult result = g.merge().include(db.getRef("branch1"))
- .setSquash(true).call();
+ MergeResult result = g.merge()
+ .include(db.exactRef("refs/heads/branch1"))
+ .setSquash(true)
+ .call();
assertEquals(MergeResult.MergeStatus.FAST_FORWARD_SQUASHED,
result.getMergeStatus());
assertNotNull(db.readSquashCommitMsg());
- g.reset().setMode(ResetType.HARD).setRef(first.getName()).call();
+ assertSameAsHead(g.reset().setMode(ResetType.HARD)
+ .setRef(first.getName()).call());
assertNull(db.readSquashCommitMsg());
}
@@ -495,7 +501,7 @@ public class ResetCommandTest extends RepositoryTestCase {
File fileA = writeTrashFile("a.txt", "content");
git.add().addFilepattern("a.txt").call();
// Should assume an empty tree, like in C Git 1.8.2
- git.reset().setMode(ResetType.HARD).call();
+ assertSameAsHead(git.reset().setMode(ResetType.HARD).call());
DirCache cache = db.readDirCache();
DirCacheEntry aEntry = cache.getEntry("a.txt");
@@ -537,16 +543,10 @@ public class ResetCommandTest extends RepositoryTestCase {
*/
private boolean inHead(String path) throws IOException {
ObjectId headId = db.resolve(Constants.HEAD);
- RevWalk rw = new RevWalk(db);
- TreeWalk tw = null;
- try {
- tw = TreeWalk.forPath(db, path, rw.parseTree(headId));
+ try (RevWalk rw = new RevWalk(db);
+ TreeWalk tw = TreeWalk.forPath(db, path,
+ rw.parseTree(headId))) {
return tw != null;
- } finally {
- rw.release();
- rw.dispose();
- if (tw != null)
- tw.release();
}
}
@@ -562,4 +562,14 @@ public class ResetCommandTest extends RepositoryTestCase {
return dc.getEntry(path) != null;
}
+ /**
+ * Asserts that a certain ref is similar to repos HEAD.
+ * @param ref
+ * @throws IOException
+ */
+ private void assertSameAsHead(Ref ref) throws IOException {
+ Ref headRef = db.getRef(Constants.HEAD);
+ assertEquals(headRef.getName(), ref.getName());
+ assertEquals(headRef.getObjectId(), ref.getObjectId());
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashApplyCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashApplyCommandTest.java
index 95b1419..ce235a7 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashApplyCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashApplyCommandTest.java
@@ -609,7 +609,7 @@ public class StashApplyCommandTest extends RepositoryTestCase {
} catch (JGitInternalException e) {
assertEquals(MessageFormat.format(
JGitText.get().stashCommitIncorrectNumberOfParents,
- head.name(), 0),
+ head.name(), "0"),
e.getMessage());
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java
index 3871203..ae8551e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java
@@ -110,7 +110,7 @@ public class StashCreateCommandTest extends RepositoryTestCase {
int parentCount)
throws IOException {
assertNotNull(commit);
- Ref stashRef = db.getRef(Constants.R_STASH);
+ Ref stashRef = db.exactRef(Constants.R_STASH);
assertNotNull(stashRef);
assertEquals(commit, stashRef.getObjectId());
assertNotNull(commit.getAuthorIdent());
@@ -118,12 +118,9 @@ public class StashCreateCommandTest extends RepositoryTestCase {
assertEquals(parentCount, commit.getParentCount());
// Load parents
- RevWalk walk = new RevWalk(db);
- try {
+ try (RevWalk walk = new RevWalk(db)) {
for (RevCommit parent : commit.getParents())
walk.parseBody(parent);
- } finally {
- walk.release();
}
assertEquals(1, commit.getParent(1).getParentCount());
@@ -144,37 +141,28 @@ public class StashCreateCommandTest extends RepositoryTestCase {
private List<DiffEntry> diffWorkingAgainstHead(final RevCommit commit)
throws IOException {
- TreeWalk walk = createTreeWalk();
- try {
+ try (TreeWalk walk = createTreeWalk()) {
walk.addTree(commit.getParent(0).getTree());
walk.addTree(commit.getTree());
return DiffEntry.scan(walk);
- } finally {
- walk.release();
}
}
private List<DiffEntry> diffIndexAgainstHead(final RevCommit commit)
throws IOException {
- TreeWalk walk = createTreeWalk();
- try {
+ try (TreeWalk walk = createTreeWalk()) {
walk.addTree(commit.getParent(0).getTree());
walk.addTree(commit.getParent(1).getTree());
return DiffEntry.scan(walk);
- } finally {
- walk.release();
}
}
private List<DiffEntry> diffIndexAgainstWorking(final RevCommit commit)
throws IOException {
- TreeWalk walk = createTreeWalk();
- try {
+ try (TreeWalk walk = createTreeWalk()) {
walk.addTree(commit.getParent(1).getTree());
walk.addTree(commit.getTree());
return DiffEntry.scan(walk);
- } finally {
- walk.release();
}
}
@@ -224,11 +212,12 @@ public class StashCreateCommandTest extends RepositoryTestCase {
writeTrashFile("file", "content2");
RevCommit stashedWorkTree = Git.wrap(db).stashCreate().call();
validateStashedCommit(stashedWorkTree);
- RevWalk walk = new RevWalk(db);
- RevCommit stashedIndex = stashedWorkTree.getParent(1);
- walk.parseBody(stashedIndex);
- walk.parseBody(stashedIndex.getTree());
- walk.parseBody(stashedIndex.getParent(0));
+ try (RevWalk walk = new RevWalk(db)) {
+ RevCommit stashedIndex = stashedWorkTree.getParent(1);
+ walk.parseBody(stashedIndex);
+ walk.parseBody(stashedIndex.getTree());
+ walk.parseBody(stashedIndex.getParent(0));
+ }
List<DiffEntry> workTreeStashAgainstWorkTree = diffWorkingAgainstHead(stashedWorkTree);
assertEquals(1, workTreeStashAgainstWorkTree.size());
List<DiffEntry> workIndexAgainstWorkTree = diffIndexAgainstHead(stashedWorkTree);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java
index cfad817..859277e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java
@@ -96,13 +96,13 @@ public class StashDropCommandTest extends RepositoryTestCase {
@Test
public void dropWithInvalidLogIndex() throws Exception {
write(committedFile, "content2");
- Ref stashRef = git.getRepository().getRef(Constants.R_STASH);
+ Ref stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNull(stashRef);
RevCommit stashed = git.stashCreate().call();
assertNotNull(stashed);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
- assertEquals(stashed, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
+ assertEquals(stashed,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
try {
assertNull(git.stashDrop().setStashRef(100).call());
fail("Exception not thrown");
@@ -115,15 +115,15 @@ public class StashDropCommandTest extends RepositoryTestCase {
@Test
public void dropSingleStashedCommit() throws Exception {
write(committedFile, "content2");
- Ref stashRef = git.getRepository().getRef(Constants.R_STASH);
+ Ref stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNull(stashRef);
RevCommit stashed = git.stashCreate().call();
assertNotNull(stashed);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
- assertEquals(stashed, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
+ assertEquals(stashed,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
assertNull(git.stashDrop().call());
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNull(stashRef);
ReflogReader reader = git.getRepository().getReflogReader(
@@ -134,25 +134,25 @@ public class StashDropCommandTest extends RepositoryTestCase {
@Test
public void dropAll() throws Exception {
write(committedFile, "content2");
- Ref stashRef = git.getRepository().getRef(Constants.R_STASH);
+ Ref stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNull(stashRef);
RevCommit firstStash = git.stashCreate().call();
assertNotNull(firstStash);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
- assertEquals(firstStash, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ assertEquals(firstStash,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
write(committedFile, "content3");
RevCommit secondStash = git.stashCreate().call();
assertNotNull(secondStash);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
- assertEquals(secondStash, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ assertEquals(secondStash,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
assertNull(git.stashDrop().setAll(true).call());
- assertNull(git.getRepository().getRef(Constants.R_STASH));
+ assertNull(git.getRepository().exactRef(Constants.R_STASH));
ReflogReader reader = git.getRepository().getReflogReader(
Constants.R_STASH);
@@ -162,25 +162,25 @@ public class StashDropCommandTest extends RepositoryTestCase {
@Test
public void dropFirstStashedCommit() throws Exception {
write(committedFile, "content2");
- Ref stashRef = git.getRepository().getRef(Constants.R_STASH);
+ Ref stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNull(stashRef);
RevCommit firstStash = git.stashCreate().call();
assertNotNull(firstStash);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
- assertEquals(firstStash, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ assertEquals(firstStash,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
write(committedFile, "content3");
RevCommit secondStash = git.stashCreate().call();
assertNotNull(secondStash);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
- assertEquals(secondStash, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ assertEquals(secondStash,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
assertEquals(firstStash, git.stashDrop().call());
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
assertEquals(firstStash, stashRef.getObjectId());
@@ -196,33 +196,33 @@ public class StashDropCommandTest extends RepositoryTestCase {
@Test
public void dropMiddleStashCommit() throws Exception {
write(committedFile, "content2");
- Ref stashRef = git.getRepository().getRef(Constants.R_STASH);
+ Ref stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNull(stashRef);
RevCommit firstStash = git.stashCreate().call();
assertNotNull(firstStash);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
- assertEquals(firstStash, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ assertEquals(firstStash,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
write(committedFile, "content3");
RevCommit secondStash = git.stashCreate().call();
assertNotNull(secondStash);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
- assertEquals(secondStash, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ assertEquals(secondStash,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
write(committedFile, "content4");
RevCommit thirdStash = git.stashCreate().call();
assertNotNull(thirdStash);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
- assertEquals(thirdStash, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ assertEquals(thirdStash,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
assertEquals(thirdStash, git.stashDrop().setStashRef(1).call());
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
assertEquals(thirdStash, stashRef.getObjectId());
@@ -241,46 +241,46 @@ public class StashDropCommandTest extends RepositoryTestCase {
@Test
public void dropBoundaryStashedCommits() throws Exception {
write(committedFile, "content2");
- Ref stashRef = git.getRepository().getRef(Constants.R_STASH);
+ Ref stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNull(stashRef);
RevCommit firstStash = git.stashCreate().call();
assertNotNull(firstStash);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
- assertEquals(firstStash, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ assertEquals(firstStash,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
write(committedFile, "content3");
RevCommit secondStash = git.stashCreate().call();
assertNotNull(secondStash);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
- assertEquals(secondStash, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ assertEquals(secondStash,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
write(committedFile, "content4");
RevCommit thirdStash = git.stashCreate().call();
assertNotNull(thirdStash);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
- assertEquals(thirdStash, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ assertEquals(thirdStash,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
write(committedFile, "content5");
RevCommit fourthStash = git.stashCreate().call();
assertNotNull(fourthStash);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
- assertEquals(fourthStash, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ assertEquals(fourthStash,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
assertEquals(thirdStash, git.stashDrop().call());
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
assertEquals(thirdStash, stashRef.getObjectId());
assertEquals(thirdStash, git.stashDrop().setStashRef(2).call());
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
assertEquals(thirdStash, stashRef.getObjectId());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StatusCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StatusCommandTest.java
index 4765023..c70604e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StatusCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StatusCommandTest.java
@@ -53,6 +53,7 @@ import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.NoFilepatternException;
import org.eclipse.jgit.errors.NoWorkTreeException;
import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.Sets;
import org.junit.Test;
public class StatusCommandTest extends RepositoryTestCase {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/TagCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/TagCommandTest.java
index 061d29f..87098b6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/TagCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/TagCommandTest.java
@@ -62,171 +62,185 @@ public class TagCommandTest extends RepositoryTestCase {
@Test
public void testTaggingOnHead() throws GitAPIException, IOException {
- Git git = new Git(db);
- RevCommit commit = git.commit().setMessage("initial commit").call();
- Ref tagRef = git.tag().setName("tag").call();
- assertEquals(commit.getId(), db.peel(tagRef).getPeeledObjectId());
- RevWalk walk = new RevWalk(db);
- assertEquals("tag", walk.parseTag(tagRef.getObjectId()).getTagName());
+ try (Git git = new Git(db);
+ RevWalk walk = new RevWalk(db)) {
+ RevCommit commit = git.commit().setMessage("initial commit").call();
+ Ref tagRef = git.tag().setName("tag").call();
+ assertEquals(commit.getId(), db.peel(tagRef).getPeeledObjectId());
+ assertEquals("tag", walk.parseTag(tagRef.getObjectId()).getTagName());
+ }
}
@Test
public void testTagging() throws GitAPIException, JGitInternalException {
- Git git = new Git(db);
- git.commit().setMessage("initial commit").call();
- RevCommit commit = git.commit().setMessage("second commit").call();
- git.commit().setMessage("third commit").call();
- Ref tagRef = git.tag().setObjectId(commit).setName("tag").call();
- assertEquals(commit.getId(), db.peel(tagRef).getPeeledObjectId());
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("initial commit").call();
+ RevCommit commit = git.commit().setMessage("second commit").call();
+ git.commit().setMessage("third commit").call();
+ Ref tagRef = git.tag().setObjectId(commit).setName("tag").call();
+ assertEquals(commit.getId(), db.peel(tagRef).getPeeledObjectId());
+ }
}
@Test
public void testUnannotatedTagging() throws GitAPIException,
JGitInternalException {
- Git git = new Git(db);
- git.commit().setMessage("initial commit").call();
- RevCommit commit = git.commit().setMessage("second commit").call();
- git.commit().setMessage("third commit").call();
- Ref tagRef = git.tag().setObjectId(commit).setName("tag")
- .setAnnotated(false).call();
- assertEquals(commit.getId(), tagRef.getObjectId());
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("initial commit").call();
+ RevCommit commit = git.commit().setMessage("second commit").call();
+ git.commit().setMessage("third commit").call();
+ Ref tagRef = git.tag().setObjectId(commit).setName("tag")
+ .setAnnotated(false).call();
+ assertEquals(commit.getId(), tagRef.getObjectId());
+ }
}
@Test
public void testEmptyTagName() throws GitAPIException {
- Git git = new Git(db);
- git.commit().setMessage("initial commit").call();
- try {
- // forget to tag name
- git.tag().setMessage("some message").call();
- fail("We should have failed without a tag name");
- } catch (InvalidTagNameException e) {
- // should hit here
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("initial commit").call();
+ try {
+ // forget to tag name
+ git.tag().setMessage("some message").call();
+ fail("We should have failed without a tag name");
+ } catch (InvalidTagNameException e) {
+ // should hit here
+ }
}
}
@Test
public void testInvalidTagName() throws GitAPIException {
- Git git = new Git(db);
- git.commit().setMessage("initial commit").call();
- try {
- git.tag().setName("bad~tag~name").setMessage("some message").call();
- fail("We should have failed due to a bad tag name");
- } catch (InvalidTagNameException e) {
- // should hit here
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("initial commit").call();
+ try {
+ git.tag().setName("bad~tag~name").setMessage("some message").call();
+ fail("We should have failed due to a bad tag name");
+ } catch (InvalidTagNameException e) {
+ // should hit here
+ }
}
}
@Test
public void testFailureOnSignedTags() throws GitAPIException {
- Git git = new Git(db);
- git.commit().setMessage("initial commit").call();
- try {
- git.tag().setSigned(true).setName("tag").call();
- fail("We should have failed with an UnsupportedOperationException due to signed tag");
- } catch (UnsupportedOperationException e) {
- // should hit here
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("initial commit").call();
+ try {
+ git.tag().setSigned(true).setName("tag").call();
+ fail("We should have failed with an UnsupportedOperationException due to signed tag");
+ } catch (UnsupportedOperationException e) {
+ // should hit here
+ }
}
}
@Test
public void testDelete() throws Exception {
- Git git = new Git(db);
- git.commit().setMessage("initial commit").call();
- Ref tagRef = git.tag().setName("tag").call();
- assertEquals(1, db.getTags().size());
-
- List<String> deleted = git.tagDelete().setTags(tagRef.getName())
- .call();
- assertEquals(1, deleted.size());
- assertEquals(tagRef.getName(), deleted.get(0));
- assertEquals(0, db.getTags().size());
-
- Ref tagRef1 = git.tag().setName("tag1").call();
- Ref tagRef2 = git.tag().setName("tag2").call();
- assertEquals(2, db.getTags().size());
- deleted = git.tagDelete().setTags(tagRef1.getName(), tagRef2.getName())
- .call();
- assertEquals(2, deleted.size());
- assertEquals(0, db.getTags().size());
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("initial commit").call();
+ Ref tagRef = git.tag().setName("tag").call();
+ assertEquals(1, db.getTags().size());
+
+ List<String> deleted = git.tagDelete().setTags(tagRef.getName())
+ .call();
+ assertEquals(1, deleted.size());
+ assertEquals(tagRef.getName(), deleted.get(0));
+ assertEquals(0, db.getTags().size());
+
+ Ref tagRef1 = git.tag().setName("tag1").call();
+ Ref tagRef2 = git.tag().setName("tag2").call();
+ assertEquals(2, db.getTags().size());
+ deleted = git.tagDelete().setTags(tagRef1.getName(), tagRef2.getName())
+ .call();
+ assertEquals(2, deleted.size());
+ assertEquals(0, db.getTags().size());
+ }
}
@Test
public void testDeleteFullName() throws Exception {
- Git git = new Git(db);
- git.commit().setMessage("initial commit").call();
- Ref tagRef = git.tag().setName("tag").call();
- assertEquals(1, db.getTags().size());
-
- List<String> deleted = git.tagDelete()
- .setTags(Repository.shortenRefName(tagRef.getName())).call();
- assertEquals(1, deleted.size());
- assertEquals(tagRef.getName(), deleted.get(0));
- assertEquals(0, db.getTags().size());
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("initial commit").call();
+ Ref tagRef = git.tag().setName("tag").call();
+ assertEquals(1, db.getTags().size());
+
+ List<String> deleted = git.tagDelete()
+ .setTags(Repository.shortenRefName(tagRef.getName())).call();
+ assertEquals(1, deleted.size());
+ assertEquals(tagRef.getName(), deleted.get(0));
+ assertEquals(0, db.getTags().size());
+ }
}
@Test
public void testDeleteEmptyTagNames() throws Exception {
- Git git = new Git(db);
- git.commit().setMessage("initial commit").call();
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("initial commit").call();
- List<String> deleted = git.tagDelete().setTags().call();
- assertEquals(0, deleted.size());
+ List<String> deleted = git.tagDelete().setTags().call();
+ assertEquals(0, deleted.size());
+ }
}
@Test
public void testDeleteNonExisting() throws Exception {
- Git git = new Git(db);
- git.commit().setMessage("initial commit").call();
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("initial commit").call();
- List<String> deleted = git.tagDelete().setTags("tag").call();
- assertEquals(0, deleted.size());
+ List<String> deleted = git.tagDelete().setTags("tag").call();
+ assertEquals(0, deleted.size());
+ }
}
@Test
public void testDeleteBadName() throws Exception {
- Git git = new Git(db);
- git.commit().setMessage("initial commit").call();
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("initial commit").call();
- List<String> deleted = git.tagDelete().setTags("bad~tag~name")
- .call();
- assertEquals(0, deleted.size());
+ List<String> deleted = git.tagDelete().setTags("bad~tag~name")
+ .call();
+ assertEquals(0, deleted.size());
+ }
}
@Test
public void testShouldNotBlowUpIfThereAreNoTagsInRepository()
throws Exception {
- Git git = new Git(db);
- git.add().addFilepattern("*").call();
- git.commit().setMessage("initial commit").call();
- List<Ref> list = git.tagList().call();
- assertEquals(0, list.size());
+ try (Git git = new Git(db)) {
+ git.add().addFilepattern("*").call();
+ git.commit().setMessage("initial commit").call();
+ List<Ref> list = git.tagList().call();
+ assertEquals(0, list.size());
+ }
}
@Test
public void testShouldNotBlowUpIfThereAreNoCommitsInRepository()
throws Exception {
- Git git = new Git(db);
- List<Ref> list = git.tagList().call();
- assertEquals(0, list.size());
+ try (Git git = new Git(db)) {
+ List<Ref> list = git.tagList().call();
+ assertEquals(0, list.size());
+ }
}
@Test
public void testListAllTagsInRepositoryInOrder() throws Exception {
- Git git = new Git(db);
- git.add().addFilepattern("*").call();
- git.commit().setMessage("initial commit").call();
+ try (Git git = new Git(db)) {
+ git.add().addFilepattern("*").call();
+ git.commit().setMessage("initial commit").call();
- git.tag().setName("v3").call();
- git.tag().setName("v2").call();
- git.tag().setName("v10").call();
+ git.tag().setName("v3").call();
+ git.tag().setName("v2").call();
+ git.tag().setName("v10").call();
- List<Ref> list = git.tagList().call();
+ List<Ref> list = git.tagList().call();
- assertEquals(3, list.size());
- assertEquals("refs/tags/v10", list.get(0).getName());
- assertEquals("refs/tags/v2", list.get(1).getName());
- assertEquals("refs/tags/v3", list.get(2).getName());
+ assertEquals(3, list.size());
+ assertEquals("refs/tags/v10", list.get(0).getName());
+ assertEquals("refs/tags/v2", list.get(1).getName());
+ assertEquals("refs/tags/v3", list.get(2).getName());
+ }
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/blame/BlameGeneratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/blame/BlameGeneratorTest.java
index a7bce1e..42909f0 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/blame/BlameGeneratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/blame/BlameGeneratorTest.java
@@ -70,8 +70,7 @@ public class BlameGeneratorTest extends RepositoryTestCase {
git.add().addFilepattern("file.txt").call();
RevCommit c2 = git.commit().setMessage("create file").call();
- BlameGenerator generator = new BlameGenerator(db, "file.txt");
- try {
+ try (BlameGenerator generator = new BlameGenerator(db, "file.txt")) {
generator.push(null, db.resolve(Constants.HEAD));
assertEquals(3, generator.getResultContents().size());
@@ -94,8 +93,6 @@ public class BlameGeneratorTest extends RepositoryTestCase {
assertEquals("file.txt", generator.getSourcePath());
assertFalse(generator.next());
- } finally {
- generator.release();
}
}
@@ -123,8 +120,7 @@ public class BlameGeneratorTest extends RepositoryTestCase {
git.add().addFilepattern(FILENAME_2).call();
RevCommit c2 = git.commit().setMessage("change file2").call();
- BlameGenerator generator = new BlameGenerator(db, FILENAME_2);
- try {
+ try (BlameGenerator generator = new BlameGenerator(db, FILENAME_2)) {
generator.push(null, db.resolve(Constants.HEAD));
assertEquals(3, generator.getResultContents().size());
@@ -147,13 +143,10 @@ public class BlameGeneratorTest extends RepositoryTestCase {
assertEquals(FILENAME_1, generator.getSourcePath());
assertFalse(generator.next());
- } finally {
- generator.release();
}
// and test again with other BlameGenerator API:
- generator = new BlameGenerator(db, FILENAME_2);
- try {
+ try (BlameGenerator generator = new BlameGenerator(db, FILENAME_2)) {
generator.push(null, db.resolve(Constants.HEAD));
BlameResult result = generator.computeBlameResult();
@@ -167,9 +160,6 @@ public class BlameGeneratorTest extends RepositoryTestCase {
assertEquals(c1, result.getSourceCommit(2));
assertEquals(FILENAME_1, result.getSourcePath(2));
-
- } finally {
- generator.release();
}
}
@@ -193,8 +183,7 @@ public class BlameGeneratorTest extends RepositoryTestCase {
git.add().addFilepattern("file.txt").call();
RevCommit c3 = git.commit().setMessage("create file").call();
- BlameGenerator generator = new BlameGenerator(db, "file.txt");
- try {
+ try (BlameGenerator generator = new BlameGenerator(db, "file.txt")) {
generator.push(null, db.resolve(Constants.HEAD));
assertEquals(3, generator.getResultContents().size());
@@ -204,8 +193,6 @@ public class BlameGeneratorTest extends RepositoryTestCase {
assertEquals(3, generator.getResultEnd());
assertFalse(generator.next());
- } finally {
- generator.release();
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java
index 6865406..9f82b8a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesMatcherTest.java
@@ -51,9 +51,6 @@ import org.junit.Test;
/**
* Tests git attributes pattern matches
- * <p>
- * Inspired by {@link org.eclipse.jgit.ignore.IgnoreMatcherTest}
- * </p>
*/
public class AttributesMatcherTest {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java
index 49279e6..0e595e6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java
@@ -52,9 +52,7 @@ import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.Collections;
-import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Map;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.attributes.Attribute.State;
@@ -243,27 +241,29 @@ public class AttributesNodeDirCacheIteratorTest extends RepositoryTestCase {
DirCacheIterator itr = walk.getTree(0, DirCacheIterator.class);
assertNotNull("has tree", itr);
- AttributesNode attributeNode = itr.getEntryAttributesNode(db
+ AttributesNode attributesNode = itr.getEntryAttributesNode(db
.newObjectReader());
- assertAttributeNode(pathName, attributeNode, nodeAttrs);
+ assertAttributesNode(pathName, attributesNode, nodeAttrs);
if (D.equals(type))
walk.enterSubtree();
}
- private void assertAttributeNode(String pathName,
- AttributesNode attributeNode, List<Attribute> nodeAttrs) {
- if (attributeNode == null)
+ private void assertAttributesNode(String pathName,
+ AttributesNode attributesNode, List<Attribute> nodeAttrs) {
+ if (attributesNode == null)
assertTrue(nodeAttrs == null || nodeAttrs.isEmpty());
else {
- Map<String, Attribute> entryAttributes = new LinkedHashMap<String, Attribute>();
- attributeNode.getAttributes(pathName, false, entryAttributes);
+ Attributes entryAttributes = new Attributes();
+ attributesNode.getAttributes(pathName,
+ false, entryAttributes);
if (nodeAttrs != null && !nodeAttrs.isEmpty()) {
for (Attribute attribute : nodeAttrs) {
- assertThat(entryAttributes.values(), hasItem(attribute));
+ assertThat(entryAttributes.getAll(),
+ hasItem(attribute));
}
} else {
assertTrue(
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributeNodeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java
similarity index 86%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributeNodeTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java
index ea25036..d478a7c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributeNodeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java
@@ -49,10 +49,6 @@ import static org.junit.Assert.assertEquals;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
import org.junit.After;
import org.junit.Test;
@@ -60,7 +56,7 @@ import org.junit.Test;
/**
* Test {@link AttributesNode}
*/
-public class AttributeNodeTest {
+public class AttributesNodeTest {
private static final Attribute A_SET_ATTR = new Attribute("A", SET);
@@ -104,8 +100,8 @@ public class AttributeNodeTest {
is = new ByteArrayInputStream(attributeFileContent.getBytes());
AttributesNode node = new AttributesNode();
node.parse(is);
- assertAttribute("file.type1", node, Collections.<Attribute> emptySet());
- assertAttribute("file.type2", node, Collections.<Attribute> emptySet());
+ assertAttribute("file.type1", node, new Attributes());
+ assertAttribute("file.type2", node, new Attributes());
}
@Test
@@ -115,7 +111,7 @@ public class AttributeNodeTest {
is = new ByteArrayInputStream(attributeFileContent.getBytes());
AttributesNode node = new AttributesNode();
node.parse(is);
- assertAttribute("file.type1", node, Collections.<Attribute> emptySet());
+ assertAttribute("file.type1", node, new Attributes());
assertAttribute("file.type2", node, asSet(A_UNSET_ATTR));
}
@@ -127,8 +123,8 @@ public class AttributeNodeTest {
is = new ByteArrayInputStream(attributeFileContent.getBytes());
AttributesNode node = new AttributesNode();
node.parse(is);
- assertAttribute("file.type1", node, Collections.<Attribute> emptySet());
- assertAttribute("file.type2", node, Collections.<Attribute> emptySet());
+ assertAttribute("file.type1", node, new Attributes());
+ assertAttribute("file.type2", node, new Attributes());
assertAttribute("file.type3", node, asSet(new Attribute("attr", "")));
}
@@ -166,17 +162,14 @@ public class AttributeNodeTest {
}
private void assertAttribute(String path, AttributesNode node,
- Set<Attribute> attrs) {
- HashMap<String, Attribute> attributes = new HashMap<String, Attribute>();
+ Attributes attrs) {
+ Attributes attributes = new Attributes();
node.getAttributes(path, false, attributes);
- assertEquals(attrs, new HashSet<Attribute>(attributes.values()));
+ assertEquals(attrs, attributes);
}
- static Set<Attribute> asSet(Attribute... attrs) {
- Set<Attribute> result = new HashSet<Attribute>();
- for (Attribute attr : attrs)
- result.add(attr);
- return result;
+ static Attributes asSet(Attribute... attrs) {
+ return new Attributes(attrs);
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java
index 64b0535..4215ba2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java
@@ -53,12 +53,9 @@ import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
-import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Map;
import org.eclipse.jgit.attributes.Attribute.State;
-import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.FileMode;
@@ -76,14 +73,10 @@ public class AttributesNodeWorkingTreeIteratorTest extends RepositoryTestCase {
private static final FileMode F = FileMode.REGULAR_FILE;
- private static Attribute EOL_CRLF = new Attribute("eol", "crlf");
-
private static Attribute EOL_LF = new Attribute("eol", "lf");
private static Attribute DELTA_UNSET = new Attribute("delta", State.UNSET);
- private static Attribute CUSTOM_VALUE = new Attribute("custom", "value");
-
private TreeWalk walk;
@Test
@@ -112,25 +105,19 @@ public class AttributesNodeWorkingTreeIteratorTest extends RepositoryTestCase {
walk = beginWalk();
assertIteration(F, ".gitattributes");
- assertIteration(F, "global.txt", asList(EOL_LF), null,
- asList(CUSTOM_VALUE));
- assertIteration(F, "readme.txt", asList(EOL_LF), null,
- asList(CUSTOM_VALUE));
+ assertIteration(F, "global.txt", asList(EOL_LF));
+ assertIteration(F, "readme.txt", asList(EOL_LF));
assertIteration(D, "src");
assertIteration(D, "src/config");
assertIteration(F, "src/config/.gitattributes");
- assertIteration(F, "src/config/readme.txt", asList(DELTA_UNSET), null,
- asList(CUSTOM_VALUE));
- assertIteration(F, "src/config/windows.file", null, asList(EOL_CRLF),
- null);
- assertIteration(F, "src/config/windows.txt", asList(DELTA_UNSET),
- asList(EOL_CRLF), asList(CUSTOM_VALUE));
+ assertIteration(F, "src/config/readme.txt", asList(DELTA_UNSET));
+ assertIteration(F, "src/config/windows.file", null);
+ assertIteration(F, "src/config/windows.txt", asList(DELTA_UNSET));
- assertIteration(F, "windows.file", null, asList(EOL_CRLF), null);
- assertIteration(F, "windows.txt", asList(EOL_LF), asList(EOL_CRLF),
- asList(CUSTOM_VALUE));
+ assertIteration(F, "windows.file", null);
+ assertIteration(F, "windows.txt", asList(EOL_LF));
endWalk();
}
@@ -212,14 +199,11 @@ public class AttributesNodeWorkingTreeIteratorTest extends RepositoryTestCase {
private void assertIteration(FileMode type, String pathName)
throws IOException {
- assertIteration(type, pathName, Collections.<Attribute> emptyList(),
- Collections.<Attribute> emptyList(),
- Collections.<Attribute> emptyList());
+ assertIteration(type, pathName, Collections.<Attribute> emptyList());
}
private void assertIteration(FileMode type, String pathName,
- List<Attribute> nodeAttrs, List<Attribute> infoAttrs,
- List<Attribute> globalAttrs)
+ List<Attribute> nodeAttrs)
throws IOException {
assertTrue("walk has entry", walk.next());
assertEquals(pathName, walk.getPathString());
@@ -227,29 +211,27 @@ public class AttributesNodeWorkingTreeIteratorTest extends RepositoryTestCase {
WorkingTreeIterator itr = walk.getTree(0, WorkingTreeIterator.class);
assertNotNull("has tree", itr);
- AttributesNode attributeNode = itr.getEntryAttributesNode();
- assertAttributeNode(pathName, attributeNode, nodeAttrs);
- AttributesNode infoAttributeNode = itr.getInfoAttributesNode();
- assertAttributeNode(pathName, infoAttributeNode, infoAttrs);
- AttributesNode globalAttributeNode = itr.getGlobalAttributesNode();
- assertAttributeNode(pathName, globalAttributeNode, globalAttrs);
+ AttributesNode attributesNode = itr.getEntryAttributesNode();
+ assertAttributesNode(pathName, attributesNode, nodeAttrs);
if (D.equals(type))
walk.enterSubtree();
}
- private void assertAttributeNode(String pathName,
- AttributesNode attributeNode, List<Attribute> nodeAttrs) {
- if (attributeNode == null)
+ private void assertAttributesNode(String pathName,
+ AttributesNode attributesNode, List<Attribute> nodeAttrs) {
+ if (attributesNode == null)
assertTrue(nodeAttrs == null || nodeAttrs.isEmpty());
else {
- Map<String, Attribute> entryAttributes = new LinkedHashMap<String, Attribute>();
- attributeNode.getAttributes(pathName, false, entryAttributes);
+ Attributes entryAttributes = new Attributes();
+ attributesNode.getAttributes(pathName,
+ false, entryAttributes);
if (nodeAttrs != null && !nodeAttrs.isEmpty()) {
for (Attribute attribute : nodeAttrs) {
- assertThat(entryAttributes.values(), hasItem(attribute));
+ assertThat(entryAttributes.getAll(),
+ hasItem(attribute));
}
} else {
assertTrue(
@@ -270,7 +252,7 @@ public class AttributesNodeWorkingTreeIteratorTest extends RepositoryTestCase {
writeTrashFile(name, data.toString());
}
- private TreeWalk beginWalk() throws CorruptObjectException {
+ private TreeWalk beginWalk() {
TreeWalk newWalk = new TreeWalk(db);
newWalk.addTree(new FileTreeIterator(db));
return newWalk;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java
new file mode 100644
index 0000000..b044c01
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java
@@ -0,0 +1,866 @@
+/*
+ * Copyright (C) 2014, Obeo.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.attributes;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.NoFilepatternException;
+import org.eclipse.jgit.attributes.Attribute.State;
+import org.eclipse.jgit.dircache.DirCacheIterator;
+import org.eclipse.jgit.errors.NoWorkTreeException;
+import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.treewalk.FileTreeIterator;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests the attributes are correctly computed in a {@link TreeWalk}.
+ *
+ * @see TreeWalk#getAttributes()
+ */
+public class TreeWalkAttributeTest extends RepositoryTestCase {
+
+ private static final FileMode M = FileMode.MISSING;
+
+ private static final FileMode D = FileMode.TREE;
+
+ private static final FileMode F = FileMode.REGULAR_FILE;
+
+ private static Attribute EOL_CRLF = new Attribute("eol", "crlf");
+
+ private static Attribute EOL_LF = new Attribute("eol", "lf");
+
+ private static Attribute TEXT_SET = new Attribute("text", State.SET);
+
+ private static Attribute TEXT_UNSET = new Attribute("text", State.UNSET);
+
+ private static Attribute DELTA_UNSET = new Attribute("delta", State.UNSET);
+
+ private static Attribute DELTA_SET = new Attribute("delta", State.SET);
+
+ private static Attribute CUSTOM_GLOBAL = new Attribute("custom", "global");
+
+ private static Attribute CUSTOM_INFO = new Attribute("custom", "info");
+
+ private static Attribute CUSTOM_ROOT = new Attribute("custom", "root");
+
+ private static Attribute CUSTOM_PARENT = new Attribute("custom", "parent");
+
+ private static Attribute CUSTOM_CURRENT = new Attribute("custom", "current");
+
+ private static Attribute CUSTOM2_UNSET = new Attribute("custom2",
+ State.UNSET);
+
+ private static Attribute CUSTOM2_SET = new Attribute("custom2", State.SET);
+
+ private TreeWalk walk;
+
+ private TreeWalk ci_walk;
+
+ private Git git;
+
+ private File customAttributeFile;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ git = new Git(db);
+ }
+
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ if (customAttributeFile != null)
+ customAttributeFile.delete();
+ }
+
+ /**
+ * Checks that the attributes are computed correctly depending on the
+ * operation type.
+ * <p>
+ * In this test we changed the content of the attribute files in the working
+ * tree compared to the one in the index.
+ * </p>
+ *
+ * @throws IOException
+ * @throws NoFilepatternException
+ * @throws GitAPIException
+ */
+ @Test
+ public void testCheckinCheckoutDifferences() throws IOException,
+ NoFilepatternException, GitAPIException {
+
+ writeGlobalAttributeFile("globalAttributesFile", "*.txt -custom2");
+ writeAttributesFile(".git/info/attributes", "*.txt eol=crlf");
+ writeAttributesFile(".gitattributes", "*.txt custom=root");
+ writeAttributesFile("level1/.gitattributes", "*.txt text");
+ writeAttributesFile("level1/level2/.gitattributes", "*.txt -delta");
+
+ writeTrashFile("l0.txt", "");
+
+ writeTrashFile("level1/l1.txt", "");
+
+ writeTrashFile("level1/level2/l2.txt", "");
+
+ git.add().addFilepattern(".").call();
+
+ beginWalk();
+
+ // Modify all attributes
+ writeGlobalAttributeFile("globalAttributesFile", "*.txt custom2");
+ writeAttributesFile(".git/info/attributes", "*.txt eol=lf");
+ writeAttributesFile(".gitattributes", "*.txt custom=info");
+ writeAttributesFile("level1/.gitattributes", "*.txt -text");
+ writeAttributesFile("level1/level2/.gitattributes", "*.txt delta");
+
+ assertEntry(F, ".gitattributes");
+ assertEntry(F, "l0.txt", asSet(EOL_LF, CUSTOM_INFO, CUSTOM2_SET),
+ asSet(EOL_LF, CUSTOM_ROOT, CUSTOM2_SET));
+
+ assertEntry(D, "level1");
+ assertEntry(F, "level1/.gitattributes");
+ assertEntry(F, "level1/l1.txt",
+ asSet(EOL_LF, CUSTOM_INFO, CUSTOM2_SET, TEXT_UNSET),
+ asSet(EOL_LF, CUSTOM_ROOT, CUSTOM2_SET, TEXT_SET));
+
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/.gitattributes");
+ assertEntry(F, "level1/level2/l2.txt",
+ asSet(EOL_LF, CUSTOM_INFO, CUSTOM2_SET, TEXT_UNSET, DELTA_SET),
+ asSet(EOL_LF, CUSTOM_ROOT, CUSTOM2_SET, TEXT_SET, DELTA_UNSET));
+
+ endWalk();
+ }
+
+ /**
+ * Checks that the index is used as fallback when the git attributes file
+ * are missing in the working tree.
+ *
+ * @throws IOException
+ * @throws NoFilepatternException
+ * @throws GitAPIException
+ */
+ @Test
+ public void testIndexOnly() throws IOException, NoFilepatternException,
+ GitAPIException {
+ List<File> attrFiles = new ArrayList<File>();
+ attrFiles.add(writeGlobalAttributeFile("globalAttributesFile",
+ "*.txt -custom2"));
+ attrFiles.add(writeAttributesFile(".git/info/attributes",
+ "*.txt eol=crlf"));
+ attrFiles
+ .add(writeAttributesFile(".gitattributes", "*.txt custom=root"));
+ attrFiles
+ .add(writeAttributesFile("level1/.gitattributes", "*.txt text"));
+ attrFiles.add(writeAttributesFile("level1/level2/.gitattributes",
+ "*.txt -delta"));
+
+ writeTrashFile("l0.txt", "");
+
+ writeTrashFile("level1/l1.txt", "");
+
+ writeTrashFile("level1/level2/l2.txt", "");
+
+ git.add().addFilepattern(".").call();
+
+ // Modify all attributes
+ for (File attrFile : attrFiles)
+ attrFile.delete();
+
+ beginWalk();
+
+ assertEntry(M, ".gitattributes");
+ assertEntry(F, "l0.txt", asSet(CUSTOM_ROOT));
+
+ assertEntry(D, "level1");
+ assertEntry(M, "level1/.gitattributes");
+ assertEntry(F, "level1/l1.txt",
+
+ asSet(CUSTOM_ROOT, TEXT_SET));
+
+ assertEntry(D, "level1/level2");
+ assertEntry(M, "level1/level2/.gitattributes");
+ assertEntry(F, "level1/level2/l2.txt",
+
+ asSet(CUSTOM_ROOT, TEXT_SET, DELTA_UNSET));
+
+ endWalk();
+ }
+
+ /**
+ * Check that we search in the working tree for attributes although the file
+ * we are currently inspecting does not exist anymore in the working tree.
+ *
+ * @throws IOException
+ * @throws NoFilepatternException
+ * @throws GitAPIException
+ */
+ @Test
+ public void testIndexOnly2()
+ throws IOException, NoFilepatternException, GitAPIException {
+ File l2 = writeTrashFile("level1/level2/l2.txt", "");
+ writeTrashFile("level1/level2/l1.txt", "");
+
+ git.add().addFilepattern(".").call();
+
+ writeAttributesFile(".gitattributes", "*.txt custom=root");
+ assertTrue(l2.delete());
+
+ beginWalk();
+
+ assertEntry(F, ".gitattributes");
+ assertEntry(D, "level1");
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/l1.txt", asSet(CUSTOM_ROOT));
+ assertEntry(M, "level1/level2/l2.txt", asSet(CUSTOM_ROOT));
+
+ endWalk();
+ }
+
+ /**
+ * Basic test for git attributes.
+ * <p>
+ * In this use case files are present in both the working tree and the index
+ * </p>
+ *
+ * @throws IOException
+ * @throws NoFilepatternException
+ * @throws GitAPIException
+ */
+ @Test
+ public void testRules() throws IOException, NoFilepatternException,
+ GitAPIException {
+ writeAttributesFile(".git/info/attributes", "windows* eol=crlf");
+
+ writeAttributesFile(".gitattributes", "*.txt eol=lf");
+ writeTrashFile("windows.file", "");
+ writeTrashFile("windows.txt", "");
+ writeTrashFile("readme.txt", "");
+
+ writeAttributesFile("src/config/.gitattributes", "*.txt -delta");
+ writeTrashFile("src/config/readme.txt", "");
+ writeTrashFile("src/config/windows.file", "");
+ writeTrashFile("src/config/windows.txt", "");
+
+ beginWalk();
+
+ git.add().addFilepattern(".").call();
+
+ assertEntry(F, ".gitattributes");
+ assertEntry(F, "readme.txt", asSet(EOL_LF));
+
+ assertEntry(D, "src");
+ assertEntry(D, "src/config");
+ assertEntry(F, "src/config/.gitattributes");
+ assertEntry(F, "src/config/readme.txt", asSet(DELTA_UNSET, EOL_LF));
+ assertEntry(F, "src/config/windows.file", asSet(EOL_CRLF));
+ assertEntry(F, "src/config/windows.txt", asSet(DELTA_UNSET, EOL_CRLF));
+
+ assertEntry(F, "windows.file", asSet(EOL_CRLF));
+ assertEntry(F, "windows.txt", asSet(EOL_CRLF));
+
+ endWalk();
+ }
+
+ /**
+ * Checks that if there is no .gitattributes file in the repository
+ * everything still work fine.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testNoAttributes() throws IOException {
+ writeTrashFile("l0.txt", "");
+ writeTrashFile("level1/l1.txt", "");
+ writeTrashFile("level1/level2/l2.txt", "");
+
+ beginWalk();
+
+ assertEntry(F, "l0.txt");
+
+ assertEntry(D, "level1");
+ assertEntry(F, "level1/l1.txt");
+
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/l2.txt");
+
+ endWalk();
+ }
+
+ /**
+ * Checks that an empty .gitattribute file does not return incorrect value.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testEmptyGitAttributeFile() throws IOException {
+ writeAttributesFile(".git/info/attributes", "");
+ writeTrashFile("l0.txt", "");
+ writeAttributesFile(".gitattributes", "");
+ writeTrashFile("level1/l1.txt", "");
+ writeTrashFile("level1/level2/l2.txt", "");
+
+ beginWalk();
+
+ assertEntry(F, ".gitattributes");
+ assertEntry(F, "l0.txt");
+
+ assertEntry(D, "level1");
+ assertEntry(F, "level1/l1.txt");
+
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/l2.txt");
+
+ endWalk();
+ }
+
+ @Test
+ public void testNoMatchingAttributes() throws IOException {
+ writeAttributesFile(".git/info/attributes", "*.java delta");
+ writeAttributesFile(".gitattributes", "*.java -delta");
+ writeAttributesFile("levelA/.gitattributes", "*.java eol=lf");
+ writeAttributesFile("levelB/.gitattributes", "*.txt eol=lf");
+
+ writeTrashFile("levelA/lA.txt", "");
+
+ beginWalk();
+
+ assertEntry(F, ".gitattributes");
+
+ assertEntry(D, "levelA");
+ assertEntry(F, "levelA/.gitattributes");
+ assertEntry(F, "levelA/lA.txt");
+
+ assertEntry(D, "levelB");
+ assertEntry(F, "levelB/.gitattributes");
+
+ endWalk();
+ }
+
+ /**
+ * Checks that $GIT_DIR/info/attributes file has the highest precedence.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testPrecedenceInfo() throws IOException {
+ writeGlobalAttributeFile("globalAttributesFile", "*.txt custom=global");
+ writeAttributesFile(".git/info/attributes", "*.txt custom=info");
+ writeAttributesFile(".gitattributes", "*.txt custom=root");
+ writeAttributesFile("level1/.gitattributes", "*.txt custom=parent");
+ writeAttributesFile("level1/level2/.gitattributes",
+ "*.txt custom=current");
+
+ writeTrashFile("level1/level2/file.txt", "");
+
+ beginWalk();
+
+ assertEntry(F, ".gitattributes");
+
+ assertEntry(D, "level1");
+ assertEntry(F, "level1/.gitattributes");
+
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/.gitattributes");
+ assertEntry(F, "level1/level2/file.txt", asSet(CUSTOM_INFO));
+
+ endWalk();
+ }
+
+ /**
+ * Checks that a subfolder ".gitattributes" file has precedence over its
+ * parent.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testPrecedenceCurrent() throws IOException {
+ writeGlobalAttributeFile("globalAttributesFile", "*.txt custom=global");
+ writeAttributesFile(".gitattributes", "*.txt custom=root");
+ writeAttributesFile("level1/.gitattributes", "*.txt custom=parent");
+ writeAttributesFile("level1/level2/.gitattributes",
+ "*.txt custom=current");
+
+ writeTrashFile("level1/level2/file.txt", "");
+
+ beginWalk();
+
+ assertEntry(F, ".gitattributes");
+
+ assertEntry(D, "level1");
+ assertEntry(F, "level1/.gitattributes");
+
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/.gitattributes");
+ assertEntry(F, "level1/level2/file.txt", asSet(CUSTOM_CURRENT));
+
+ endWalk();
+ }
+
+ /**
+ * Checks that the parent ".gitattributes" file is used as fallback.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testPrecedenceParent() throws IOException {
+ writeGlobalAttributeFile("globalAttributesFile", "*.txt custom=global");
+ writeAttributesFile(".gitattributes", "*.txt custom=root");
+ writeAttributesFile("level1/.gitattributes", "*.txt custom=parent");
+
+ writeTrashFile("level1/level2/file.txt", "");
+
+ beginWalk();
+
+ assertEntry(F, ".gitattributes");
+
+ assertEntry(D, "level1");
+ assertEntry(F, "level1/.gitattributes");
+
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/file.txt", asSet(CUSTOM_PARENT));
+
+ endWalk();
+ }
+
+ /**
+ * Checks that the grand parent ".gitattributes" file is used as fallback.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testPrecedenceRoot() throws IOException {
+ writeGlobalAttributeFile("globalAttributesFile", "*.txt custom=global");
+ writeAttributesFile(".gitattributes", "*.txt custom=root");
+
+ writeTrashFile("level1/level2/file.txt", "");
+
+ beginWalk();
+
+ assertEntry(F, ".gitattributes");
+
+ assertEntry(D, "level1");
+
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/file.txt", asSet(CUSTOM_ROOT));
+
+ endWalk();
+ }
+
+ /**
+ * Checks that the global attribute file is used as fallback.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testPrecedenceGlobal() throws IOException {
+ writeGlobalAttributeFile("globalAttributesFile", "*.txt custom=global");
+
+ writeTrashFile("level1/level2/file.txt", "");
+
+ beginWalk();
+
+ assertEntry(D, "level1");
+
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/file.txt", asSet(CUSTOM_GLOBAL));
+
+ endWalk();
+ }
+
+ /**
+ * Checks the precedence on a hierarchy with multiple attributes.
+ * <p>
+ * In this test all file are present in both the working tree and the index.
+ * </p>
+ *
+ * @throws IOException
+ * @throws GitAPIException
+ * @throws NoFilepatternException
+ */
+ @Test
+ public void testHierarchyBothIterator() throws IOException,
+ NoFilepatternException, GitAPIException {
+ writeAttributesFile(".git/info/attributes", "*.global eol=crlf");
+ writeAttributesFile(".gitattributes", "*.local eol=lf");
+ writeAttributesFile("level1/.gitattributes", "*.local text");
+ writeAttributesFile("level1/level2/.gitattributes", "*.local -text");
+
+ writeTrashFile("l0.global", "");
+ writeTrashFile("l0.local", "");
+
+ writeTrashFile("level1/l1.global", "");
+ writeTrashFile("level1/l1.local", "");
+
+ writeTrashFile("level1/level2/l2.global", "");
+ writeTrashFile("level1/level2/l2.local", "");
+
+ beginWalk();
+
+ git.add().addFilepattern(".").call();
+
+ assertEntry(F, ".gitattributes");
+ assertEntry(F, "l0.global", asSet(EOL_CRLF));
+ assertEntry(F, "l0.local", asSet(EOL_LF));
+
+ assertEntry(D, "level1");
+ assertEntry(F, "level1/.gitattributes");
+ assertEntry(F, "level1/l1.global", asSet(EOL_CRLF));
+ assertEntry(F, "level1/l1.local", asSet(EOL_LF, TEXT_SET));
+
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/.gitattributes");
+ assertEntry(F, "level1/level2/l2.global", asSet(EOL_CRLF));
+ assertEntry(F, "level1/level2/l2.local", asSet(EOL_LF, TEXT_UNSET));
+
+ endWalk();
+
+ }
+
+ /**
+ * Checks the precedence on a hierarchy with multiple attributes.
+ * <p>
+ * In this test all file are present only in the working tree.
+ * </p>
+ *
+ * @throws IOException
+ * @throws GitAPIException
+ * @throws NoFilepatternException
+ */
+ @Test
+ public void testHierarchyWorktreeOnly()
+ throws IOException, NoFilepatternException, GitAPIException {
+ writeAttributesFile(".git/info/attributes", "*.global eol=crlf");
+ writeAttributesFile(".gitattributes", "*.local eol=lf");
+ writeAttributesFile("level1/.gitattributes", "*.local text");
+ writeAttributesFile("level1/level2/.gitattributes", "*.local -text");
+
+ writeTrashFile("l0.global", "");
+ writeTrashFile("l0.local", "");
+
+ writeTrashFile("level1/l1.global", "");
+ writeTrashFile("level1/l1.local", "");
+
+ writeTrashFile("level1/level2/l2.global", "");
+ writeTrashFile("level1/level2/l2.local", "");
+
+ beginWalk();
+
+ assertEntry(F, ".gitattributes");
+ assertEntry(F, "l0.global", asSet(EOL_CRLF));
+ assertEntry(F, "l0.local", asSet(EOL_LF));
+
+ assertEntry(D, "level1");
+ assertEntry(F, "level1/.gitattributes");
+ assertEntry(F, "level1/l1.global", asSet(EOL_CRLF));
+ assertEntry(F, "level1/l1.local", asSet(EOL_LF, TEXT_SET));
+
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/.gitattributes");
+ assertEntry(F, "level1/level2/l2.global", asSet(EOL_CRLF));
+ assertEntry(F, "level1/level2/l2.local", asSet(EOL_LF, TEXT_UNSET));
+
+ endWalk();
+
+ }
+
+ /**
+ * Checks that the list of attributes is an aggregation of all the
+ * attributes from the attributes files hierarchy.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testAggregation() throws IOException {
+ writeGlobalAttributeFile("globalAttributesFile", "*.txt -custom2");
+ writeAttributesFile(".git/info/attributes", "*.txt eol=crlf");
+ writeAttributesFile(".gitattributes", "*.txt custom=root");
+ writeAttributesFile("level1/.gitattributes", "*.txt text");
+ writeAttributesFile("level1/level2/.gitattributes", "*.txt -delta");
+
+ writeTrashFile("l0.txt", "");
+
+ writeTrashFile("level1/l1.txt", "");
+
+ writeTrashFile("level1/level2/l2.txt", "");
+
+ beginWalk();
+
+ assertEntry(F, ".gitattributes");
+ assertEntry(F, "l0.txt", asSet(EOL_CRLF, CUSTOM_ROOT, CUSTOM2_UNSET));
+
+ assertEntry(D, "level1");
+ assertEntry(F, "level1/.gitattributes");
+ assertEntry(F, "level1/l1.txt",
+ asSet(EOL_CRLF, CUSTOM_ROOT, TEXT_SET, CUSTOM2_UNSET));
+
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/.gitattributes");
+ assertEntry(
+ F,
+ "level1/level2/l2.txt",
+ asSet(EOL_CRLF, CUSTOM_ROOT, TEXT_SET, DELTA_UNSET,
+ CUSTOM2_UNSET));
+
+ endWalk();
+
+ }
+
+ /**
+ * Checks that the last entry in .gitattributes is used if 2 lines match the
+ * same attribute
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testOverriding() throws IOException {
+ writeAttributesFile(".git/info/attributes",//
+ //
+ "*.txt custom=current",//
+ "*.txt custom=parent",//
+ "*.txt custom=root",//
+ "*.txt custom=info",
+ //
+ "*.txt delta",//
+ "*.txt -delta",
+ //
+ "*.txt eol=lf",//
+ "*.txt eol=crlf",
+ //
+ "*.txt text",//
+ "*.txt -text");
+
+ writeTrashFile("l0.txt", "");
+ beginWalk();
+
+ assertEntry(F, "l0.txt",
+ asSet(TEXT_UNSET, EOL_CRLF, DELTA_UNSET, CUSTOM_INFO));
+
+ endWalk();
+ }
+
+ /**
+ * Checks that the last value of an attribute is used if in the same line an
+ * attribute is defined several time.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testOverriding2() throws IOException {
+ writeAttributesFile(".git/info/attributes",
+ "*.txt custom=current custom=parent custom=root custom=info",//
+ "*.txt delta -delta",//
+ "*.txt eol=lf eol=crlf",//
+ "*.txt text -text");
+ writeTrashFile("l0.txt", "");
+ beginWalk();
+
+ assertEntry(F, "l0.txt",
+ asSet(TEXT_UNSET, EOL_CRLF, DELTA_UNSET, CUSTOM_INFO));
+
+ endWalk();
+ }
+
+ @Test
+ public void testRulesInherited() throws Exception {
+ writeAttributesFile(".gitattributes", "**/*.txt eol=lf");
+ writeTrashFile("src/config/readme.txt", "");
+ writeTrashFile("src/config/windows.file", "");
+
+ beginWalk();
+
+ assertEntry(F, ".gitattributes");
+ assertEntry(D, "src");
+ assertEntry(D, "src/config");
+
+ assertEntry(F, "src/config/readme.txt", asSet(EOL_LF));
+ assertEntry(F, "src/config/windows.file",
+ Collections.<Attribute> emptySet());
+
+ endWalk();
+ }
+
+ private void beginWalk() throws NoWorkTreeException, IOException {
+ walk = new TreeWalk(db);
+ walk.addTree(new FileTreeIterator(db));
+ walk.addTree(new DirCacheIterator(db.readDirCache()));
+
+ ci_walk = new TreeWalk(db);
+ ci_walk.setOperationType(OperationType.CHECKIN_OP);
+ ci_walk.addTree(new FileTreeIterator(db));
+ ci_walk.addTree(new DirCacheIterator(db.readDirCache()));
+ }
+
+ /**
+ * Assert an entry in which checkin and checkout attributes are expected to
+ * be the same.
+ *
+ * @param type
+ * @param pathName
+ * @param forBothOperaiton
+ * @throws IOException
+ */
+ private void assertEntry(FileMode type, String pathName,
+ Set<Attribute> forBothOperaiton) throws IOException {
+ assertEntry(type, pathName, forBothOperaiton, forBothOperaiton);
+ }
+
+ /**
+ * Assert an entry with no attribute expected.
+ *
+ * @param type
+ * @param pathName
+ * @throws IOException
+ */
+ private void assertEntry(FileMode type, String pathName) throws IOException {
+ assertEntry(type, pathName, Collections.<Attribute> emptySet(),
+ Collections.<Attribute> emptySet());
+ }
+
+ /**
+ * Assert that an entry;
+ * <ul>
+ * <li>Has the correct type</li>
+ * <li>Exist in the tree walk</li>
+ * <li>Has the expected attributes on a checkin operation</li>
+ * <li>Has the expected attributes on a checkout operation</li>
+ * </ul>
+ *
+ * @param type
+ * @param pathName
+ * @param checkinAttributes
+ * @param checkoutAttributes
+ * @throws IOException
+ */
+ private void assertEntry(FileMode type, String pathName,
+ Set<Attribute> checkinAttributes, Set<Attribute> checkoutAttributes)
+ throws IOException {
+ assertTrue("walk has entry", walk.next());
+ assertTrue("walk has entry", ci_walk.next());
+ assertEquals(pathName, walk.getPathString());
+ assertEquals(type, walk.getFileMode(0));
+
+ assertEquals(checkinAttributes,
+ asSet(ci_walk.getAttributes().getAll()));
+ assertEquals(checkoutAttributes,
+ asSet(walk.getAttributes().getAll()));
+
+ if (D.equals(type)) {
+ walk.enterSubtree();
+ ci_walk.enterSubtree();
+ }
+ }
+
+ private static Set<Attribute> asSet(Collection<Attribute> attributes) {
+ Set<Attribute> ret = new HashSet<Attribute>();
+ for (Attribute a : attributes) {
+ ret.add(a);
+ }
+ return (ret);
+ }
+
+ private File writeAttributesFile(String name, String... rules)
+ throws IOException {
+ StringBuilder data = new StringBuilder();
+ for (String line : rules)
+ data.append(line + "\n");
+ return writeTrashFile(name, data.toString());
+ }
+
+ /**
+ * Creates an attributes file and set its location in the git configuration.
+ *
+ * @param fileName
+ * @param attributes
+ * @return The attribute file
+ * @throws IOException
+ * @see Repository#getConfig()
+ */
+ private File writeGlobalAttributeFile(String fileName, String... attributes)
+ throws IOException {
+ customAttributeFile = File.createTempFile("tmp_", fileName, null);
+ customAttributeFile.deleteOnExit();
+ StringBuilder attributesFileContent = new StringBuilder();
+ for (String attr : attributes) {
+ attributesFileContent.append(attr).append("\n");
+ }
+ JGitTestUtil.write(customAttributeFile,
+ attributesFileContent.toString());
+ db.getConfig().setString("core", null, "attributesfile",
+ customAttributeFile.getAbsolutePath());
+ return customAttributeFile;
+ }
+
+ static Set<Attribute> asSet(Attribute... attrs) {
+ HashSet<Attribute> result = new HashSet<Attribute>();
+ for (Attribute attr : attrs)
+ result.add(attr);
+ return result;
+ }
+
+ private void endWalk() throws IOException {
+ assertFalse("Not all files tested", walk.next());
+ assertFalse("Not all files tested", ci_walk.next());
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java
index 91498e7..81a4c6a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterTest.java
@@ -98,8 +98,9 @@ public class DiffFormatterTest extends RepositoryTestCase {
@Override
@After
public void tearDown() throws Exception {
- if (df != null)
- df.release();
+ if (df != null) {
+ df.close();
+ }
super.tearDown();
}
@@ -240,8 +241,7 @@ public class DiffFormatterTest extends RepositoryTestCase {
ObjectId bId = blob("b\n");
String diffHeader = makeDiffHeaderModeChange(PATH_A, PATH_A, aId, bId,
- GITLINK, REGULAR_FILE)
- + "-Subproject commit " + aId.name() + "\n";
+ GITLINK, REGULAR_FILE);
DiffEntry ad = DiffEntry.delete(PATH_A, aId);
ad.oldMode = FileMode.GITLINK;
@@ -256,7 +256,7 @@ public class DiffFormatterTest extends RepositoryTestCase {
assertEquals(1, fh.getHunks().size());
HunkHeader hh = fh.getHunks().get(0);
- assertEquals(0, hh.toEditList().size());
+ assertEquals(1, hh.toEditList().size());
}
@Test
@@ -317,30 +317,31 @@ public class DiffFormatterTest extends RepositoryTestCase {
File folder = new File(db.getDirectory().getParent(), "folder");
FileUtils.mkdir(folder);
write(new File(folder, "folder.txt"), "folder");
- Git git = new Git(db);
- git.add().addFilepattern(".").call();
- git.commit().setMessage("Initial commit").call();
- write(new File(folder, "folder.txt"), "folder change");
-
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- DiffFormatter dfmt = new DiffFormatter(new SafeBufferedOutputStream(os));
- dfmt.setRepository(db);
- dfmt.setPathFilter(PathFilter.create("folder"));
- DirCacheIterator oldTree = new DirCacheIterator(db.readDirCache());
- FileTreeIterator newTree = new FileTreeIterator(db);
- dfmt.format(oldTree, newTree);
- dfmt.flush();
-
- String actual = os.toString("UTF-8");
- String expected =
- "diff --git a/folder/folder.txt b/folder/folder.txt\n"
- + "index 0119635..95c4c65 100644\n"
- + "--- a/folder/folder.txt\n" + "+++ b/folder/folder.txt\n"
- + "@@ -1 +1 @@\n" + "-folder\n"
- + "\\ No newline at end of file\n" + "+folder change\n"
- + "\\ No newline at end of file\n";
-
- assertEquals(expected, actual);
+ try (Git git = new Git(db);
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ DiffFormatter dfmt = new DiffFormatter(new SafeBufferedOutputStream(os))) {
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("Initial commit").call();
+ write(new File(folder, "folder.txt"), "folder change");
+ dfmt.setRepository(db);
+ dfmt.setPathFilter(PathFilter.create("folder"));
+ DirCacheIterator oldTree = new DirCacheIterator(db.readDirCache());
+ FileTreeIterator newTree = new FileTreeIterator(db);
+
+ dfmt.format(oldTree, newTree);
+ dfmt.flush();
+
+ String actual = os.toString("UTF-8");
+ String expected =
+ "diff --git a/folder/folder.txt b/folder/folder.txt\n"
+ + "index 0119635..95c4c65 100644\n"
+ + "--- a/folder/folder.txt\n" + "+++ b/folder/folder.txt\n"
+ + "@@ -1 +1 @@\n" + "-folder\n"
+ + "\\ No newline at end of file\n" + "+folder change\n"
+ + "\\ No newline at end of file\n";
+
+ assertEquals(expected, actual);
+ }
}
@Test
@@ -349,29 +350,30 @@ public class DiffFormatterTest extends RepositoryTestCase {
File folder = new File(db.getDirectory().getParent(), "folder");
FileUtils.mkdir(folder);
write(new File(folder, "folder.txt"), "folder");
- Git git = new Git(db);
- git.add().addFilepattern(".").call();
- RevCommit commit = git.commit().setMessage("Initial commit").call();
- write(new File(folder, "folder.txt"), "folder change");
-
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- DiffFormatter dfmt = new DiffFormatter(new SafeBufferedOutputStream(os));
- dfmt.setRepository(db);
- dfmt.setPathFilter(PathFilter.create("folder"));
- dfmt.format(null, commit.getTree().getId());
- dfmt.flush();
-
- String actual = os.toString("UTF-8");
- String expected = "diff --git a/folder/folder.txt b/folder/folder.txt\n"
- + "new file mode 100644\n"
- + "index 0000000..0119635\n"
- + "--- /dev/null\n"
- + "+++ b/folder/folder.txt\n"
- + "@@ -0,0 +1 @@\n"
- + "+folder\n"
- + "\\ No newline at end of file\n";
-
- assertEquals(expected, actual);
+ try (Git git = new Git(db);
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ DiffFormatter dfmt = new DiffFormatter(new SafeBufferedOutputStream(os))) {
+ git.add().addFilepattern(".").call();
+ RevCommit commit = git.commit().setMessage("Initial commit").call();
+ write(new File(folder, "folder.txt"), "folder change");
+
+ dfmt.setRepository(db);
+ dfmt.setPathFilter(PathFilter.create("folder"));
+ dfmt.format(null, commit.getTree().getId());
+ dfmt.flush();
+
+ String actual = os.toString("UTF-8");
+ String expected = "diff --git a/folder/folder.txt b/folder/folder.txt\n"
+ + "new file mode 100644\n"
+ + "index 0000000..0119635\n"
+ + "--- /dev/null\n"
+ + "+++ b/folder/folder.txt\n"
+ + "@@ -0,0 +1 @@\n"
+ + "+folder\n"
+ + "\\ No newline at end of file\n";
+
+ assertEquals(expected, actual);
+ }
}
@Test
@@ -380,43 +382,45 @@ public class DiffFormatterTest extends RepositoryTestCase {
File folder = new File(db.getDirectory().getParent(), "folder");
FileUtils.mkdir(folder);
write(new File(folder, "folder.txt"), "folder");
- Git git = new Git(db);
- git.add().addFilepattern(".").call();
- RevCommit commit = git.commit().setMessage("Initial commit").call();
- write(new File(folder, "folder.txt"), "folder change");
-
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- DiffFormatter dfmt = new DiffFormatter(new SafeBufferedOutputStream(os));
- dfmt.setRepository(db);
- dfmt.setPathFilter(PathFilter.create("folder"));
- dfmt.format(commit.getTree().getId(), null);
- dfmt.flush();
-
- String actual = os.toString("UTF-8");
- String expected = "diff --git a/folder/folder.txt b/folder/folder.txt\n"
- + "deleted file mode 100644\n"
- + "index 0119635..0000000\n"
- + "--- a/folder/folder.txt\n"
- + "+++ /dev/null\n"
- + "@@ -1 +0,0 @@\n"
- + "-folder\n"
- + "\\ No newline at end of file\n";
-
- assertEquals(expected, actual);
+ try (Git git = new Git(db);
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ DiffFormatter dfmt = new DiffFormatter(new SafeBufferedOutputStream(os));) {
+ git.add().addFilepattern(".").call();
+ RevCommit commit = git.commit().setMessage("Initial commit").call();
+ write(new File(folder, "folder.txt"), "folder change");
+
+ dfmt.setRepository(db);
+ dfmt.setPathFilter(PathFilter.create("folder"));
+ dfmt.format(commit.getTree().getId(), null);
+ dfmt.flush();
+
+ String actual = os.toString("UTF-8");
+ String expected = "diff --git a/folder/folder.txt b/folder/folder.txt\n"
+ + "deleted file mode 100644\n"
+ + "index 0119635..0000000\n"
+ + "--- a/folder/folder.txt\n"
+ + "+++ /dev/null\n"
+ + "@@ -1 +0,0 @@\n"
+ + "-folder\n"
+ + "\\ No newline at end of file\n";
+
+ assertEquals(expected, actual);
+ }
}
@Test
public void testDiffNullToNull() throws Exception {
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- DiffFormatter dfmt = new DiffFormatter(new SafeBufferedOutputStream(os));
- dfmt.setRepository(db);
- dfmt.format((AnyObjectId) null, null);
- dfmt.flush();
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream();
+ DiffFormatter dfmt = new DiffFormatter(new SafeBufferedOutputStream(os))) {
+ dfmt.setRepository(db);
+ dfmt.format((AnyObjectId) null, null);
+ dfmt.flush();
- String actual = os.toString("UTF-8");
- String expected = "";
+ String actual = os.toString("UTF-8");
+ String expected = "";
- assertEquals(expected, actual);
+ assertEquals(expected, actual);
+ }
}
private static String makeDiffHeader(String pathA, String pathB,
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java
index e83ef77..4315be9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java
@@ -48,6 +48,7 @@ import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import java.util.Arrays;
import java.util.List;
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
@@ -227,6 +228,19 @@ public class RenameDetectorTest extends RepositoryTestCase {
}
@Test
+ public void testExactRename_UnstagedFile() throws Exception {
+ ObjectId aId = blob("foo");
+ DiffEntry a = DiffEntry.delete(PATH_A, aId);
+ DiffEntry b = DiffEntry.add(PATH_B, aId);
+
+ rd.addAll(Arrays.asList(a, b));
+ List<DiffEntry> entries = rd.compute();
+
+ assertEquals(1, entries.size());
+ assertRename(a, b, 100, entries.get(0));
+ }
+
+ @Test
public void testInexactRename_OnePair() throws Exception {
ObjectId aId = blob("foo\nbar\nbaz\nblarg\n");
ObjectId bId = blob("foo\nbar\nbaz\nblah\n");
@@ -430,6 +444,23 @@ public class RenameDetectorTest extends RepositoryTestCase {
}
@Test
+ public void testNoRenames_UntrackedFile() throws Exception {
+ ObjectId aId = blob("foo");
+ ObjectId bId = ObjectId
+ .fromString("3049eb6eee7e1318f4e78e799bf33f1e54af9cbf");
+
+ DiffEntry a = DiffEntry.delete(PATH_A, aId);
+ DiffEntry b = DiffEntry.add(PATH_B, bId);
+
+ rd.addAll(Arrays.asList(a, b));
+ List<DiffEntry> entries = rd.compute();
+
+ assertEquals(2, entries.size());
+ assertSame(a, entries.get(0));
+ assertSame(b, entries.get(1));
+ }
+
+ @Test
public void testBreakModify_BreakAll() throws Exception {
ObjectId aId = blob("foo");
ObjectId bId = blob("bar");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheEntryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheEntryTest.java
index e159ed9..05fa007 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheEntryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheEntryTest.java
@@ -69,15 +69,17 @@ public class DirCacheEntryTest {
assertFalse(isValidPath("a\u0000b"));
}
- private static boolean isValidPath(final String path) {
+ @SuppressWarnings("unused")
+ private static boolean isValidPath(String path) {
try {
- DirCacheCheckout.checkValidPath(path);
+ new DirCacheEntry(path);
return true;
} catch (InvalidPathException e) {
return false;
}
}
+ @SuppressWarnings("unused")
@Test
public void testCreate_ByStringPath() {
assertEquals("a", new DirCacheEntry("a").getPathString());
@@ -91,6 +93,7 @@ public class DirCacheEntryTest {
}
}
+ @SuppressWarnings("unused")
@Test
public void testCreate_ByStringPathAndStage() {
DirCacheEntry e;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCachePathEditTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCachePathEditTest.java
index 63ec858..c85e156 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCachePathEditTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCachePathEditTest.java
@@ -43,11 +43,13 @@
package org.eclipse.jgit.dircache;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
+import org.eclipse.jgit.errors.DirCacheNameConflictException;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
@@ -154,6 +156,125 @@ public class DirCachePathEditTest {
assertEquals(DirCacheEntry.STAGE_3, entries.get(2).getStage());
}
+ @Test
+ public void testFileReplacesTree() throws Exception {
+ DirCache dc = DirCache.newInCore();
+ DirCacheEditor editor = dc.editor();
+ editor.add(new AddEdit("a"));
+ editor.add(new AddEdit("b/c"));
+ editor.add(new AddEdit("b/d"));
+ editor.add(new AddEdit("e"));
+ editor.finish();
+
+ editor = dc.editor();
+ editor.add(new AddEdit("b"));
+ editor.finish();
+
+ assertEquals(3, dc.getEntryCount());
+ assertEquals("a", dc.getEntry(0).getPathString());
+ assertEquals("b", dc.getEntry(1).getPathString());
+ assertEquals("e", dc.getEntry(2).getPathString());
+
+ dc.clear();
+ editor = dc.editor();
+ editor.add(new AddEdit("A.c"));
+ editor.add(new AddEdit("A/c"));
+ editor.add(new AddEdit("A0c"));
+ editor.finish();
+
+ editor = dc.editor();
+ editor.add(new AddEdit("A"));
+ editor.finish();
+ assertEquals(3, dc.getEntryCount());
+ assertEquals("A", dc.getEntry(0).getPathString());
+ assertEquals("A.c", dc.getEntry(1).getPathString());
+ assertEquals("A0c", dc.getEntry(2).getPathString());
+ }
+
+ @Test
+ public void testTreeReplacesFile() throws Exception {
+ DirCache dc = DirCache.newInCore();
+ DirCacheEditor editor = dc.editor();
+ editor.add(new AddEdit("a"));
+ editor.add(new AddEdit("ab"));
+ editor.add(new AddEdit("b"));
+ editor.add(new AddEdit("e"));
+ editor.finish();
+
+ editor = dc.editor();
+ editor.add(new AddEdit("b/c/d/f"));
+ editor.add(new AddEdit("b/g/h/i"));
+ editor.finish();
+
+ assertEquals(5, dc.getEntryCount());
+ assertEquals("a", dc.getEntry(0).getPathString());
+ assertEquals("ab", dc.getEntry(1).getPathString());
+ assertEquals("b/c/d/f", dc.getEntry(2).getPathString());
+ assertEquals("b/g/h/i", dc.getEntry(3).getPathString());
+ assertEquals("e", dc.getEntry(4).getPathString());
+ }
+
+ @Test
+ public void testDuplicateFiles() throws Exception {
+ DirCache dc = DirCache.newInCore();
+ DirCacheEditor editor = dc.editor();
+ editor.add(new AddEdit("a"));
+ editor.add(new AddEdit("a"));
+
+ try {
+ editor.finish();
+ fail("Expected DirCacheNameConflictException to be thrown");
+ } catch (DirCacheNameConflictException e) {
+ assertEquals("a a", e.getMessage());
+ assertEquals("a", e.getPath1());
+ assertEquals("a", e.getPath2());
+ }
+ }
+
+ @Test
+ public void testFileOverlapsTree() throws Exception {
+ DirCache dc = DirCache.newInCore();
+ DirCacheEditor editor = dc.editor();
+ editor.add(new AddEdit("a"));
+ editor.add(new AddEdit("a/b").setReplace(false));
+ try {
+ editor.finish();
+ fail("Expected DirCacheNameConflictException to be thrown");
+ } catch (DirCacheNameConflictException e) {
+ assertEquals("a a/b", e.getMessage());
+ assertEquals("a", e.getPath1());
+ assertEquals("a/b", e.getPath2());
+ }
+
+ editor = dc.editor();
+ editor.add(new AddEdit("A.c"));
+ editor.add(new AddEdit("A/c").setReplace(false));
+ editor.add(new AddEdit("A0c"));
+ editor.add(new AddEdit("A"));
+ try {
+ editor.finish();
+ fail("Expected DirCacheNameConflictException to be thrown");
+ } catch (DirCacheNameConflictException e) {
+ assertEquals("A A/c", e.getMessage());
+ assertEquals("A", e.getPath1());
+ assertEquals("A/c", e.getPath2());
+ }
+
+ editor = dc.editor();
+ editor.add(new AddEdit("A.c"));
+ editor.add(new AddEdit("A/b/c/d").setReplace(false));
+ editor.add(new AddEdit("A/b/c"));
+ editor.add(new AddEdit("A0c"));
+ try {
+ editor.finish();
+ fail("Expected DirCacheNameConflictException to be thrown");
+ } catch (DirCacheNameConflictException e) {
+ assertEquals("A/b/c A/b/c/d", e.getMessage());
+ assertEquals("A/b/c", e.getPath1());
+ assertEquals("A/b/c/d", e.getPath2());
+ }
+ }
+
private static DirCacheEntry createEntry(String path, int stage) {
DirCacheEntry entry = new DirCacheEntry(path, stage);
entry.setFileMode(FileMode.REGULAR_FILE);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/ManifestParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/ManifestParserTest.java
new file mode 100644
index 0000000..5ed4268
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/ManifestParserTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.gitrepo;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.StringBufferInputStream;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.Test;
+
+public class ManifestParserTest {
+
+ @Test
+ public void testManifestParser() throws Exception {
+ String baseUrl = "https://git.google.com/";
+ StringBuilder xmlContent = new StringBuilder();
+ Set<String> results = new HashSet<String>();
+ xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ .append("<manifest>")
+ .append("<remote name=\"remote1\" fetch=\".\" />")
+ .append("<default revision=\"master\" remote=\"remote1\" />")
+ .append("<project path=\"foo\" name=\"")
+ .append("foo")
+ .append("\" groups=\"a,test\" />")
+ .append("<project path=\"bar\" name=\"")
+ .append("bar")
+ .append("\" groups=\"notdefault\" />")
+ .append("<project path=\"foo/a\" name=\"")
+ .append("a")
+ .append("\" groups=\"a\" />")
+ .append("<project path=\"b\" name=\"")
+ .append("b")
+ .append("\" groups=\"b\" />")
+ .append("</manifest>");
+
+ ManifestParser parser = new ManifestParser(
+ null, null, "master", baseUrl, null, null);
+ parser.read(new StringBufferInputStream(xmlContent.toString()));
+ // Unfiltered projects should have them all.
+ results.clear();
+ results.add("foo");
+ results.add("bar");
+ results.add("foo/a");
+ results.add("b");
+ for (RepoProject proj : parser.getProjects()) {
+ String msg = String.format(
+ "project \"%s\" should be included in unfiltered projects",
+ proj.getPath());
+ assertTrue(msg, results.contains(proj.getPath()));
+ results.remove(proj.getPath());
+ }
+ assertTrue(
+ "Unfiltered projects shouldn't contain any unexpected results",
+ results.isEmpty());
+ // Filtered projects should have foo & b
+ results.clear();
+ results.add("foo");
+ results.add("b");
+ for (RepoProject proj : parser.getFilteredProjects()) {
+ String msg = String.format(
+ "project \"%s\" should be included in filtered projects",
+ proj.getPath());
+ assertTrue(msg, results.contains(proj.getPath()));
+ results.remove(proj.getPath());
+ }
+ assertTrue(
+ "Filtered projects shouldn't contain any unexpected results",
+ results.isEmpty());
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
index 3d86cfd..524d0b8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
@@ -56,6 +56,8 @@ import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
import org.junit.Test;
public class RepoCommandTest extends RepositoryTestCase {
@@ -241,9 +243,9 @@ public class RepoCommandTest extends RepositoryTestCase {
@Test
public void testBareRepo() throws Exception {
- Repository remoteDb = createBareRepository();
- Repository tempDb = createWorkRepository();
- try {
+ try (
+ Repository remoteDb = createBareRepository();
+ Repository tempDb = createWorkRepository()) {
StringBuilder xmlContent = new StringBuilder();
xmlContent
.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
@@ -280,9 +282,6 @@ public class RepoCommandTest extends RepositoryTestCase {
String remote = defaultDb.resolve(Constants.HEAD).name();
assertEquals("The gitlink should be the same as remote head",
remote, gitlink);
- } finally {
- tempDb.close();
- remoteDb.close();
}
}
@@ -366,9 +365,9 @@ public class RepoCommandTest extends RepositoryTestCase {
@Test
public void testRevisionBare() throws Exception {
- Repository remoteDb = createBareRepository();
- Repository tempDb = createWorkRepository();
- try {
+ try (
+ Repository remoteDb = createBareRepository();
+ Repository tempDb = createWorkRepository()) {
StringBuilder xmlContent = new StringBuilder();
xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
.append("<manifest>")
@@ -393,17 +392,14 @@ public class RepoCommandTest extends RepositoryTestCase {
localDb.close();
assertEquals("The gitlink is same as remote head",
oldCommitId.name(), gitlink);
- } finally {
- tempDb.close();
- remoteDb.close();
}
}
@Test
public void testCopyFileBare() throws Exception {
- Repository remoteDb = createBareRepository();
- Repository tempDb = createWorkRepository();
- try {
+ try (
+ Repository remoteDb = createBareRepository();
+ Repository tempDb = createWorkRepository()) {
StringBuilder xmlContent = new StringBuilder();
xmlContent
.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
@@ -413,6 +409,7 @@ public class RepoCommandTest extends RepositoryTestCase {
.append("<project path=\"foo\" name=\"").append(defaultUri)
.append("\" revision=\"").append(BRANCH).append("\" >")
.append("<copyfile src=\"hello.txt\" dest=\"Hello\" />")
+ .append("<copyfile src=\"hello.txt\" dest=\"foo/Hello\" />")
.append("</project>").append("</manifest>");
JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
xmlContent.toString());
@@ -427,25 +424,26 @@ public class RepoCommandTest extends RepositoryTestCase {
.getRepository();
// The Hello file should exist
File hello = new File(localDb.getWorkTree(), "Hello");
- localDb.close();
assertTrue("The Hello file should exist", hello.exists());
+ // The foo/Hello file should be skipped.
+ File foohello = new File(localDb.getWorkTree(), "foo/Hello");
+ assertFalse(
+ "The foo/Hello file should be skipped", foohello.exists());
+ localDb.close();
// The content of Hello file should be expected
BufferedReader reader = new BufferedReader(new FileReader(hello));
String content = reader.readLine();
reader.close();
assertEquals("The Hello file should have expected content",
"branch world", content);
- } finally {
- tempDb.close();
- remoteDb.close();
}
}
@Test
public void testReplaceManifestBare() throws Exception {
- Repository remoteDb = createBareRepository();
- Repository tempDb = createWorkRepository();
- try {
+ try (
+ Repository remoteDb = createBareRepository();
+ Repository tempDb = createWorkRepository()) {
StringBuilder xmlContent = new StringBuilder();
xmlContent
.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
@@ -512,17 +510,14 @@ public class RepoCommandTest extends RepositoryTestCase {
reader.close();
assertTrue("The bar submodule should exist", bar);
assertFalse("The foo submodule shouldn't exist", foo);
- } finally {
- tempDb.close();
- remoteDb.close();
}
}
@Test
public void testRemoveOverlappingBare() throws Exception {
- Repository remoteDb = createBareRepository();
- Repository tempDb = createWorkRepository();
- try {
+ try (
+ Repository remoteDb = createBareRepository();
+ Repository tempDb = createWorkRepository()) {
StringBuilder xmlContent = new StringBuilder();
xmlContent
.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
@@ -571,9 +566,6 @@ public class RepoCommandTest extends RepositoryTestCase {
assertTrue("The foo submodule should exist", foo);
assertFalse("The foo/bar submodule shouldn't exist", foobar);
assertTrue("The a submodule should exist", a);
- } finally {
- tempDb.close();
- remoteDb.close();
}
}
@@ -671,6 +663,90 @@ public class RepoCommandTest extends RepositoryTestCase {
assertTrue("We should have foo", file.exists());
}
+ @Test
+ public void testTargetBranch() throws Exception {
+ try (
+ Repository remoteDb1 = createBareRepository();
+ Repository remoteDb2 = createBareRepository();
+ Repository tempDb = createWorkRepository()) {
+ StringBuilder xmlContent = new StringBuilder();
+ xmlContent
+ .append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ .append("<manifest>")
+ .append("<remote name=\"remote1\" fetch=\".\" />")
+ .append("<default revision=\"master\" remote=\"remote1\" />")
+ .append("<project path=\"foo\" name=\"").append(defaultUri)
+ .append("\" />").append("</manifest>");
+ JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
+ xmlContent.toString());
+ RepoCommand command = new RepoCommand(remoteDb1);
+ command
+ .setPath(tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+ .setURI(rootUri)
+ .setTargetBranch("test")
+ .call();
+ ObjectId branchId = remoteDb1.resolve(
+ Constants.R_HEADS + "test^{tree}");
+ command = new RepoCommand(remoteDb2);
+ command
+ .setPath(tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+ .setURI(rootUri)
+ .call();
+ ObjectId defaultId = remoteDb2.resolve(Constants.HEAD + "^{tree}");
+ assertEquals(
+ "The tree id of branch db and default db should be the same",
+ branchId, defaultId);
+ }
+ }
+
+ @Test
+ public void testRecordRemoteBranch() throws Exception {
+ try (
+ Repository remoteDb = createBareRepository();
+ Repository tempDb = createWorkRepository()) {
+ StringBuilder xmlContent = new StringBuilder();
+ xmlContent
+ .append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+ .append("<manifest>")
+ .append("<remote name=\"remote1\" fetch=\".\" />")
+ .append("<default revision=\"master\" remote=\"remote1\" />")
+ .append("<project path=\"with-branch\" ")
+ .append("revision=\"master\" ")
+ .append("name=\"").append(notDefaultUri).append("\" />")
+ .append("<project path=\"with-long-branch\" ")
+ .append("revision=\"refs/heads/master\" ")
+ .append("name=\"").append(defaultUri).append("\" />")
+ .append("</manifest>");
+ JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
+ xmlContent.toString());
+
+ RepoCommand command = new RepoCommand(remoteDb);
+ command.setPath(tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
+ .setURI(rootUri)
+ .setRecordRemoteBranch(true)
+ .call();
+ // Clone it
+ File directory = createTempDirectory("testBareRepo");
+ try (Repository localDb = Git.cloneRepository()
+ .setDirectory(directory)
+ .setURI(remoteDb.getDirectory().toURI().toString()).call()
+ .getRepository();) {
+ // The .gitmodules file should exist
+ File gitmodules = new File(localDb.getWorkTree(),
+ ".gitmodules");
+ assertTrue("The .gitmodules file should exist",
+ gitmodules.exists());
+ FileBasedConfig c = new FileBasedConfig(gitmodules,
+ FS.DETECTED);
+ c.load();
+ assertEquals("standard branches work", "master",
+ c.getString("submodule", "with-branch", "branch"));
+ assertEquals("long branches work", "refs/heads/master",
+ c.getString("submodule", "with-long-branch", "branch"));
+ }
+ }
+ }
+
private void resolveRelativeUris() {
// Find the longest common prefix ends with "/" as rootUri.
defaultUri = defaultDb.getDirectory().toURI().toString();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java
index 007bdcc..480e326 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java
@@ -47,33 +47,16 @@ import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
-
-import java.util.Arrays;
import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameter;
-import org.junit.runners.Parameterized.Parameters;
- at SuppressWarnings("deprecation")
- at RunWith(Parameterized.class)
public class FastIgnoreRuleTest {
- @Parameters(name = "OldRule? {0}")
- public static Iterable<Boolean[]> data() {
- return Arrays.asList(new Boolean[][] { { Boolean.FALSE },
- { Boolean.TRUE } });
- }
-
- @Parameter
- public Boolean useOldRule;
-
@Test
public void testSimpleCharClass() {
+ assertMatched("][a]", "]a");
assertMatched("[a]", "a");
+ assertMatched("][a]", "]a");
assertMatched("[a]", "a/");
assertMatched("[a]", "a/b");
@@ -385,58 +368,46 @@ public class FastIgnoreRuleTest {
assertNotMatched("a/b/", "c/a/b/c");
}
- @SuppressWarnings("boxing")
@Test
public void testWildmatch() {
- if (useOldRule)
- System.err
- .println("IgnoreRule can't understand wildmatch rules, skipping testWildmatch!");
-
- Boolean assume = useOldRule;
- assertMatched("**/a/b", "a/b", assume);
- assertMatched("**/a/b", "c/a/b", assume);
- assertMatched("**/a/b", "c/d/a/b", assume);
- assertMatched("**/**/a/b", "c/d/a/b", assume);
-
- assertMatched("/**/a/b", "a/b", assume);
- assertMatched("/**/a/b", "c/a/b", assume);
- assertMatched("/**/a/b", "c/d/a/b", assume);
- assertMatched("/**/**/a/b", "c/d/a/b", assume);
-
- assertMatched("a/b/**", "a/b", assume);
- assertMatched("a/b/**", "a/b/c", assume);
- assertMatched("a/b/**", "a/b/c/d/", assume);
- assertMatched("a/b/**/**", "a/b/c/d", assume);
-
- assertMatched("**/a/**/b", "c/d/a/b", assume);
- assertMatched("**/a/**/b", "c/d/a/e/b", assume);
- assertMatched("**/**/a/**/**/b", "c/d/a/e/b", assume);
-
- assertMatched("/**/a/**/b", "c/d/a/b", assume);
- assertMatched("/**/a/**/b", "c/d/a/e/b", assume);
- assertMatched("/**/**/a/**/**/b", "c/d/a/e/b", assume);
-
- assertMatched("a/**/b", "a/b", assume);
- assertMatched("a/**/b", "a/c/b", assume);
- assertMatched("a/**/b", "a/c/d/b", assume);
- assertMatched("a/**/**/b", "a/c/d/b", assume);
-
- assertMatched("a/**/b/**/c", "a/c/b/d/c", assume);
- assertMatched("a/**/**/b/**/**/c", "a/c/b/d/c", assume);
+ assertMatched("**/a/b", "a/b");
+ assertMatched("**/a/b", "c/a/b");
+ assertMatched("**/a/b", "c/d/a/b");
+ assertMatched("**/**/a/b", "c/d/a/b");
+
+ assertMatched("/**/a/b", "a/b");
+ assertMatched("/**/a/b", "c/a/b");
+ assertMatched("/**/a/b", "c/d/a/b");
+ assertMatched("/**/**/a/b", "c/d/a/b");
+
+ assertMatched("a/b/**", "a/b");
+ assertMatched("a/b/**", "a/b/c");
+ assertMatched("a/b/**", "a/b/c/d/");
+ assertMatched("a/b/**/**", "a/b/c/d");
+
+ assertMatched("**/a/**/b", "c/d/a/b");
+ assertMatched("**/a/**/b", "c/d/a/e/b");
+ assertMatched("**/**/a/**/**/b", "c/d/a/e/b");
+
+ assertMatched("/**/a/**/b", "c/d/a/b");
+ assertMatched("/**/a/**/b", "c/d/a/e/b");
+ assertMatched("/**/**/a/**/**/b", "c/d/a/e/b");
+
+ assertMatched("a/**/b", "a/b");
+ assertMatched("a/**/b", "a/c/b");
+ assertMatched("a/**/b", "a/c/d/b");
+ assertMatched("a/**/**/b", "a/c/d/b");
+
+ assertMatched("a/**/b/**/c", "a/c/b/d/c");
+ assertMatched("a/**/**/b/**/**/c", "a/c/b/d/c");
}
- @SuppressWarnings("boxing")
@Test
public void testWildmatchDoNotMatch() {
- if (useOldRule)
- System.err
- .println("IgnoreRule can't understand wildmatch rules, skipping testWildmatchDoNotMatch!");
-
- Boolean assume = useOldRule;
- assertNotMatched("**/a/b", "a/c/b", assume);
- assertNotMatched("!/**/*.zip", "c/a/b.zip", assume);
- assertNotMatched("!**/*.zip", "c/a/b.zip", assume);
- assertNotMatched("a/**/b", "a/c/bb", assume);
+ assertNotMatched("**/a/b", "a/c/b");
+ assertNotMatched("!/**/*.zip", "c/a/b.zip");
+ assertNotMatched("!**/*.zip", "c/a/b.zip");
+ assertNotMatched("a/**/b", "a/c/bb");
}
@SuppressWarnings("unused")
@@ -478,55 +449,43 @@ public class FastIgnoreRuleTest {
split("/a/b/c/", '/').toArray());
}
- public void assertMatched(String pattern, String path, Boolean... assume) {
+ public void assertMatched(String pattern, String path) {
boolean match = match(pattern, path);
String result = path + " is " + (match ? "ignored" : "not ignored")
+ " via '" + pattern + "' rule";
- if (!match)
+ if (!match) {
System.err.println(result);
- if (assume.length == 0 || !assume[0].booleanValue())
- assertTrue("Expected a match for: " + pattern + " with: " + path,
- match);
- else
- assumeTrue("Expected a match for: " + pattern + " with: " + path,
+ }
+ assertTrue("Expected a match for: " + pattern + " with: " + path,
match);
- if (pattern.startsWith("!"))
+ if (pattern.startsWith("!")) {
pattern = pattern.substring(1);
- else
+ } else {
pattern = "!" + pattern;
+ }
match = match(pattern, path);
- if (assume.length == 0 || !assume[0].booleanValue())
- assertFalse("Expected no match for: " + pattern + " with: " + path,
- match);
- else
- assumeFalse("Expected no match for: " + pattern + " with: " + path,
- match);
+ assertFalse("Expected no match for: " + pattern + " with: " + path,
+ match);
}
- public void assertNotMatched(String pattern, String path, Boolean... assume) {
+ public void assertNotMatched(String pattern, String path) {
boolean match = match(pattern, path);
String result = path + " is " + (match ? "ignored" : "not ignored")
+ " via '" + pattern + "' rule";
- if (match)
+ if (match) {
System.err.println(result);
- if (assume.length == 0 || !assume[0].booleanValue())
- assertFalse("Expected no match for: " + pattern + " with: " + path,
- match);
- else
- assumeFalse("Expected no match for: " + pattern + " with: " + path,
+ }
+ assertFalse("Expected no match for: " + pattern + " with: " + path,
match);
- if (pattern.startsWith("!"))
+ if (pattern.startsWith("!")) {
pattern = pattern.substring(1);
- else
+ } else {
pattern = "!" + pattern;
+ }
match = match(pattern, path);
- if (assume.length == 0 || !assume[0].booleanValue())
- assertTrue("Expected a match for: " + pattern + " with: " + path,
- match);
- else
- assumeTrue("Expected a match for: " + pattern + " with: " + path,
+ assertTrue("Expected a match for: " + pattern + " with: " + path,
match);
}
@@ -542,16 +501,6 @@ public class FastIgnoreRuleTest {
*/
private boolean match(String pattern, String target) {
boolean isDirectory = target.endsWith("/");
- if (useOldRule.booleanValue()) {
- IgnoreRule r = new IgnoreRule(pattern);
- // If speed of this test is ever an issue, we can use a presetRule
- // field
- // to avoid recompiling a pattern each time.
- boolean match = r.isMatch(target, isDirectory);
- if (r.getNegation())
- match = !match;
- return match;
- }
FastIgnoreRule r = new FastIgnoreRule(pattern);
// If speed of this test is ever an issue, we can use a presetRule field
// to avoid recompiling a pattern each time.
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherParametrizedTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherParametrizedTest.java
index 699542c..529c75f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherParametrizedTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherParametrizedTest.java
@@ -47,31 +47,14 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
-import java.util.Arrays;
-
import org.eclipse.jgit.junit.Assert;
import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameter;
-import org.junit.runners.Parameterized.Parameters;
/**
* Tests ignore pattern matches
*/
- at SuppressWarnings("deprecation")
- at RunWith(Parameterized.class)
public class IgnoreMatcherParametrizedTest {
- @Parameters(name = "OldRule? {0}")
- public static Iterable<Boolean[]> data() {
- return Arrays.asList(new Boolean[][] { { Boolean.FALSE },
- { Boolean.TRUE } });
- }
-
- @Parameter
- public Boolean useOldRule;
-
@Test
public void testBasic() {
String pattern = "/test.stp";
@@ -252,47 +235,38 @@ public class IgnoreMatcherParametrizedTest {
@Test
public void testDirModeAndRegex1() {
- // IgnoreRule was buggy for some cases below, therefore using "Assume"
- Boolean assume = useOldRule;
-
String pattern = "a/*/src/";
assertMatched(pattern, "a/b/src/");
assertMatched(pattern, "a/b/src/new");
assertMatched(pattern, "a/b/src/new/a.c");
assertMatched(pattern, "a/b/src/a.c");
// no match as a "file" pattern, because rule is for directories only
- assertNotMatched(pattern, "a/b/src", assume);
+ assertNotMatched(pattern, "a/b/src");
assertNotMatched(pattern, "a/b/srcA/");
}
@Test
public void testDirModeAndRegex2() {
- // IgnoreRule was buggy for some cases below, therefore using "Assume"
- Boolean assume = useOldRule;
-
String pattern = "a/[a-b]/src/";
assertMatched(pattern, "a/b/src/");
assertMatched(pattern, "a/b/src/new");
assertMatched(pattern, "a/b/src/new/a.c");
assertMatched(pattern, "a/b/src/a.c");
// no match as a "file" pattern, because rule is for directories only
- assertNotMatched(pattern, "a/b/src", assume);
+ assertNotMatched(pattern, "a/b/src");
assertNotMatched(pattern, "a/b/srcA/");
}
@Test
public void testDirModeAndRegex3() {
- // IgnoreRule was buggy for some cases below, therefore using "Assume"
- Boolean assume = useOldRule;
-
String pattern = "**/src/";
- assertMatched(pattern, "a/b/src/", assume);
- assertMatched(pattern, "a/b/src/new", assume);
- assertMatched(pattern, "a/b/src/new/a.c", assume);
- assertMatched(pattern, "a/b/src/a.c", assume);
+ assertMatched(pattern, "a/b/src/");
+ assertMatched(pattern, "a/b/src/new");
+ assertMatched(pattern, "a/b/src/new/a.c");
+ assertMatched(pattern, "a/b/src/a.c");
// no match as a "file" pattern, because rule is for directories only
- assertNotMatched(pattern, "a/b/src", assume);
- assertNotMatched(pattern, "a/b/srcA/", assume);
+ assertNotMatched(pattern, "a/b/src");
+ assertNotMatched(pattern, "a/b/srcA/");
}
@Test
@@ -416,13 +390,8 @@ public class IgnoreMatcherParametrizedTest {
private boolean match(String pattern, String target) {
boolean isDirectory = target.endsWith("/");
boolean match;
- if (useOldRule.booleanValue()) {
- IgnoreRule r = new IgnoreRule(pattern);
- match = r.isMatch(target, isDirectory);
- } else {
- FastIgnoreRule r = new FastIgnoreRule(pattern);
- match = r.isMatch(target, isDirectory);
- }
+ FastIgnoreRule r = new FastIgnoreRule(pattern);
+ match = r.isMatch(target, isDirectory);
if (isDirectory) {
boolean noTrailingSlash = matchAsDir(pattern,
@@ -447,10 +416,6 @@ public class IgnoreMatcherParametrizedTest {
*/
private boolean matchAsDir(String pattern, String target) {
assertFalse(target.endsWith("/"));
- if (useOldRule.booleanValue()) {
- IgnoreRule r = new IgnoreRule(pattern);
- return r.isMatch(target, true);
- }
FastIgnoreRule r = new FastIgnoreRule(pattern);
return r.isMatch(target, true);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherTest.java
deleted file mode 100644
index 0713b1a..0000000
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherTest.java
+++ /dev/null
@@ -1,400 +0,0 @@
-/*
- * Copyright (C) 2010, Red Hat Inc.
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package org.eclipse.jgit.ignore;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import org.junit.Test;
-
-
-/**
- * Tests ignore pattern matches
- */
- at SuppressWarnings("deprecation")
-public class IgnoreMatcherTest {
-
- @Test
- public void testBasic() {
- String pattern = "/test.stp";
- assertMatched(pattern, "/test.stp");
-
- pattern = "#/test.stp";
- assertNotMatched(pattern, "/test.stp");
- }
-
- @Test
- public void testFileNameWildcards() {
- //Test basic * and ? for any pattern + any character
- String pattern = "*.st?";
- assertMatched(pattern, "/test.stp");
- assertMatched(pattern, "/anothertest.stg");
- assertMatched(pattern, "/anothertest.st0");
- assertNotMatched(pattern, "/anothertest.sta1");
- //Check that asterisk does not expand to "/"
- assertNotMatched(pattern, "/another/test.sta1");
-
- //Same as above, with a leading slash to ensure that doesn't cause problems
- pattern = "/*.st?";
- assertMatched(pattern, "/test.stp");
- assertMatched(pattern, "/anothertest.stg");
- assertMatched(pattern, "/anothertest.st0");
- assertNotMatched(pattern, "/anothertest.sta1");
- //Check that asterisk does not expand to "/"
- assertNotMatched(pattern, "/another/test.sta1");
-
- //Test for numbers
- pattern = "*.sta[0-5]";
- assertMatched(pattern, "/test.sta5");
- assertMatched(pattern, "/test.sta4");
- assertMatched(pattern, "/test.sta3");
- assertMatched(pattern, "/test.sta2");
- assertMatched(pattern, "/test.sta1");
- assertMatched(pattern, "/test.sta0");
- assertMatched(pattern, "/anothertest.sta2");
- assertNotMatched(pattern, "test.stag");
- assertNotMatched(pattern, "test.sta6");
-
- //Test for letters
- pattern = "/[tv]est.sta[a-d]";
- assertMatched(pattern, "/test.staa");
- assertMatched(pattern, "/test.stab");
- assertMatched(pattern, "/test.stac");
- assertMatched(pattern, "/test.stad");
- assertMatched(pattern, "/vest.stac");
- assertNotMatched(pattern, "test.stae");
- assertNotMatched(pattern, "test.sta9");
-
- //Test child directory/file is matched
- pattern = "/src/ne?";
- assertMatched(pattern, "/src/new/");
- assertMatched(pattern, "/src/new");
- assertMatched(pattern, "/src/new/a.c");
- assertMatched(pattern, "/src/new/a/a.c");
- assertNotMatched(pattern, "/src/new.c");
-
- //Test name-only fnmatcher matches
- pattern = "ne?";
- assertMatched(pattern, "/src/new/");
- assertMatched(pattern, "/src/new");
- assertMatched(pattern, "/src/new/a.c");
- assertMatched(pattern, "/src/new/a/a.c");
- assertMatched(pattern, "/neb");
- assertNotMatched(pattern, "/src/new.c");
- }
-
- @Test
- public void testTargetWithoutLeadingSlash() {
- //Test basic * and ? for any pattern + any character
- String pattern = "/*.st?";
- assertMatched(pattern, "test.stp");
- assertMatched(pattern, "anothertest.stg");
- assertMatched(pattern, "anothertest.st0");
- assertNotMatched(pattern, "anothertest.sta1");
- //Check that asterisk does not expand to ""
- assertNotMatched(pattern, "another/test.sta1");
-
- //Same as above, with a leading slash to ensure that doesn't cause problems
- pattern = "/*.st?";
- assertMatched(pattern, "test.stp");
- assertMatched(pattern, "anothertest.stg");
- assertMatched(pattern, "anothertest.st0");
- assertNotMatched(pattern, "anothertest.sta1");
- //Check that asterisk does not expand to ""
- assertNotMatched(pattern, "another/test.sta1");
-
- //Test for numbers
- pattern = "/*.sta[0-5]";
- assertMatched(pattern, "test.sta5");
- assertMatched(pattern, "test.sta4");
- assertMatched(pattern, "test.sta3");
- assertMatched(pattern, "test.sta2");
- assertMatched(pattern, "test.sta1");
- assertMatched(pattern, "test.sta0");
- assertMatched(pattern, "anothertest.sta2");
- assertNotMatched(pattern, "test.stag");
- assertNotMatched(pattern, "test.sta6");
-
- //Test for letters
- pattern = "/[tv]est.sta[a-d]";
- assertMatched(pattern, "test.staa");
- assertMatched(pattern, "test.stab");
- assertMatched(pattern, "test.stac");
- assertMatched(pattern, "test.stad");
- assertMatched(pattern, "vest.stac");
- assertNotMatched(pattern, "test.stae");
- assertNotMatched(pattern, "test.sta9");
-
- //Test child directory/file is matched
- pattern = "/src/ne?";
- assertMatched(pattern, "src/new/");
- assertMatched(pattern, "src/new");
- assertMatched(pattern, "src/new/a.c");
- assertMatched(pattern, "src/new/a/a.c");
- assertNotMatched(pattern, "src/new.c");
-
- //Test name-only fnmatcher matches
- pattern = "ne?";
- assertMatched(pattern, "src/new/");
- assertMatched(pattern, "src/new");
- assertMatched(pattern, "src/new/a.c");
- assertMatched(pattern, "src/new/a/a.c");
- assertMatched(pattern, "neb");
- assertNotMatched(pattern, "src/new.c");
- }
-
- @Test
- public void testParentDirectoryGitIgnores() {
- //Contains git ignore patterns such as might be seen in a parent directory
-
- //Test for wildcards
- String pattern = "/*/*.c";
- assertMatched(pattern, "/file/a.c");
- assertMatched(pattern, "/src/a.c");
- assertNotMatched(pattern, "/src/new/a.c");
-
- //Test child directory/file is matched
- pattern = "/src/new";
- assertMatched(pattern, "/src/new/");
- assertMatched(pattern, "/src/new");
- assertMatched(pattern, "/src/new/a.c");
- assertMatched(pattern, "/src/new/a/a.c");
- assertNotMatched(pattern, "/src/new.c");
-
- //Test child directory is matched, slash after name
- pattern = "/src/new/";
- assertMatched(pattern, "/src/new/");
- assertMatched(pattern, "/src/new/a.c");
- assertMatched(pattern, "/src/new/a/a.c");
- assertNotMatched(pattern, "/src/new");
- assertNotMatched(pattern, "/src/new.c");
-
- //Test directory is matched by name only
- pattern = "b1";
- assertMatched(pattern, "/src/new/a/b1/a.c");
- assertNotMatched(pattern, "/src/new/a/b2/file.c");
- assertNotMatched(pattern, "/src/new/a/bb1/file.c");
- assertNotMatched(pattern, "/src/new/a/file.c");
- }
-
- @Test
- public void testTrailingSlash() {
- String pattern = "/src/";
- assertMatched(pattern, "/src/");
- assertMatched(pattern, "/src/new");
- assertMatched(pattern, "/src/new/a.c");
- assertMatched(pattern, "/src/a.c");
- assertNotMatched(pattern, "/src");
- assertNotMatched(pattern, "/srcA/");
- }
-
- @Test
- public void testNameOnlyMatches() {
- /*
- * Name-only matches do not contain any path separators
- */
- //Test matches for file extension
- String pattern = "*.stp";
- assertMatched(pattern, "/test.stp");
- assertMatched(pattern, "/src/test.stp");
- assertNotMatched(pattern, "/test.stp1");
- assertNotMatched(pattern, "/test.astp");
-
- //Test matches for name-only, applies to file name or folder name
- pattern = "src";
- assertMatched(pattern, "/src");
- assertMatched(pattern, "/src/");
- assertMatched(pattern, "/src/a.c");
- assertMatched(pattern, "/src/new/a.c");
- assertMatched(pattern, "/new/src/a.c");
- assertMatched(pattern, "/file/src");
-
- //Test matches for name-only, applies only to folder names
- pattern = "src/";
- assertMatched(pattern, "/src/");
- assertMatched(pattern, "/src/a.c");
- assertMatched(pattern, "/src/new/a.c");
- assertMatched(pattern, "/new/src/a.c");
- assertNotMatched(pattern, "/src");
- assertNotMatched(pattern, "/file/src");
-
- //Test matches for name-only, applies to file name or folder name
- //With a small wildcard
- pattern = "?rc";
- assertMatched(pattern, "/src/a.c");
- assertMatched(pattern, "/src/new/a.c");
- assertMatched(pattern, "/new/src/a.c");
- assertMatched(pattern, "/file/src");
- assertMatched(pattern, "/src/");
-
- //Test matches for name-only, applies to file name or folder name
- //With a small wildcard
- pattern = "?r[a-c]";
- assertMatched(pattern, "/src/a.c");
- assertMatched(pattern, "/src/new/a.c");
- assertMatched(pattern, "/new/src/a.c");
- assertMatched(pattern, "/file/src");
- assertMatched(pattern, "/src/");
- assertMatched(pattern, "/srb/a.c");
- assertMatched(pattern, "/grb/new/a.c");
- assertMatched(pattern, "/new/crb/a.c");
- assertMatched(pattern, "/file/3rb");
- assertMatched(pattern, "/xrb/");
- assertMatched(pattern, "/3ra/a.c");
- assertMatched(pattern, "/5ra/new/a.c");
- assertMatched(pattern, "/new/1ra/a.c");
- assertMatched(pattern, "/file/dra");
- assertMatched(pattern, "/era/");
- assertNotMatched(pattern, "/crg");
- assertNotMatched(pattern, "/cr3");
- }
-
- @Test
- public void testNegation() {
- String pattern = "!/test.stp";
- assertMatched(pattern, "/test.stp");
- }
-
- @Test
- public void testGetters() {
- IgnoreRule r = new IgnoreRule("/pattern/");
- assertFalse(r.getNameOnly());
- assertTrue(r.dirOnly());
- assertFalse(r.getNegation());
- assertEquals(r.getPattern(), "/pattern");
-
- r = new IgnoreRule("/patter?/");
- assertFalse(r.getNameOnly());
- assertTrue(r.dirOnly());
- assertFalse(r.getNegation());
- assertEquals(r.getPattern(), "/patter?");
-
- r = new IgnoreRule("patt*");
- assertTrue(r.getNameOnly());
- assertFalse(r.dirOnly());
- assertFalse(r.getNegation());
- assertEquals(r.getPattern(), "patt*");
-
- r = new IgnoreRule("pattern");
- assertTrue(r.getNameOnly());
- assertFalse(r.dirOnly());
- assertFalse(r.getNegation());
- assertEquals(r.getPattern(), "pattern");
-
- r = new IgnoreRule("!pattern");
- assertTrue(r.getNameOnly());
- assertFalse(r.dirOnly());
- assertTrue(r.getNegation());
- assertEquals(r.getPattern(), "pattern");
-
- r = new IgnoreRule("!/pattern");
- assertFalse(r.getNameOnly());
- assertFalse(r.dirOnly());
- assertTrue(r.getNegation());
- assertEquals(r.getPattern(), "/pattern");
-
- r = new IgnoreRule("!/patter?");
- assertFalse(r.getNameOnly());
- assertFalse(r.dirOnly());
- assertTrue(r.getNegation());
- assertEquals(r.getPattern(), "/patter?");
- }
-
- @Test
- public void testResetState() {
- String pattern = "/build/*";
- String target = "/build";
- // Don't use the assert methods of this class, as we want to test
- // whether the state in IgnoreRule is reset properly
- IgnoreRule r = new IgnoreRule(pattern);
- // Result should be the same for the same inputs
- assertFalse(r.isMatch(target, true));
- assertFalse(r.isMatch(target, true));
- }
-
- /**
- * Check for a match. If target ends with "/", match will assume that the
- * target is meant to be a directory.
- * @param pattern
- * Pattern as it would appear in a .gitignore file
- * @param target
- * Target file path relative to repository's GIT_DIR
- */
- public void assertMatched(String pattern, String target) {
- boolean value = match(pattern, target);
- assertTrue("Expected a match for: " + pattern + " with: " + target,
- value);
- }
-
- /**
- * Check for a match. If target ends with "/", match will assume that the
- * target is meant to be a directory.
- * @param pattern
- * Pattern as it would appear in a .gitignore file
- * @param target
- * Target file path relative to repository's GIT_DIR
- */
- public void assertNotMatched(String pattern, String target) {
- boolean value = match(pattern, target);
- assertFalse("Expected no match for: " + pattern + " with: " + target,
- value);
- }
-
- /**
- * Check for a match. If target ends with "/", match will assume that the
- * target is meant to be a directory.
- *
- * @param pattern
- * Pattern as it would appear in a .gitignore file
- * @param target
- * Target file path relative to repository's GIT_DIR
- * @return Result of IgnoreRule.isMatch(String, boolean)
- */
- private static boolean match(String pattern, String target) {
- IgnoreRule r = new IgnoreRule(pattern);
- //If speed of this test is ever an issue, we can use a presetRule field
- //to avoid recompiling a pattern each time.
- return r.isMatch(target, target.endsWith("/"));
- }
-}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java
index 571f318..c026efc 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreNodeTest.java
@@ -44,15 +44,17 @@ package org.eclipse.jgit.ignore;
import static org.eclipse.jgit.junit.Assert.assertEquals;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
import java.util.Arrays;
-import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.ignore.IgnoreNode.MatchResult;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.FileMode;
@@ -60,6 +62,7 @@ import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.WorkingTreeIterator;
import org.eclipse.jgit.util.FileUtils;
+import org.eclipse.jgit.util.SystemReader;
import org.junit.Test;
/**
@@ -324,6 +327,15 @@ public class IgnoreNodeTest extends RepositoryTestCase {
}
@Test
+ public void testEmptyIgnoreRules() throws IOException {
+ IgnoreNode node = new IgnoreNode();
+ node.parse(writeToString("", "#", "!", "[[=a=]]"));
+ assertEquals(new ArrayList<>(), node.getRules());
+ node.parse(writeToString(" ", " / "));
+ assertEquals(2, node.getRules().size());
+ }
+
+ @Test
public void testSlashOnlyMatchesDirectory() throws IOException {
writeIgnoreFile(".gitignore", "out/");
writeTrashFile("out", "");
@@ -343,6 +355,56 @@ public class IgnoreNodeTest extends RepositoryTestCase {
}
@Test
+ public void testSlashMatchesDirectory() throws IOException {
+ writeIgnoreFile(".gitignore", "out2/");
+
+ writeTrashFile("out1/out1", "");
+ writeTrashFile("out1/out2", "");
+ writeTrashFile("out2/out1", "");
+ writeTrashFile("out2/out2", "");
+
+ beginWalk();
+ assertEntry(F, tracked, ".gitignore");
+ assertEntry(D, tracked, "out1");
+ assertEntry(F, tracked, "out1/out1");
+ assertEntry(F, tracked, "out1/out2");
+ assertEntry(D, ignored, "out2");
+ assertEntry(F, ignored, "out2/out1");
+ assertEntry(F, ignored, "out2/out2");
+ endWalk();
+ }
+
+ @Test
+ public void testWildcardWithSlashMatchesDirectory() throws IOException {
+ writeIgnoreFile(".gitignore", "out2*/");
+
+ writeTrashFile("out1/out1.txt", "");
+ writeTrashFile("out1/out2", "");
+ writeTrashFile("out1/out2.txt", "");
+ writeTrashFile("out1/out2x/a", "");
+ writeTrashFile("out2/out1.txt", "");
+ writeTrashFile("out2/out2.txt", "");
+ writeTrashFile("out2x/out1.txt", "");
+ writeTrashFile("out2x/out2.txt", "");
+
+ beginWalk();
+ assertEntry(F, tracked, ".gitignore");
+ assertEntry(D, tracked, "out1");
+ assertEntry(F, tracked, "out1/out1.txt");
+ assertEntry(F, tracked, "out1/out2");
+ assertEntry(F, tracked, "out1/out2.txt");
+ assertEntry(D, ignored, "out1/out2x");
+ assertEntry(F, ignored, "out1/out2x/a");
+ assertEntry(D, ignored, "out2");
+ assertEntry(F, ignored, "out2/out1.txt");
+ assertEntry(F, ignored, "out2/out2.txt");
+ assertEntry(D, ignored, "out2x");
+ assertEntry(F, ignored, "out2x/out1.txt");
+ assertEntry(F, ignored, "out2x/out2.txt");
+ endWalk();
+ }
+
+ @Test
public void testWithSlashDoesNotMatchInSubDirectory() throws IOException {
writeIgnoreFile(".gitignore", "a/b");
writeTrashFile("a/a", "");
@@ -375,6 +437,70 @@ public class IgnoreNodeTest extends RepositoryTestCase {
}
@Test
+ public void testLeadingSpaces() throws IOException {
+ writeTrashFile(" a/ a", "");
+ writeTrashFile(" a/ a", "");
+ writeTrashFile(" a/a", "");
+ writeTrashFile(" a/ a", "");
+ writeTrashFile(" a/ a", "");
+ writeTrashFile(" a/a", "");
+ writeIgnoreFile(".gitignore", " a", " a");
+ writeTrashFile("a/ a", "");
+ writeTrashFile("a/ a", "");
+ writeTrashFile("a/a", "");
+
+ beginWalk();
+ assertEntry(D, ignored, " a");
+ assertEntry(F, ignored, " a/ a");
+ assertEntry(F, ignored, " a/ a");
+ assertEntry(F, ignored, " a/a");
+ assertEntry(D, ignored, " a");
+ assertEntry(F, ignored, " a/ a");
+ assertEntry(F, ignored, " a/ a");
+ assertEntry(F, ignored, " a/a");
+ assertEntry(F, tracked, ".gitignore");
+ assertEntry(D, tracked, "a");
+ assertEntry(F, ignored, "a/ a");
+ assertEntry(F, ignored, "a/ a");
+ assertEntry(F, tracked, "a/a");
+ endWalk();
+ }
+
+ @Test
+ public void testTrailingSpaces() throws IOException {
+ // Windows can't create files with trailing spaces
+ // If this assumption fails the test is halted and ignored.
+ org.junit.Assume.assumeFalse(SystemReader.getInstance().isWindows());
+ writeTrashFile("a /a", "");
+ writeTrashFile("a /a ", "");
+ writeTrashFile("a /a ", "");
+ writeTrashFile("a /a", "");
+ writeTrashFile("a /a ", "");
+ writeTrashFile("a /a ", "");
+ writeTrashFile("a/a", "");
+ writeTrashFile("a/a ", "");
+ writeTrashFile("a/a ", "");
+
+ writeIgnoreFile(".gitignore", "a\\ ", "a \\ ");
+
+ beginWalk();
+ assertEntry(F, tracked, ".gitignore");
+ assertEntry(D, ignored, "a ");
+ assertEntry(F, ignored, "a /a");
+ assertEntry(F, ignored, "a /a ");
+ assertEntry(F, ignored, "a /a ");
+ assertEntry(D, ignored, "a ");
+ assertEntry(F, ignored, "a /a");
+ assertEntry(F, ignored, "a /a ");
+ assertEntry(F, ignored, "a /a ");
+ assertEntry(D, tracked, "a");
+ assertEntry(F, tracked, "a/a");
+ assertEntry(F, ignored, "a/a ");
+ assertEntry(F, ignored, "a/a ");
+ endWalk();
+ }
+
+ @Test
public void testToString() throws Exception {
assertEquals(Arrays.asList("").toString(), new IgnoreNode().toString());
assertEquals(Arrays.asList("hello").toString(),
@@ -382,7 +508,7 @@ public class IgnoreNodeTest extends RepositoryTestCase {
.toString());
}
- private void beginWalk() throws CorruptObjectException {
+ private void beginWalk() {
walk = new TreeWalk(db);
walk.addTree(new FileTreeIterator(db));
}
@@ -411,4 +537,12 @@ public class IgnoreNodeTest extends RepositoryTestCase {
data.append(line + "\n");
writeTrashFile(name, data.toString());
}
+
+ private InputStream writeToString(String... rules) throws IOException {
+ StringBuilder data = new StringBuilder();
+ for (String line : rules) {
+ data.append(line + "\n");
+ }
+ return new ByteArrayInputStream(data.toString().getBytes("UTF-8"));
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreRuleSpecialCasesTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreRuleSpecialCasesTest.java
index 7afa69f..567f3d8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreRuleSpecialCasesTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreRuleSpecialCasesTest.java
@@ -46,64 +46,32 @@ package org.eclipse.jgit.ignore;
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
-import java.util.Arrays;
-
import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameter;
-import org.junit.runners.Parameterized.Parameters;
- at RunWith(Parameterized.class)
- at SuppressWarnings({ "deprecation", "boxing" })
+ at SuppressWarnings({ "boxing" })
public class IgnoreRuleSpecialCasesTest {
- @Parameters(name = "OldRule? {0}")
- public static Iterable<Boolean[]> data() {
- return Arrays.asList(new Boolean[][] { { Boolean.FALSE },
- { Boolean.TRUE } });
- }
-
- @Parameter
- public Boolean useOldRule;
-
private void assertMatch(final String pattern, final String input,
final boolean matchExpected, Boolean... assume) {
boolean assumeDir = input.endsWith("/");
- if (useOldRule.booleanValue()) {
- final IgnoreRule matcher = new IgnoreRule(pattern);
- if (assume.length == 0 || !assume[0].booleanValue())
- assertEquals(matchExpected, matcher.isMatch(input, assumeDir));
- else
- assumeTrue(matchExpected == matcher.isMatch(input, assumeDir));
+ FastIgnoreRule matcher = new FastIgnoreRule(pattern);
+ if (assume.length == 0 || !assume[0].booleanValue()) {
+ assertEquals(matchExpected, matcher.isMatch(input, assumeDir));
} else {
- FastIgnoreRule matcher = new FastIgnoreRule(pattern);
- if (assume.length == 0 || !assume[0].booleanValue())
- assertEquals(matchExpected, matcher.isMatch(input, assumeDir));
- else
- assumeTrue(matchExpected == matcher.isMatch(input, assumeDir));
+ assumeTrue(matchExpected == matcher.isMatch(input, assumeDir));
}
}
private void assertFileNameMatch(final String pattern, final String input,
final boolean matchExpected) {
boolean assumeDir = input.endsWith("/");
- if (useOldRule.booleanValue()) {
- final IgnoreRule matcher = new IgnoreRule(pattern);
- assertEquals(matchExpected, matcher.isMatch(input, assumeDir));
- } else {
- FastIgnoreRule matcher = new FastIgnoreRule(pattern);
- assertEquals(matchExpected, matcher.isMatch(input, assumeDir));
- }
+ FastIgnoreRule matcher = new FastIgnoreRule(pattern);
+ assertEquals(matchExpected, matcher.isMatch(input, assumeDir));
}
@Test
public void testVerySimplePatternCase0() throws Exception {
- if (useOldRule)
- System.err
- .println("IgnoreRule can't understand blank lines, skipping");
- Boolean assume = useOldRule;
- assertMatch("", "", false, assume);
+ assertMatch("", "", false);
}
@Test
@@ -800,17 +768,14 @@ public class IgnoreRuleSpecialCasesTest {
@Test
public void testSpecialGroupCase9() throws Exception {
- assertMatch("][", "][", true);
+ assertMatch("][", "][", false);
}
@Test
public void testSpecialGroupCase10() throws Exception {
- if (useOldRule)
- System.err.println("IgnoreRule can't understand [[:], skipping");
- Boolean assume = useOldRule;
// Second bracket is threated literally, so both [ and : should match
- assertMatch("[[:]", ":", true, assume);
- assertMatch("[[:]", "[", true, assume);
+ assertMatch("[[:]", ":", true);
+ assertMatch("[[:]", "[", true);
}
@Test
@@ -865,13 +830,45 @@ public class IgnoreRuleSpecialCasesTest {
}
@Test
+ public void testIgnoredBackslash() throws Exception {
+ // In Git CLI a\b\c is equal to abc
+ assertMatch("a\\b\\c", "abc", true);
+ }
+
+ @Test
public void testEscapedBackslash() throws Exception {
- if (useOldRule)
- System.err
- .println("IgnoreRule can't understand escaped backslashes, skipping");
- Boolean assume = useOldRule;
// In Git CLI a\\b matches a\b file
- assertMatch("a\\\\b", "a\\b", true, assume);
+ assertMatch("a\\\\b", "a\\b", true);
+ assertMatch("a\\\\b\\c", "a\\bc", true);
+
+ }
+
+ @Test
+ public void testEscapedExclamationMark() throws Exception {
+ assertMatch("\\!b!.txt", "!b!.txt", true);
+ assertMatch("a\\!b!.txt", "a!b!.txt", true);
+ }
+
+ @Test
+ public void testEscapedHash() throws Exception {
+ assertMatch("\\#b", "#b", true);
+ assertMatch("a\\#", "a#", true);
+ }
+
+ @Test
+ public void testEscapedTrailingSpaces() throws Exception {
+ assertMatch("\\ ", " ", true);
+ assertMatch("a\\ ", "a ", true);
+ }
+
+ @Test
+ public void testNotEscapingBackslash() throws Exception {
+ assertMatch("\\out", "out", true);
+ assertMatch("\\out", "a/out", true);
+ assertMatch("c:\\/", "c:/", true);
+ assertMatch("c:\\/", "a/c:/", true);
+ assertMatch("c:\\tmp", "c:tmp", true);
+ assertMatch("c:\\tmp", "a/c:tmp", true);
}
@Test
@@ -880,6 +877,153 @@ public class IgnoreRuleSpecialCasesTest {
}
@Test
+ public void testBackslash() throws Exception {
+ assertMatch("a\\", "a", true);
+ assertMatch("\\a", "a", true);
+ assertMatch("a/\\", "a/", true);
+ assertMatch("a/b\\", "a/b", true);
+ assertMatch("\\a/b", "a/b", true);
+ assertMatch("/\\a", "/a", true);
+ assertMatch("\\a\\b\\c\\", "abc", true);
+ assertMatch("/\\a/\\b/\\c\\", "a/b/c", true);
+
+ // empty path segment doesn't match
+ assertMatch("\\/a", "/a", false);
+ assertMatch("\\/a", "a", false);
+ }
+
+ @Test
+ public void testDollar() throws Exception {
+ assertMatch("$", "$", true);
+ assertMatch("$x", "$x", true);
+ assertMatch("$x", "x$", false);
+ assertMatch("$x", "$", false);
+
+ assertMatch("$x.*", "$x.a", true);
+ assertMatch("*$", "x$", true);
+ assertMatch("*.$", "x.$", true);
+
+ assertMatch("$*x", "$ax", true);
+ assertMatch("x*$", "xa$", true);
+ assertMatch("x*$", "xa", false);
+ assertMatch("[a$b]", "$", true);
+ }
+
+ @Test
+ public void testCaret() throws Exception {
+ assertMatch("^", "^", true);
+ assertMatch("^x", "^x", true);
+ assertMatch("^x", "x^", false);
+ assertMatch("^x", "^", false);
+
+ assertMatch("^x.*", "^x.a", true);
+ assertMatch("*^", "x^", true);
+ assertMatch("*.^", "x.^", true);
+
+ assertMatch("x*^", "xa^", true);
+ assertMatch("^*x", "^ax", true);
+ assertMatch("^*x", "ax", false);
+ assertMatch("[a^b]", "^", true);
+ }
+
+ @Test
+ public void testPlus() throws Exception {
+ assertMatch("+", "+", true);
+ assertMatch("+x", "+x", true);
+ assertMatch("+x", "x+", false);
+ assertMatch("+x", "+", false);
+ assertMatch("x+", "xx", false);
+
+ assertMatch("+x.*", "+x.a", true);
+ assertMatch("*+", "x+", true);
+ assertMatch("*.+", "x.+", true);
+
+ assertMatch("x*+", "xa+", true);
+ assertMatch("+*x", "+ax", true);
+ assertMatch("+*x", "ax", false);
+ assertMatch("[a+b]", "+", true);
+ }
+
+ @Test
+ public void testPipe() throws Exception {
+ assertMatch("|", "|", true);
+ assertMatch("|x", "|x", true);
+ assertMatch("|x", "x|", false);
+ assertMatch("|x", "|", false);
+ assertMatch("x|x", "xx", false);
+
+ assertMatch("x|x.*", "x|x.a", true);
+ assertMatch("*|", "x|", true);
+ assertMatch("*.|", "x.|", true);
+
+ assertMatch("x*|a", "xb|a", true);
+ assertMatch("b|*x", "b|ax", true);
+ assertMatch("b|*x", "ax", false);
+ assertMatch("[a|b]", "|", true);
+ }
+
+ @Test
+ public void testBrackets() throws Exception {
+ assertMatch("{}*()", "{}x()", true);
+ assertMatch("[a{}()b][a{}()b]?[a{}()b][a{}()b]", "{}x()", true);
+ assertMatch("x*{x}3", "xa{x}3", true);
+ assertMatch("a*{x}3", "axxx", false);
+
+ assertMatch("?", "[", true);
+ assertMatch("*", "[", true);
+
+ // Escaped bracket matches, but see weird things below...
+ assertMatch("\\[", "[", true);
+ }
+
+ /**
+ * The ignore rules here <b>do not match</b> any paths because single '['
+ * begins character group and the entire rule cannot be parsed due the
+ * invalid glob pattern. See
+ * http://article.gmane.org/gmane.comp.version-control.git/278699.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testBracketsUnmatched1() throws Exception {
+ assertMatch("[", "[", false);
+ assertMatch("[*", "[", false);
+ assertMatch("*[", "[", false);
+ assertMatch("*[", "a[", false);
+ assertMatch("[a][", "a[", false);
+ assertMatch("*[", "a", false);
+ assertMatch("[a", "a", false);
+ assertMatch("[*", "a", false);
+ assertMatch("[*a", "a", false);
+ }
+
+ /**
+ * Single ']' is treated here literally, not as an and of a character group
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testBracketsUnmatched2() throws Exception {
+ assertMatch("*]", "a", false);
+ assertMatch("]a", "a", false);
+ assertMatch("]*", "a", false);
+ assertMatch("]*a", "a", false);
+
+ assertMatch("]", "]", true);
+ assertMatch("]*", "]", true);
+ assertMatch("]*", "]a", true);
+ assertMatch("*]", "]", true);
+ assertMatch("*]", "a]", true);
+ }
+
+ @Test
+ public void testBracketsRandom() throws Exception {
+ assertMatch("[\\]", "[$0+//r4a\\d]", false);
+ assertMatch("[:]]sZX]", "[:]]sZX]", false);
+ assertMatch("[:]]:]]]", "[:]]:]]]", false);
+ }
+
+ @Test
public void testFilePathSimpleCase() throws Exception {
assertFileNameMatch("a/b", "a/b", true);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DeltaBaseCacheTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DeltaBaseCacheTest.java
new file mode 100644
index 0000000..5bef9fa
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DeltaBaseCacheTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.dfs;
+
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import org.eclipse.jgit.internal.storage.dfs.DeltaBaseCache.Entry;
+import org.eclipse.jgit.junit.TestRng;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DeltaBaseCacheTest {
+ private static final int SZ = 512;
+
+ private DfsPackKey key;
+ private DeltaBaseCache cache;
+ private TestRng rng;
+
+ @Before
+ public void setUp() {
+ key = new DfsPackKey();
+ cache = new DeltaBaseCache(SZ);
+ rng = new TestRng(getClass().getSimpleName());
+ }
+
+ @Test
+ public void testObjectLargerThanCacheDoesNotEvict() {
+ byte[] obj12 = put(12, 32);
+ put(24, SZ + 5);
+ assertNull("does not store large object", cache.get(key, 24));
+ get(obj12, 12);
+ }
+
+ @Test
+ public void testCacheLruExpires1() {
+ byte[] obj1 = put(1, SZ / 4);
+ put(2, SZ / 4);
+ byte[] obj3 = put(3, SZ / 4);
+ put(4, SZ / 4);
+ assertEquals(SZ, cache.getMemoryUsed());
+
+ get(obj3, 3);
+ get(obj1, 1);
+ put(5, SZ / 2);
+ assertEquals(SZ, cache.getMemoryUsed());
+ assertEquals(SZ, cache.getMemoryUsedByTableForTest());
+ assertEquals(SZ, cache.getMemoryUsedByLruChainForTest());
+ assertNull(cache.get(key, 4));
+ assertNull(cache.get(key, 2));
+
+ get(obj1, 1);
+ get(obj3, 3);
+ }
+
+ @Test
+ public void testCacheLruExpires2() {
+ int pos0 = (0 << 10) | 2;
+ int pos1 = (1 << 10) | 2;
+ int pos2 = (2 << 10) | 2;
+ int pos5 = (5 << 10) | 2;
+ int pos6 = (6 << 10) | 2;
+
+ put(pos0, SZ / 4);
+ put(pos5, SZ / 4);
+ byte[] obj1 = put(pos1, SZ / 4);
+ byte[] obj2 = put(pos2, SZ / 4);
+ assertEquals(SZ, cache.getMemoryUsed());
+
+ byte[] obj6 = put(pos6, SZ / 2);
+ assertEquals(SZ, cache.getMemoryUsed());
+ assertEquals(SZ, cache.getMemoryUsedByTableForTest());
+ assertEquals(SZ, cache.getMemoryUsedByLruChainForTest());
+ assertNull(cache.get(key, pos0));
+ assertNull(cache.get(key, pos5));
+
+ get(obj1, pos1);
+ get(obj2, pos2);
+ get(obj6, pos6);
+ }
+
+ @Test
+ public void testCacheMemoryUsedConsistentWithExpectations() {
+ put(1, 32);
+ put(2, 32);
+ put(3, 32);
+
+ assertNotNull(cache.get(key, 1));
+ assertNotNull(cache.get(key, 1));
+
+ assertEquals(32 * 3, cache.getMemoryUsed());
+ assertEquals(32 * 3, cache.getMemoryUsedByTableForTest());
+ assertEquals(32 * 3, cache.getMemoryUsedByLruChainForTest());
+ }
+
+ private void get(byte[] data, int position) {
+ Entry e = cache.get(key, position);
+ assertNotNull("expected entry at " + position, e);
+ assertEquals("expected blob for " + position, OBJ_BLOB, e.type);
+ assertSame("expected data for " + position, data, e.data);
+ }
+
+ private byte[] put(int position, int sz) {
+ byte[] data = rng.nextBytes(sz);
+ cache.put(key, position, OBJ_BLOB, data);
+ return data;
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java
index 693388c..11a0924 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsInserterTest.java
@@ -51,9 +51,12 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.zip.Deflater;
+import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
import org.eclipse.jgit.internal.storage.pack.PackExt;
import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.TestRng;
@@ -78,12 +81,11 @@ public class DfsInserterTest {
@Test
public void testInserterDiscardsPack() throws IOException {
- ObjectInserter ins = db.newObjectInserter();
- ins.insert(Constants.OBJ_BLOB, Constants.encode("foo"));
- ins.insert(Constants.OBJ_BLOB, Constants.encode("bar"));
- assertEquals(0, db.getObjectDatabase().listPacks().size());
-
- ins.release();
+ try (ObjectInserter ins = db.newObjectInserter()) {
+ ins.insert(Constants.OBJ_BLOB, Constants.encode("foo"));
+ ins.insert(Constants.OBJ_BLOB, Constants.encode("bar"));
+ assertEquals(0, db.getObjectDatabase().listPacks().size());
+ }
assertEquals(0, db.getObjectDatabase().listPacks().size());
}
@@ -162,6 +164,56 @@ public class DfsInserterTest {
assertEquals(id2, objs.iterator().next());
}
+ @Test
+ public void testGarbageSelectivelyVisible() throws IOException {
+ ObjectInserter ins = db.newObjectInserter();
+ ObjectId fooId = ins.insert(Constants.OBJ_BLOB, Constants.encode("foo"));
+ ins.flush();
+ assertEquals(1, db.getObjectDatabase().listPacks().size());
+
+ // Make pack 0 garbage.
+ db.getObjectDatabase().listPacks().get(0).setPackSource(PackSource.UNREACHABLE_GARBAGE);
+
+ // Default behavior should be that the database has foo, because we allow garbage objects.
+ assertTrue(db.getObjectDatabase().has(fooId));
+ // But we should not be able to see it if we pass the right args.
+ assertFalse(db.getObjectDatabase().has(fooId, true));
+ }
+
+ @Test
+ public void testInserterIgnoresUnreachable() throws IOException {
+ ObjectInserter ins = db.newObjectInserter();
+ ObjectId fooId = ins.insert(Constants.OBJ_BLOB, Constants.encode("foo"));
+ ins.flush();
+ assertEquals(1, db.getObjectDatabase().listPacks().size());
+
+ // Make pack 0 garbage.
+ db.getObjectDatabase().listPacks().get(0).setPackSource(PackSource.UNREACHABLE_GARBAGE);
+
+ // We shouldn't be able to see foo because it's garbage.
+ assertFalse(db.getObjectDatabase().has(fooId, true));
+
+ // But if we re-insert foo, it should become visible again.
+ ins.insert(Constants.OBJ_BLOB, Constants.encode("foo"));
+ ins.flush();
+ assertTrue(db.getObjectDatabase().has(fooId, true));
+
+ // Verify that we have a foo in both packs, and 1 of them is garbage.
+ DfsReader reader = new DfsReader(db.getObjectDatabase());
+ DfsPackFile packs[] = db.getObjectDatabase().getPacks();
+ Set<PackSource> pack_sources = new HashSet<PackSource>();
+
+ assertEquals(2, packs.length);
+
+ pack_sources.add(packs[0].getPackDescription().getPackSource());
+ pack_sources.add(packs[1].getPackDescription().getPackSource());
+
+ assertTrue(packs[0].hasObject(reader, fooId));
+ assertTrue(packs[1].hasObject(reader, fooId));
+ assertTrue(pack_sources.contains(PackSource.UNREACHABLE_GARBAGE));
+ assertTrue(pack_sources.contains(PackSource.INSERT));
+ }
+
private static String readString(ObjectLoader loader) throws IOException {
return RawParseUtils.decode(readStream(loader));
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java
index 3a0b827..9875403 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/AbbreviationTest.java
@@ -94,8 +94,9 @@ public class AbbreviationTest extends LocalDiskRepositoryTestCase {
@After
public void tearDown() throws Exception {
- if (reader != null)
- reader.release();
+ if (reader != null) {
+ reader.close();
+ }
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ConcurrentRepackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ConcurrentRepackTest.java
index 562cde7..514e00f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ConcurrentRepackTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/ConcurrentRepackTest.java
@@ -150,11 +150,11 @@ public class ConcurrentRepackTest extends RepositoryTestCase {
// within the pack has been modified.
//
final RevObject o2 = writeBlob(eden, "o2");
- final PackWriter pw = new PackWriter(eden);
- pw.addObject(o2);
- pw.addObject(o1);
- write(out1, pw);
- pw.release();
+ try (PackWriter pw = new PackWriter(eden)) {
+ pw.addObject(o2);
+ pw.addObject(o1);
+ write(out1, pw);
+ }
// Try the old name, then the new name. The old name should cause the
// pack to reload when it opens and the index and pack mismatch.
@@ -216,18 +216,18 @@ public class ConcurrentRepackTest extends RepositoryTestCase {
private File[] pack(final Repository src, final RevObject... list)
throws IOException {
- final PackWriter pw = new PackWriter(src);
- for (final RevObject o : list) {
- pw.addObject(o);
- }
+ try (PackWriter pw = new PackWriter(src)) {
+ for (final RevObject o : list) {
+ pw.addObject(o);
+ }
- final ObjectId name = pw.computeName();
- final File packFile = fullPackFileName(name, ".pack");
- final File idxFile = fullPackFileName(name, ".idx");
- final File[] files = new File[] { packFile, idxFile };
- write(files, pw);
- pw.release();
- return files;
+ final ObjectId name = pw.computeName();
+ final File packFile = fullPackFileName(name, ".pack");
+ final File idxFile = fullPackFileName(name, ".idx");
+ final File[] files = new File[] { packFile, idxFile };
+ write(files, pw);
+ return files;
+ }
}
private static void write(final File[] files, final PackWriter pw)
@@ -282,13 +282,10 @@ public class ConcurrentRepackTest extends RepositoryTestCase {
throws IOException {
final RevWalk revWalk = new RevWalk(repo);
final byte[] bytes = Constants.encode(data);
- final ObjectInserter inserter = repo.newObjectInserter();
final ObjectId id;
- try {
+ try (ObjectInserter inserter = repo.newObjectInserter()) {
id = inserter.insert(Constants.OBJ_BLOB, bytes);
inserter.flush();
- } finally {
- inserter.release();
}
try {
parse(id);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderTest.java
index 280d604..dca3564 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileRepositoryBuilderTest.java
@@ -72,6 +72,7 @@ public class FileRepositoryBuilderTest extends LocalDiskRepositoryTestCase {
.findGitDir(d).getGitDir());
}
+ @SuppressWarnings("unused")
@Test
public void emptyRepositoryFormatVersion() throws Exception {
Repository r = createWorkRepository();
@@ -83,6 +84,7 @@ public class FileRepositoryBuilderTest extends LocalDiskRepositoryTestCase {
new FileRepository(r.getDirectory());
}
+ @SuppressWarnings("unused")
@Test
public void invalidRepositoryFormatVersion() throws Exception {
Repository r = createWorkRepository();
@@ -99,12 +101,13 @@ public class FileRepositoryBuilderTest extends LocalDiskRepositoryTestCase {
}
}
+ @SuppressWarnings("unused")
@Test
public void unknownRepositoryFormatVersion() throws Exception {
Repository r = createWorkRepository();
StoredConfig config = r.getConfig();
config.setLong(ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_REPO_FORMAT_VERSION, 1);
+ ConfigConstants.CONFIG_KEY_REPO_FORMAT_VERSION, 999999);
config.save();
try {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
index 0742504..f549fb5 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcBasicPackingTest.java
@@ -45,13 +45,16 @@ package org.eclipse.jgit.internal.storage.file;
import static org.junit.Assert.assertEquals;
+import java.io.File;
import java.io.IOException;
import java.util.Collection;
+import java.util.Date;
import java.util.Iterator;
import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.storage.pack.PackConfig;
+import org.junit.Test;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
@@ -82,6 +85,7 @@ public class GcBasicPackingTest extends GcTestCase {
assertEquals(4, stats.numberOfLooseObjects);
assertEquals(0, stats.numberOfPackedObjects);
assertEquals(0, stats.numberOfPackFiles);
+ assertEquals(0, stats.numberOfBitmaps);
}
@Theory
@@ -99,6 +103,7 @@ public class GcBasicPackingTest extends GcTestCase {
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(8, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);
+ assertEquals(2, stats.numberOfBitmaps);
}
@Theory
@@ -115,6 +120,7 @@ public class GcBasicPackingTest extends GcTestCase {
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(4, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);
+ assertEquals(1, stats.numberOfBitmaps);
// Do the gc again and check that it hasn't changed anything
gc.gc();
@@ -122,10 +128,12 @@ public class GcBasicPackingTest extends GcTestCase {
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(4, stats.numberOfPackedObjects);
assertEquals(1, stats.numberOfPackFiles);
+ assertEquals(1, stats.numberOfBitmaps);
}
@Theory
- public void testPackCommitsAndLooseOne(boolean aggressive) throws Exception {
+ public void testPackCommitsAndLooseOne(boolean aggressive)
+ throws Exception {
BranchBuilder bb = tr.branch("refs/heads/master");
RevCommit first = bb.commit().add("A", "A").add("B", "B").create();
bb.commit().add("A", "A2").add("B", "B2").create();
@@ -140,6 +148,7 @@ public class GcBasicPackingTest extends GcTestCase {
assertEquals(0, stats.numberOfLooseObjects);
assertEquals(8, stats.numberOfPackedObjects);
assertEquals(2, stats.numberOfPackFiles);
+ assertEquals(1, stats.numberOfBitmaps);
}
@Theory
@@ -176,6 +185,47 @@ public class GcBasicPackingTest extends GcTestCase {
}
}
+ @Test
+ public void testDonePruneTooYoungPacks() throws Exception {
+ BranchBuilder bb = tr.branch("refs/heads/master");
+ bb.commit().message("M").add("M", "M").create();
+
+ gc.setExpireAgeMillis(0);
+ gc.gc();
+ stats = gc.getStatistics();
+ assertEquals(0, stats.numberOfLooseObjects);
+ assertEquals(3, stats.numberOfPackedObjects);
+ assertEquals(1, stats.numberOfPackFiles);
+ File oldPackfile = tr.getRepository().getObjectDatabase().getPacks()
+ .iterator().next().getPackFile();
+
+ fsTick();
+ bb.commit().message("B").add("B", "Q").create();
+
+ // The old packfile is too young to be deleted. We should end up with
+ // two pack files
+ gc.setExpire(new Date(oldPackfile.lastModified() - 1));
+ gc.gc();
+ stats = gc.getStatistics();
+ assertEquals(0, stats.numberOfLooseObjects);
+ // if objects exist in multiple packFiles then they are counted multiple
+ // times
+ assertEquals(9, stats.numberOfPackedObjects);
+ assertEquals(2, stats.numberOfPackFiles);
+
+ // repack again but now without a grace period for packfiles. We should
+ // end up with one packfile
+ gc.setExpireAgeMillis(0);
+ gc.gc();
+ stats = gc.getStatistics();
+ assertEquals(0, stats.numberOfLooseObjects);
+ // if objects exist in multiple packFiles then they are counted multiple
+ // times
+ assertEquals(6, stats.numberOfPackedObjects);
+ assertEquals(1, stats.numberOfPackFiles);
+
+ }
+
private void configureGc(GC myGc, boolean aggressive) {
PackConfig pconfig = new PackConfig(repo);
if (aggressive) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java
index c7336da..fa40458 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java
@@ -77,7 +77,7 @@ public class GcPackRefsTest extends GcTestCase {
tr.lightweightTag("t", a);
gc.packRefs();
- assertSame(repo.getRef("t").getStorage(), Storage.PACKED);
+ assertSame(repo.exactRef("refs/tags/t").getStorage(), Storage.PACKED);
}
@Test
@@ -126,8 +126,8 @@ public class GcPackRefsTest extends GcTestCase {
refLock.unlock();
}
- assertSame(repo.getRef("refs/tags/t1").getStorage(), Storage.LOOSE);
- assertSame(repo.getRef("refs/tags/t2").getStorage(), Storage.PACKED);
+ assertSame(repo.exactRef("refs/tags/t1").getStorage(), Storage.LOOSE);
+ assertSame(repo.exactRef("refs/tags/t2").getStorage(), Storage.PACKED);
}
@Test
@@ -146,7 +146,7 @@ public class GcPackRefsTest extends GcTestCase {
public Result call() throws Exception {
RefUpdate update = new RefDirectoryUpdate(
(RefDirectory) repo.getRefDatabase(),
- repo.getRef("refs/tags/t")) {
+ repo.exactRef("refs/tags/t")) {
@Override
public boolean isForceUpdate() {
try {
@@ -182,7 +182,7 @@ public class GcPackRefsTest extends GcTestCase {
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
}
- assertEquals(repo.getRef("refs/tags/t").getObjectId(), b);
+ assertEquals(repo.exactRef("refs/tags/t").getObjectId(), b);
}
@Test
@@ -194,23 +194,23 @@ public class GcPackRefsTest extends GcTestCase {
// check for the unborn branch master. HEAD should point to master and
// master doesn't exist.
- assertEquals(repo.getRef("HEAD").getTarget().getName(),
+ assertEquals(repo.exactRef("HEAD").getTarget().getName(),
"refs/heads/master");
- assertNull(repo.getRef("HEAD").getTarget().getObjectId());
+ assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
gc.packRefs();
- assertSame(repo.getRef("HEAD").getStorage(), Storage.LOOSE);
- assertEquals(repo.getRef("HEAD").getTarget().getName(),
+ assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
+ assertEquals(repo.exactRef("HEAD").getTarget().getName(),
"refs/heads/master");
- assertNull(repo.getRef("HEAD").getTarget().getObjectId());
+ assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
git.checkout().setName("refs/heads/side").call();
gc.packRefs();
- assertSame(repo.getRef("HEAD").getStorage(), Storage.LOOSE);
+ assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
// check for detached HEAD
git.checkout().setName(first.getName()).call();
gc.packRefs();
- assertSame(repo.getRef("HEAD").getStorage(), Storage.LOOSE);
+ assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
}
@Test
@@ -229,20 +229,20 @@ public class GcPackRefsTest extends GcTestCase {
// check for the unborn branch master. HEAD should point to master and
// master doesn't exist.
- assertEquals(repo.getRef("HEAD").getTarget().getName(),
+ assertEquals(repo.exactRef("HEAD").getTarget().getName(),
"refs/heads/master");
- assertNull(repo.getRef("HEAD").getTarget().getObjectId());
+ assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
gc.packRefs();
- assertSame(repo.getRef("HEAD").getStorage(), Storage.LOOSE);
- assertEquals(repo.getRef("HEAD").getTarget().getName(),
+ assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
+ assertEquals(repo.exactRef("HEAD").getTarget().getName(),
"refs/heads/master");
- assertNull(repo.getRef("HEAD").getTarget().getObjectId());
+ assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
// check for non-detached HEAD
repo.updateRef(Constants.HEAD).link("refs/heads/side");
gc.packRefs();
- assertSame(repo.getRef("HEAD").getStorage(), Storage.LOOSE);
- assertEquals(repo.getRef("HEAD").getTarget().getObjectId(),
+ assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
+ assertEquals(repo.exactRef("HEAD").getTarget().getObjectId(),
second.getId());
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java
index 2a096fd..3c781a9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcReflogTest.java
@@ -75,6 +75,7 @@ public class GcReflogTest extends GcTestCase {
tr.blob("x");
stats = gc.getStatistics();
assertEquals(9, stats.numberOfLooseObjects);
+ fsTick();
gc.prune(Collections.<ObjectId> emptySet());
stats = gc.getStatistics();
assertEquals(8, stats.numberOfLooseObjects);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java
index a764f0f..5abf625 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcTestCase.java
@@ -52,6 +52,7 @@ import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.junit.TestRepository.CommitBuilder;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
import org.junit.After;
import org.junit.Before;
@@ -65,7 +66,8 @@ public abstract class GcTestCase extends LocalDiskRepositoryTestCase {
public void setUp() throws Exception {
super.setUp();
repo = createWorkRepository();
- tr = new TestRepository<FileRepository>((repo));
+ tr = new TestRepository<FileRepository>(repo, new RevWalk(repo),
+ mockSystemReader);
gc = new GC(repo);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileTest.java
index 3324627..cb80768 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackFileTest.java
@@ -123,7 +123,7 @@ public class PackFileTest extends LocalDiskRepositoryTestCase {
@After
public void tearDown() throws Exception {
if (wc != null)
- wc.release();
+ wc.close();
new WindowCacheConfig().install();
super.tearDown();
}
@@ -373,8 +373,9 @@ public class PackFileTest extends LocalDiskRepositoryTestCase {
@After
public void release() {
- if (inserter != null)
- inserter.release();
+ if (inserter != null) {
+ inserter.close();
+ }
}
private PackParser index(byte[] raw) throws IOException {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
index 873b2cd..01d6ee6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/PackWriterTest.java
@@ -43,11 +43,13 @@
package org.eclipse.jgit.internal.storage.file;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.eclipse.jgit.internal.storage.pack.PackWriter.NONE;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -66,19 +68,19 @@ import java.util.Set;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
import org.eclipse.jgit.internal.storage.pack.PackWriter;
-import org.eclipse.jgit.internal.storage.pack.PackWriter.ObjectIdSet;
import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
-import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdSet;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.pack.PackConfig;
+import org.eclipse.jgit.storage.pack.PackStatistics;
import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
import org.eclipse.jgit.transport.PackParser;
import org.junit.After;
@@ -87,9 +89,6 @@ import org.junit.Test;
public class PackWriterTest extends SampleDataRepositoryTestCase {
- private static final Set<ObjectId> EMPTY_SET_OBJECT = Collections
- .<ObjectId> emptySet();
-
private static final List<RevObject> EMPTY_LIST_REVS = Collections
.<RevObject> emptyList();
@@ -120,11 +119,11 @@ public class PackWriterTest extends SampleDataRepositoryTestCase {
@After
public void tearDown() throws Exception {
if (writer != null) {
- writer.release();
+ writer.close();
writer = null;
}
if (inserter != null) {
- inserter.release();
+ inserter.close();
inserter = null;
}
super.tearDown();
@@ -170,7 +169,7 @@ public class PackWriterTest extends SampleDataRepositoryTestCase {
*/
@Test
public void testWriteEmptyPack1() throws IOException {
- createVerifyOpenPack(EMPTY_SET_OBJECT, EMPTY_SET_OBJECT, false, false);
+ createVerifyOpenPack(NONE, NONE, false, false);
assertEquals(0, writer.getObjectCount());
assertEquals(0, pack.getObjectCount());
@@ -203,8 +202,8 @@ public class PackWriterTest extends SampleDataRepositoryTestCase {
final ObjectId nonExisting = ObjectId
.fromString("0000000000000000000000000000000000000001");
try {
- createVerifyOpenPack(EMPTY_SET_OBJECT, Collections.singleton(
- nonExisting), false, false);
+ createVerifyOpenPack(NONE, Collections.singleton(nonExisting),
+ false, false);
fail("Should have thrown MissingObjectException");
} catch (MissingObjectException x) {
// expected
@@ -220,8 +219,8 @@ public class PackWriterTest extends SampleDataRepositoryTestCase {
public void testIgnoreNonExistingObjects() throws IOException {
final ObjectId nonExisting = ObjectId
.fromString("0000000000000000000000000000000000000001");
- createVerifyOpenPack(EMPTY_SET_OBJECT, Collections.singleton(
- nonExisting), false, true);
+ createVerifyOpenPack(NONE, Collections.singleton(nonExisting),
+ false, true);
// shouldn't throw anything
}
@@ -239,8 +238,8 @@ public class PackWriterTest extends SampleDataRepositoryTestCase {
final ObjectId nonExisting = ObjectId
.fromString("0000000000000000000000000000000000000001");
new GC(db).gc();
- createVerifyOpenPack(EMPTY_SET_OBJECT,
- Collections.singleton(nonExisting), false, true, true);
+ createVerifyOpenPack(NONE, Collections.singleton(nonExisting), false,
+ true, true);
// shouldn't throw anything
}
@@ -438,6 +437,38 @@ public class PackWriterTest extends SampleDataRepositoryTestCase {
}
@Test
+ public void testDeltaStatistics() throws Exception {
+ config.setDeltaCompress(true);
+ FileRepository repo = createBareRepository();
+ TestRepository<FileRepository> testRepo = new TestRepository<FileRepository>(repo);
+ ArrayList<RevObject> blobs = new ArrayList<>();
+ blobs.add(testRepo.blob(genDeltableData(1000)));
+ blobs.add(testRepo.blob(genDeltableData(1005)));
+
+ try (PackWriter pw = new PackWriter(repo)) {
+ NullProgressMonitor m = NullProgressMonitor.INSTANCE;
+ pw.preparePack(blobs.iterator());
+ pw.writePack(m, m, os);
+ PackStatistics stats = pw.getStatistics();
+ assertEquals(1, stats.getTotalDeltas());
+ assertTrue("Delta bytes not set.",
+ stats.byObjectType(OBJ_BLOB).getDeltaBytes() > 0);
+ }
+ }
+
+ // Generate consistent junk data for building files that delta well
+ private String genDeltableData(int length) {
+ assertTrue("Generated data must have a length > 0", length > 0);
+ char[] data = {'a', 'b', 'c', '\n'};
+ StringBuilder builder = new StringBuilder(length);
+ for (int i = 0; i < length; i++) {
+ builder.append(data[i % 4]);
+ }
+ return builder.toString();
+ }
+
+
+ @Test
public void testWriteIndex() throws Exception {
config.setIndexVersion(2);
writeVerifyPack4(false);
@@ -494,7 +525,7 @@ public class PackWriterTest extends SampleDataRepositoryTestCase {
RevCommit c2 = bb.commit().add("f", contentB).create();
testRepo.getRevWalk().parseHeaders(c2);
PackIndex pf2 = writePack(repo, Collections.singleton(c2),
- Collections.singleton(objectIdSet(pf1)));
+ Collections.<ObjectIdSet> singleton(pf1));
assertContent(
pf2,
Arrays.asList(c2.getId(), c2.getTree().getId(),
@@ -514,26 +545,25 @@ public class PackWriterTest extends SampleDataRepositoryTestCase {
private static PackIndex writePack(FileRepository repo,
Set<? extends ObjectId> want, Set<ObjectIdSet> excludeObjects)
throws IOException {
- PackWriter pw = new PackWriter(repo);
- pw.setDeltaBaseAsOffset(true);
- pw.setReuseDeltaCommits(false);
- for (ObjectIdSet idx : excludeObjects)
- pw.excludeObjects(idx);
- pw.preparePack(NullProgressMonitor.INSTANCE, want,
- Collections.<ObjectId> emptySet());
- String id = pw.computeName().getName();
- File packdir = new File(repo.getObjectsDirectory(), "pack");
- File packFile = new File(packdir, "pack-" + id + ".pack");
- FileOutputStream packOS = new FileOutputStream(packFile);
- pw.writePack(NullProgressMonitor.INSTANCE,
- NullProgressMonitor.INSTANCE, packOS);
- packOS.close();
- File idxFile = new File(packdir, "pack-" + id + ".idx");
- FileOutputStream idxOS = new FileOutputStream(idxFile);
- pw.writeIndex(idxOS);
- idxOS.close();
- pw.release();
- return PackIndex.open(idxFile);
+ try (PackWriter pw = new PackWriter(repo)) {
+ pw.setDeltaBaseAsOffset(true);
+ pw.setReuseDeltaCommits(false);
+ for (ObjectIdSet idx : excludeObjects)
+ pw.excludeObjects(idx);
+ pw.preparePack(NullProgressMonitor.INSTANCE, want, NONE);
+ String id = pw.computeName().getName();
+ File packdir = new File(repo.getObjectsDirectory(), "pack");
+ File packFile = new File(packdir, "pack-" + id + ".pack");
+ FileOutputStream packOS = new FileOutputStream(packFile);
+ pw.writePack(NullProgressMonitor.INSTANCE,
+ NullProgressMonitor.INSTANCE, packOS);
+ packOS.close();
+ File idxFile = new File(packdir, "pack-" + id + ".idx");
+ FileOutputStream idxOS = new FileOutputStream(idxFile);
+ pw.writeIndex(idxOS);
+ idxOS.close();
+ return PackIndex.open(idxFile);
+ }
}
// TODO: testWritePackDeltasCycle()
@@ -543,7 +573,7 @@ public class PackWriterTest extends SampleDataRepositoryTestCase {
final HashSet<ObjectId> interestings = new HashSet<ObjectId>();
interestings.add(ObjectId
.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"));
- createVerifyOpenPack(interestings, EMPTY_SET_OBJECT, false, false);
+ createVerifyOpenPack(interestings, NONE, false, false);
final ObjectId expectedOrder[] = new ObjectId[] {
ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"),
@@ -639,7 +669,7 @@ public class PackWriterTest extends SampleDataRepositoryTestCase {
writer.setIgnoreMissingUninteresting(ignoreMissingUninteresting);
writer.preparePack(m, interestings, uninterestings);
writer.writePack(m, m, os);
- writer.release();
+ writer.close();
verifyOpenPack(thin);
}
@@ -650,7 +680,7 @@ public class PackWriterTest extends SampleDataRepositoryTestCase {
writer.preparePack(objectSource.iterator());
assertEquals(objectSource.size(), writer.getObjectCount());
writer.writePack(m, m, os);
- writer.release();
+ writer.close();
verifyOpenPack(false);
}
@@ -699,12 +729,4 @@ public class PackWriterTest extends SampleDataRepositoryTestCase {
assertEquals(objectsOrder[i++].toObjectId(), me.toObjectId());
}
}
-
- private static ObjectIdSet objectIdSet(final PackIndex idx) {
- return new ObjectIdSet() {
- public boolean contains(AnyObjectId objectId) {
- return idx.hasObject(objectId);
- }
- };
- }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java
index 8dbe644..52e181b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java
@@ -359,6 +359,33 @@ public class RefDirectoryTest extends LocalDiskRepositoryTestCase {
}
@Test
+ public void testFirstExactRef_IgnoresGarbageRef() throws IOException {
+ writeLooseRef("refs/heads/A", A);
+ write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "FAIL\n");
+
+ Ref a = refdir.firstExactRef("refs/heads/bad", "refs/heads/A");
+ assertEquals("refs/heads/A", a.getName());
+ assertEquals(A, a.getObjectId());
+ }
+
+ @Test
+ public void testExactRef_IgnoresGarbageRef() throws IOException {
+ writeLooseRef("refs/heads/A", A);
+ write(new File(diskRepo.getDirectory(), "refs/heads/bad"), "FAIL\n");
+
+ Map<String, Ref> refs =
+ refdir.exactRef("refs/heads/bad", "refs/heads/A");
+
+ assertNull("no refs/heads/bad", refs.get("refs/heads/bad"));
+
+ Ref a = refs.get("refs/heads/A");
+ assertEquals("refs/heads/A", a.getName());
+ assertEquals(A, a.getObjectId());
+
+ assertEquals(1, refs.size());
+ }
+
+ @Test
public void testGetRefs_InvalidName() throws IOException {
writeLooseRef("refs/heads/A", A);
@@ -464,6 +491,21 @@ public class RefDirectoryTest extends LocalDiskRepositoryTestCase {
}
@Test
+ public void testFirstExactRef_Mixed() throws IOException {
+ writeLooseRef("refs/heads/A", A);
+ writePackedRef("refs/tags/v1.0", v1_0);
+
+ Ref a = refdir.firstExactRef("refs/heads/A", "refs/tags/v1.0");
+ Ref one = refdir.firstExactRef("refs/tags/v1.0", "refs/heads/A");
+
+ assertEquals("refs/heads/A", a.getName());
+ assertEquals("refs/tags/v1.0", one.getName());
+
+ assertEquals(A, a.getObjectId());
+ assertEquals(v1_0, one.getObjectId());
+ }
+
+ @Test
public void testGetRefs_TagsOnly_AllLoose() throws IOException {
Map<String, Ref> tags;
Ref a;
@@ -816,6 +858,36 @@ public class RefDirectoryTest extends LocalDiskRepositoryTestCase {
}
@Test
+ public void testGetRef_CycleInSymbolicRef() throws IOException {
+ Ref r;
+
+ writeLooseRef("refs/1", "ref: refs/2\n");
+ writeLooseRef("refs/2", "ref: refs/3\n");
+ writeLooseRef("refs/3", "ref: refs/4\n");
+ writeLooseRef("refs/4", "ref: refs/5\n");
+ writeLooseRef("refs/5", "ref: refs/end\n");
+ writeLooseRef("refs/end", A);
+
+ r = refdir.getRef("1");
+ assertEquals("refs/1", r.getName());
+ assertEquals(A, r.getObjectId());
+ assertTrue(r.isSymbolic());
+
+ writeLooseRef("refs/5", "ref: refs/6\n");
+ writeLooseRef("refs/6", "ref: refs/end\n");
+
+ r = refdir.getRef("1");
+ assertNull("missing 1 due to cycle", r);
+
+ writeLooseRef("refs/heads/1", B);
+
+ r = refdir.getRef("1");
+ assertEquals("refs/heads/1", r.getName());
+ assertEquals(B, r.getObjectId());
+ assertFalse(r.isSymbolic());
+ }
+
+ @Test
public void testGetRefs_PackedNotPeeled_Sorted() throws IOException {
Map<String, Ref> all;
@@ -983,6 +1055,25 @@ public class RefDirectoryTest extends LocalDiskRepositoryTestCase {
}
@Test
+ public void testExactRef_EmptyDatabase() throws IOException {
+ Ref r;
+
+ r = refdir.exactRef(HEAD);
+ assertTrue(r.isSymbolic());
+ assertSame(LOOSE, r.getStorage());
+ assertEquals("refs/heads/master", r.getTarget().getName());
+ assertSame(NEW, r.getTarget().getStorage());
+ assertNull(r.getTarget().getObjectId());
+
+ assertNull(refdir.exactRef("refs/heads/master"));
+ assertNull(refdir.exactRef("refs/tags/v1.0"));
+ assertNull(refdir.exactRef("FETCH_HEAD"));
+ assertNull(refdir.exactRef("NOT.A.REF.NAME"));
+ assertNull(refdir.exactRef("master"));
+ assertNull(refdir.exactRef("v1.0"));
+ }
+
+ @Test
public void testGetRef_FetchHead() throws IOException {
// This is an odd special case where we need to make sure we read
// exactly the first 40 bytes of the file and nothing further on
@@ -1000,6 +1091,23 @@ public class RefDirectoryTest extends LocalDiskRepositoryTestCase {
}
@Test
+ public void testExactRef_FetchHead() throws IOException {
+ // This is an odd special case where we need to make sure we read
+ // exactly the first 40 bytes of the file and nothing further on
+ // that line, or the remainder of the file.
+ write(new File(diskRepo.getDirectory(), "FETCH_HEAD"), A.name()
+ + "\tnot-for-merge"
+ + "\tbranch 'master' of git://egit.eclipse.org/jgit\n");
+
+ Ref r = refdir.exactRef("FETCH_HEAD");
+ assertFalse(r.isSymbolic());
+ assertEquals(A, r.getObjectId());
+ assertEquals("FETCH_HEAD", r.getName());
+ assertFalse(r.isPeeled());
+ assertNull(r.getPeeledObjectId());
+ }
+
+ @Test
public void testGetRef_AnyHeadWithGarbage() throws IOException {
write(new File(diskRepo.getDirectory(), "refs/heads/A"), A.name()
+ "012345 . this is not a standard reference\n"
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
index 098b31f..4843418 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
@@ -347,7 +347,7 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
Result update = updateRef.update();
assertEquals(Result.FORCED, update);
assertEquals(ppid, db.resolve("HEAD"));
- Ref ref = db.getRef("HEAD");
+ Ref ref = db.exactRef("HEAD");
assertEquals("HEAD", ref.getName());
assertTrue("is detached", !ref.isSymbolic());
@@ -377,7 +377,7 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
Result update = updateRef.update();
assertEquals(Result.NEW, update);
assertEquals(ppid, db.resolve("HEAD"));
- Ref ref = db.getRef("HEAD");
+ Ref ref = db.exactRef("HEAD");
assertEquals("HEAD", ref.getName());
assertTrue("is detached", !ref.isSymbolic());
@@ -558,13 +558,15 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
assertEquals(ppid, db.resolve("refs/heads/master"));
// real test
- RevCommit old = new RevWalk(db).parseCommit(ppid);
- RefUpdate updateRef2 = db.updateRef("refs/heads/master");
- updateRef2.setExpectedOldObjectId(old);
- updateRef2.setNewObjectId(pid);
- Result update2 = updateRef2.update();
- assertEquals(Result.FAST_FORWARD, update2);
- assertEquals(pid, db.resolve("refs/heads/master"));
+ try (RevWalk rw = new RevWalk(db)) {
+ RevCommit old = rw.parseCommit(ppid);
+ RefUpdate updateRef2 = db.updateRef("refs/heads/master");
+ updateRef2.setExpectedOldObjectId(old);
+ updateRef2.setNewObjectId(pid);
+ Result update2 = updateRef2.update();
+ assertEquals(Result.FAST_FORWARD, update2);
+ assertEquals(pid, db.resolve("refs/heads/master"));
+ }
}
/**
@@ -681,13 +683,13 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
public void testRenameBranchAlsoInPack() throws IOException {
ObjectId rb = db.resolve("refs/heads/b");
ObjectId rb2 = db.resolve("refs/heads/b~1");
- assertEquals(Ref.Storage.PACKED, db.getRef("refs/heads/b").getStorage());
+ assertEquals(Ref.Storage.PACKED, db.exactRef("refs/heads/b").getStorage());
RefUpdate updateRef = db.updateRef("refs/heads/b");
updateRef.setNewObjectId(rb2);
updateRef.setForceUpdate(true);
Result update = updateRef.update();
assertEquals("internal check new ref is loose", Result.FORCED, update);
- assertEquals(Ref.Storage.LOOSE, db.getRef("refs/heads/b").getStorage());
+ assertEquals(Ref.Storage.LOOSE, db.exactRef("refs/heads/b").getStorage());
writeReflog(db, rb, "Just a message", "refs/heads/b");
assertTrue("log on old branch", new File(db.getDirectory(),
"logs/refs/heads/b").exists());
@@ -707,9 +709,10 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
// Create new Repository instance, to reread caches and make sure our
// assumptions are persistent.
- Repository ndb = new FileRepository(db.getDirectory());
- assertEquals(rb2, ndb.resolve("refs/heads/new/name"));
- assertNull(ndb.resolve("refs/heads/b"));
+ try (Repository ndb = new FileRepository(db.getDirectory())) {
+ assertEquals(rb2, ndb.resolve("refs/heads/new/name"));
+ assertNull(ndb.resolve("refs/heads/b"));
+ }
}
public void tryRenameWhenLocked(String toLock, String fromName,
@@ -751,7 +754,7 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
assertNull(db.resolve(toName));
assertEquals(oldFromLog.toString(), db.getReflogReader(fromName)
.getReverseEntries().toString());
- if (oldHeadId != null)
+ if (oldHeadId != null && oldHeadLog != null)
assertEquals(oldHeadLog.toString(), db.getReflogReader(
Constants.HEAD).getReverseEntries().toString());
} finally {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
index 0ab013b..f4d655f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/T0003_BasicTest.java
@@ -67,7 +67,6 @@ import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
-import org.eclipse.jgit.lib.FileTreeEntry;
import org.eclipse.jgit.lib.ObjectDatabase;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
@@ -75,7 +74,6 @@ import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.TagBuilder;
-import org.eclipse.jgit.lib.Tree;
import org.eclipse.jgit.lib.TreeFormatter;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTag;
@@ -304,11 +302,12 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
// object (as it already exists in the pack).
//
final Repository newdb = createBareRepository();
- final ObjectInserter oi = newdb.newObjectInserter();
- final ObjectId treeId = oi.insert(new TreeFormatter());
- oi.release();
+ try (final ObjectInserter oi = newdb.newObjectInserter()) {
+ final ObjectId treeId = oi.insert(new TreeFormatter());
+ assertEquals("4b825dc642cb6eb9a060e54bf8d69288fbee4904",
+ treeId.name());
+ }
- assertEquals("4b825dc642cb6eb9a060e54bf8d69288fbee4904", treeId.name());
final File o = new File(new File(new File(newdb.getDirectory(),
"objects"), "4b"), "825dc642cb6eb9a060e54bf8d69288fbee4904");
assertTrue("Exists " + o, o.isFile());
@@ -363,6 +362,7 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
assertNotSame(db.getConfig(), db2.getConfig());
}
+ @SuppressWarnings("unused")
@Test
public void test008_FailOnWrongVersion() throws IOException {
final File cfg = new File(db.getDirectory(), Constants.CONFIG);
@@ -418,29 +418,6 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
}
@Test
- public void test012_SubtreeExternalSorting() throws IOException {
- final ObjectId emptyBlob = insertEmptyBlob();
- final Tree t = new Tree(db);
- final FileTreeEntry e0 = t.addFile("a-");
- final FileTreeEntry e1 = t.addFile("a-b");
- final FileTreeEntry e2 = t.addFile("a/b");
- final FileTreeEntry e3 = t.addFile("a=");
- final FileTreeEntry e4 = t.addFile("a=b");
-
- e0.setId(emptyBlob);
- e1.setId(emptyBlob);
- e2.setId(emptyBlob);
- e3.setId(emptyBlob);
- e4.setId(emptyBlob);
-
- final Tree a = (Tree) t.findTreeMember("a");
- a.setId(insertTree(a));
- assertEquals(ObjectId
- .fromString("b47a8f0a4190f7572e11212769090523e23eb1ea"),
- insertTree(t));
- }
-
- @Test
public void test020_createBlobTag() throws IOException {
final ObjectId emptyId = insertEmptyBlob();
final TagBuilder t = new TagBuilder();
@@ -463,9 +440,8 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
@Test
public void test021_createTreeTag() throws IOException {
final ObjectId emptyId = insertEmptyBlob();
- final Tree almostEmptyTree = new Tree(db);
- almostEmptyTree.addEntry(new FileTreeEntry(almostEmptyTree, emptyId,
- "empty".getBytes(), false));
+ TreeFormatter almostEmptyTree = new TreeFormatter();
+ almostEmptyTree.append("empty", FileMode.REGULAR_FILE, emptyId);
final ObjectId almostEmptyTreeId = insertTree(almostEmptyTree);
final TagBuilder t = new TagBuilder();
t.setObjectId(almostEmptyTreeId, Constants.OBJ_TREE);
@@ -487,9 +463,8 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
@Test
public void test022_createCommitTag() throws IOException {
final ObjectId emptyId = insertEmptyBlob();
- final Tree almostEmptyTree = new Tree(db);
- almostEmptyTree.addEntry(new FileTreeEntry(almostEmptyTree, emptyId,
- "empty".getBytes(), false));
+ TreeFormatter almostEmptyTree = new TreeFormatter();
+ almostEmptyTree.append("empty", FileMode.REGULAR_FILE, emptyId);
final ObjectId almostEmptyTreeId = insertTree(almostEmptyTree);
final CommitBuilder almostEmptyCommit = new CommitBuilder();
almostEmptyCommit.setAuthor(new PersonIdent(author, 1154236443000L,
@@ -519,9 +494,8 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
@Test
public void test023_createCommitNonAnullii() throws IOException {
final ObjectId emptyId = insertEmptyBlob();
- final Tree almostEmptyTree = new Tree(db);
- almostEmptyTree.addEntry(new FileTreeEntry(almostEmptyTree, emptyId,
- "empty".getBytes(), false));
+ TreeFormatter almostEmptyTree = new TreeFormatter();
+ almostEmptyTree.append("empty", FileMode.REGULAR_FILE, emptyId);
final ObjectId almostEmptyTreeId = insertTree(almostEmptyTree);
CommitBuilder commit = new CommitBuilder();
commit.setTreeId(almostEmptyTreeId);
@@ -541,9 +515,8 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
@Test
public void test024_createCommitNonAscii() throws IOException {
final ObjectId emptyId = insertEmptyBlob();
- final Tree almostEmptyTree = new Tree(db);
- almostEmptyTree.addEntry(new FileTreeEntry(almostEmptyTree, emptyId,
- "empty".getBytes(), false));
+ TreeFormatter almostEmptyTree = new TreeFormatter();
+ almostEmptyTree.append("empty", FileMode.REGULAR_FILE, emptyId);
final ObjectId almostEmptyTreeId = insertTree(almostEmptyTree);
CommitBuilder commit = new CommitBuilder();
commit.setTreeId(almostEmptyTreeId);
@@ -569,8 +542,7 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
@Test
public void test026_CreateCommitMultipleparents() throws IOException {
final ObjectId treeId;
- final ObjectInserter oi = db.newObjectInserter();
- try {
+ try (final ObjectInserter oi = db.newObjectInserter()) {
final ObjectId blobId = oi.insert(Constants.OBJ_BLOB,
"and this is the data in me\n".getBytes(Constants.CHARSET
.name()));
@@ -578,8 +550,6 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
fmt.append("i-am-a-file", FileMode.REGULAR_FILE, blobId);
treeId = oi.insert(fmt);
oi.flush();
- } finally {
- oi.release();
}
assertEquals(ObjectId
.fromString("00b1f73724f493096d1ffa0b0f1f1482dbb8c936"), treeId);
@@ -741,80 +711,51 @@ public class T0003_BasicTest extends SampleDataRepositoryTestCase {
private ObjectId insertEmptyBlob() throws IOException {
final ObjectId emptyId;
- ObjectInserter oi = db.newObjectInserter();
- try {
+ try (ObjectInserter oi = db.newObjectInserter()) {
emptyId = oi.insert(Constants.OBJ_BLOB, new byte[] {});
oi.flush();
- } finally {
- oi.release();
}
return emptyId;
}
- private ObjectId insertTree(Tree tree) throws IOException {
- ObjectInserter oi = db.newObjectInserter();
- try {
- ObjectId id = oi.insert(Constants.OBJ_TREE, tree.format());
- oi.flush();
- return id;
- } finally {
- oi.release();
- }
- }
-
private ObjectId insertTree(TreeFormatter tree) throws IOException {
- ObjectInserter oi = db.newObjectInserter();
- try {
+ try (ObjectInserter oi = db.newObjectInserter()) {
ObjectId id = oi.insert(tree);
oi.flush();
return id;
- } finally {
- oi.release();
}
}
private ObjectId insertCommit(final CommitBuilder builder)
throws IOException, UnsupportedEncodingException {
- ObjectInserter oi = db.newObjectInserter();
- try {
+ try (ObjectInserter oi = db.newObjectInserter()) {
ObjectId id = oi.insert(builder);
oi.flush();
return id;
- } finally {
- oi.release();
}
}
private RevCommit parseCommit(AnyObjectId id)
throws MissingObjectException, IncorrectObjectTypeException,
IOException {
- RevWalk rw = new RevWalk(db);
- try {
+ try (RevWalk rw = new RevWalk(db)) {
return rw.parseCommit(id);
- } finally {
- rw.release();
}
}
private ObjectId insertTag(final TagBuilder tag) throws IOException,
UnsupportedEncodingException {
- ObjectInserter oi = db.newObjectInserter();
- try {
+ try (ObjectInserter oi = db.newObjectInserter()) {
ObjectId id = oi.insert(tag);
oi.flush();
return id;
- } finally {
- oi.release();
}
}
private RevTag parseTag(AnyObjectId id) throws MissingObjectException,
IncorrectObjectTypeException, IOException {
- RevWalk rw = new RevWalk(db);
- try {
+ try (RevWalk rw = new RevWalk(db)) {
return rw.parseTag(id);
- } finally {
- rw.release();
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/UnpackedObjectTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/UnpackedObjectTest.java
index 10d2b6b..8c8c6c6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/UnpackedObjectTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/UnpackedObjectTest.java
@@ -108,7 +108,7 @@ public class UnpackedObjectTest extends LocalDiskRepositoryTestCase {
@After
public void tearDown() throws Exception {
if (wc != null)
- wc.release();
+ wc.close();
new WindowCacheConfig().install();
super.tearDown();
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/GcCommitSelectionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/GcCommitSelectionTest.java
new file mode 100644
index 0000000..5fda070
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/GcCommitSelectionTest.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.pack;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jgit.internal.storage.file.GcTestCase;
+import org.eclipse.jgit.internal.storage.file.PackBitmapIndexBuilder;
+import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
+import org.eclipse.jgit.internal.storage.pack.PackWriterBitmapPreparer;
+import org.eclipse.jgit.internal.storage.pack.PackWriterBitmapPreparer.BitmapCommit;
+import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
+import org.eclipse.jgit.junit.TestRepository.CommitBuilder;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.storage.pack.PackConfig;
+import org.junit.Test;
+
+public class GcCommitSelectionTest extends GcTestCase {
+
+ @Test
+ public void testBitmapSpansNoMerges() throws Exception {
+ /*
+ * Commit counts -> expected bitmap counts for history without merges.
+ * The top 100 contiguous commits should always have bitmaps, and the
+ * "recent" bitmaps beyond that are spaced out every 100-200 commits.
+ * (Starting at 100, the next 100 commits are searched for a merge
+ * commit. Since one is not found, the spacing between commits is 200.
+ */
+ int[][] bitmapCounts = { //
+ { 1, 1 }, { 50, 50 }, { 99, 99 }, { 100, 100 }, { 101, 100 },
+ { 200, 100 }, { 201, 100 }, { 299, 100 }, { 300, 101 },
+ { 301, 101 }, { 401, 101 }, { 499, 101 }, { 500, 102 }, };
+ int currentCommits = 0;
+ BranchBuilder bb = tr.branch("refs/heads/main");
+
+ for (int[] counts : bitmapCounts) {
+ int nextCommitCount = counts[0];
+ int expectedBitmapCount = counts[1];
+ assertTrue(nextCommitCount > currentCommits); // programming error
+ for (int i = currentCommits; i < nextCommitCount; i++) {
+ String str = "A" + i;
+ bb.commit().message(str).add(str, str).create();
+ }
+ currentCommits = nextCommitCount;
+
+ gc.setExpireAgeMillis(0); // immediately delete old packs
+ gc.gc();
+ assertEquals(currentCommits * 3, // commit/tree/object
+ gc.getStatistics().numberOfPackedObjects);
+ assertEquals(currentCommits + " commits: ", expectedBitmapCount,
+ gc.getStatistics().numberOfBitmaps);
+ }
+ }
+
+ @Test
+ public void testBitmapSpansWithMerges() throws Exception {
+ /*
+ * Commits that are merged. Since 55 is in the oldest history it is
+ * never considered. Searching goes from oldest to newest so 115 is the
+ * first merge commit found. After that the range 116-216 is ignored so
+ * 175 is never considered.
+ */
+ List<Integer> merges = Arrays.asList(Integer.valueOf(55),
+ Integer.valueOf(115), Integer.valueOf(175),
+ Integer.valueOf(235));
+ /*
+ * Commit counts -> expected bitmap counts for history with merges. The
+ * top 100 contiguous commits should always have bitmaps, and the
+ * "recent" bitmaps beyond that are spaced out every 100-200 commits.
+ * Merges in the < 100 range have no effect and merges in the > 100
+ * range will only be considered for commit counts > 200.
+ */
+ int[][] bitmapCounts = { //
+ { 1, 1 }, { 55, 55 }, { 56, 57 }, // +1 bitmap from branch A55
+ { 99, 100 }, // still +1 branch @55
+ { 100, 100 }, // 101 commits, only 100 newest
+ { 116, 100 }, // @55 still in 100 newest bitmaps
+ { 176, 101 }, // @55 branch tip is not in 100 newest
+ { 213, 101 }, // 216 commits, @115&@175 in 100 newest
+ { 214, 102 }, // @55 branch tip, merge @115, @177 in newest
+ { 236, 102 }, // all 4 merge points in history
+ { 273, 102 }, // 277 commits, @175&@235 in newest
+ { 274, 103 }, // @55, @115, merge @175, @235 in newest
+ { 334, 103 }, // @55, at 115, at 175, @235 in newest
+ { 335, 104 }, // @55, at 115, at 175, merge @235
+ { 435, 104 }, // @55, at 115, at 175, at 235 tips
+ { 436, 104 }, // force @236
+ };
+
+ int currentCommits = 0;
+ BranchBuilder bb = tr.branch("refs/heads/main");
+
+ for (int[] counts : bitmapCounts) {
+ int nextCommitCount = counts[0];
+ int expectedBitmapCount = counts[1];
+ assertTrue(nextCommitCount > currentCommits); // programming error
+ for (int i = currentCommits; i < nextCommitCount; i++) {
+ String str = "A" + i;
+ if (!merges.contains(Integer.valueOf(i))) {
+ bb.commit().message(str).add(str, str).create();
+ } else {
+ BranchBuilder bbN = tr.branch("refs/heads/A" + i);
+ bb.commit().message(str).add(str, str)
+ .parent(bbN.commit().create()).create();
+ }
+ }
+ currentCommits = nextCommitCount;
+
+ gc.setExpireAgeMillis(0); // immediately delete old packs
+ gc.gc();
+ assertEquals(currentCommits + " commits: ", expectedBitmapCount,
+ gc.getStatistics().numberOfBitmaps);
+ }
+ }
+
+ @Test
+ public void testBitmapsForExcessiveBranches() throws Exception {
+ int oneDayInSeconds = 60 * 60 * 24;
+
+ // All of branch A is committed on day1
+ BranchBuilder bbA = tr.branch("refs/heads/A");
+ for (int i = 0; i < 1001; i++) {
+ String msg = "A" + i;
+ bbA.commit().message(msg).add(msg, msg).create();
+ }
+ // All of in branch B is committed on day91
+ tr.tick(oneDayInSeconds * 90);
+ BranchBuilder bbB = tr.branch("refs/heads/B");
+ for (int i = 0; i < 1001; i++) {
+ String msg = "B" + i;
+ bbB.commit().message(msg).add(msg, msg).create();
+ }
+ // Create 100 other branches with a single commit
+ for (int i = 0; i < 100; i++) {
+ BranchBuilder bb = tr.branch("refs/heads/N" + i);
+ String msg = "singlecommit" + i;
+ bb.commit().message(msg).add(msg, msg).create();
+ }
+ // now is day92
+ tr.tick(oneDayInSeconds);
+
+ // Since there are no merges, commits in recent history are selected
+ // every 200 commits.
+ final int commitsForSparseBranch = 1 + (1001 / 200);
+ final int commitsForFullBranch = 100 + (901 / 200);
+ final int commitsForShallowBranches = 100;
+
+ // Excessive branch history pruning, one old branch.
+ gc.setExpireAgeMillis(0); // immediately delete old packs
+ gc.gc();
+ assertEquals(
+ commitsForSparseBranch + commitsForFullBranch
+ + commitsForShallowBranches,
+ gc.getStatistics().numberOfBitmaps);
+ }
+
+ @Test
+ public void testSelectionOrderingWithChains() throws Exception {
+ /*-
+ * Create a history like this, where 'N' is the number of seconds from
+ * the first commit in the branch:
+ *
+ * ---o---o---o commits b3,b5,b7
+ * / \
+ * o--o--o---o---o---o--o commits m0,m1,m2,m4,m6,m8,m9
+ */
+ BranchBuilder bb = tr.branch("refs/heads/main");
+ RevCommit m0 = addCommit(bb, "m0");
+ RevCommit m1 = addCommit(bb, "m1", m0);
+ RevCommit m2 = addCommit(bb, "m2", m1);
+ RevCommit b3 = addCommit(bb, "b3", m1);
+ RevCommit m4 = addCommit(bb, "m4", m2);
+ RevCommit b5 = addCommit(bb, "m5", b3);
+ RevCommit m6 = addCommit(bb, "m6", m4);
+ RevCommit b7 = addCommit(bb, "m7", b5);
+ RevCommit m8 = addCommit(bb, "m8", m6, b7);
+ RevCommit m9 = addCommit(bb, "m9", m8);
+
+ List<RevCommit> commits = Arrays.asList(m0, m1, m2, b3, m4, b5, m6, b7,
+ m8, m9);
+ PackWriterBitmapPreparer preparer = newPeparer(m9, commits);
+ List<BitmapCommit> selection = new ArrayList<>(
+ preparer.selectCommits(commits.size()));
+
+ // Verify that the output is ordered by the separate "chains"
+ String[] expected = { m0.name(), m1.name(), m2.name(), m4.name(),
+ m6.name(), m8.name(), m9.name(), b3.name(), b5.name(),
+ b7.name() };
+ assertEquals(expected.length, selection.size());
+ for (int i = 0; i < expected.length; i++) {
+ assertEquals("Entry " + i, expected[i], selection.get(i).getName());
+ }
+ }
+
+ private RevCommit addCommit(BranchBuilder bb, String msg,
+ RevCommit... parents) throws Exception {
+ CommitBuilder commit = bb.commit().message(msg).add(msg, msg).tick(1)
+ .noParents();
+ for (RevCommit parent : parents) {
+ commit.parent(parent);
+ }
+ return commit.create();
+ }
+
+ private PackWriterBitmapPreparer newPeparer(RevCommit want,
+ List<RevCommit> commits)
+ throws IOException {
+ List<ObjectToPack> objects = new ArrayList<>(commits.size());
+ for (RevCommit commit : commits) {
+ objects.add(new ObjectToPack(commit, Constants.OBJ_COMMIT));
+ }
+ Set<ObjectId> wants = Collections.singleton((ObjectId) want);
+ PackConfig config = new PackConfig();
+ PackBitmapIndexBuilder builder = new PackBitmapIndexBuilder(objects);
+ return new PackWriterBitmapPreparer(
+ tr.getRepository().newObjectReader(), builder,
+ NullProgressMonitor.INSTANCE, wants, config);
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparerTest.java
new file mode 100644
index 0000000..b0f92ff
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparerTest.java
@@ -0,0 +1,136 @@
+package org.eclipse.jgit.internal.storage.pack;
+
+import static org.eclipse.jgit.storage.pack.PackConfig.DEFAULT_BITMAP_DISTANT_COMMIT_SPAN;
+import static org.eclipse.jgit.storage.pack.PackConfig.DEFAULT_BITMAP_RECENT_COMMIT_COUNT;
+import static org.eclipse.jgit.storage.pack.PackConfig.DEFAULT_BITMAP_RECENT_COMMIT_SPAN;
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.storage.file.PackBitmapIndexBuilder;
+import org.eclipse.jgit.lib.AbbreviatedObjectId;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.storage.pack.PackConfig;
+import org.junit.Test;
+
+/** Tests for the {@link PackWriterBitmapPreparer}. */
+public class PackWriterBitmapPreparerTest {
+ private static class StubObjectReader extends ObjectReader {
+ @Override
+ public ObjectReader newReader() {
+ return null;
+ }
+
+ @Override
+ public Collection<ObjectId> resolve(AbbreviatedObjectId id)
+ throws IOException {
+ return null;
+ }
+
+ @Override
+ public ObjectLoader open(AnyObjectId objectId, int typeHint)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException {
+ return null;
+ }
+
+ @Override
+ public Set<ObjectId> getShallowCommits() throws IOException {
+ return null;
+ }
+
+ @Override
+ public void close() {
+ // stub
+ }
+ }
+
+ @Test
+ public void testNextSelectionDistanceForActiveBranch() throws Exception {
+ PackWriterBitmapPreparer preparer = newPeparer(
+ DEFAULT_BITMAP_RECENT_COMMIT_COUNT, // 20000
+ DEFAULT_BITMAP_RECENT_COMMIT_SPAN, // 100
+ DEFAULT_BITMAP_DISTANT_COMMIT_SPAN); // 5000
+ int[][] distancesAndSpans = { { 0, 100 }, { 100, 100 }, { 10000, 100 },
+ { 20000, 100 }, { 20100, 100 }, { 20102, 102 }, { 20200, 200 },
+ { 22200, 2200 }, { 24999, 4999 }, { 25000, 5000 },
+ { 50000, 5000 }, { 1000000, 5000 }, };
+
+ for (int[] pair : distancesAndSpans) {
+ assertEquals(pair[1], preparer.nextSpan(pair[0]));
+ }
+ }
+
+ @Test
+ public void testNextSelectionDistanceWithFewerRecentCommits()
+ throws Exception {
+ PackWriterBitmapPreparer preparer = newPeparer(1000,
+ DEFAULT_BITMAP_RECENT_COMMIT_SPAN, // 100
+ DEFAULT_BITMAP_DISTANT_COMMIT_SPAN); // 5000
+ int[][] distancesAndSpans = { { 0, 100 }, { 100, 100 }, { 1000, 100 },
+ { 1100, 100 }, { 1111, 111 }, { 2000, 1000 }, { 5999, 4999 },
+ { 6000, 5000 }, { 10000, 5000 }, { 50000, 5000 },
+ { 1000000, 5000 } };
+
+ for (int[] pair : distancesAndSpans) {
+ assertEquals(pair[1], preparer.nextSpan(pair[0]));
+ }
+ }
+
+ @Test
+ public void testNextSelectionDistanceWithSmallerRecentSpan()
+ throws Exception {
+ PackWriterBitmapPreparer preparer = newPeparer(
+ DEFAULT_BITMAP_RECENT_COMMIT_COUNT, // 20000
+ 10, // recent span
+ DEFAULT_BITMAP_DISTANT_COMMIT_SPAN); // 5000
+ int[][] distancesAndSpans = { { 0, 10 }, { 100, 10 }, { 10000, 10 },
+ { 20000, 10 }, { 20010, 10 }, { 20012, 12 }, { 20050, 50 },
+ { 20200, 200 }, { 22200, 2200 }, { 24999, 4999 },
+ { 25000, 5000 }, { 50000, 5000 }, { 1000000, 5000 } };
+
+ for (int[] pair : distancesAndSpans) {
+ assertEquals(pair[1], preparer.nextSpan(pair[0]));
+ }
+ }
+
+ @Test
+ public void testNextSelectionDistanceWithSmallerDistantSpan()
+ throws Exception {
+ PackWriterBitmapPreparer preparer = newPeparer(
+ DEFAULT_BITMAP_RECENT_COMMIT_COUNT, // 20000
+ DEFAULT_BITMAP_RECENT_COMMIT_SPAN, // 100
+ 1000);
+ int[][] distancesAndSpans = { { 0, 100 }, { 100, 100 }, { 10000, 100 },
+ { 20000, 100 }, { 20100, 100 }, { 20102, 102 }, { 20200, 200 },
+ { 20999, 999 }, { 21000, 1000 }, { 22000, 1000 },
+ { 25000, 1000 }, { 50000, 1000 }, { 1000000, 1000 } };
+
+ for (int[] pair : distancesAndSpans) {
+ assertEquals(pair[1], preparer.nextSpan(pair[0]));
+ }
+ }
+
+ private PackWriterBitmapPreparer newPeparer(int recentCount, int recentSpan,
+ int distantSpan) throws IOException {
+ List<ObjectToPack> objects = Collections.emptyList();
+ Set<ObjectId> wants = Collections.emptySet();
+ PackConfig config = new PackConfig();
+ config.setBitmapRecentCommitCount(recentCount);
+ config.setBitmapRecentCommitSpan(recentSpan);
+ config.setBitmapDistantCommitSpan(distantSpan);
+ PackBitmapIndexBuilder indexBuilder = new PackBitmapIndexBuilder(
+ objects);
+ return new PackWriterBitmapPreparer(new StubObjectReader(),
+ indexBuilder, null, wants, config);
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabaseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabaseTest.java
new file mode 100644
index 0000000..020d1b1
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabaseTest.java
@@ -0,0 +1,685 @@
+/*
+ * Copyright (C) 2010, 2013, 2016 Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.reftree;
+
+import static org.eclipse.jgit.lib.Constants.HEAD;
+import static org.eclipse.jgit.lib.Constants.R_HEADS;
+import static org.eclipse.jgit.lib.Constants.R_TAGS;
+import static org.eclipse.jgit.lib.Ref.Storage.LOOSE;
+import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
+import static org.eclipse.jgit.lib.RefDatabase.ALL;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.LOCK_FAILURE;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_NONFASTFORWARD;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.SymbolicRef;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevTag;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
+import org.junit.Before;
+import org.junit.Test;
+
+public class RefTreeDatabaseTest {
+ private InMemRefTreeRepo repo;
+ private RefTreeDatabase refdb;
+ private RefDatabase bootstrap;
+
+ private TestRepository<InMemRefTreeRepo> testRepo;
+ private RevCommit A;
+ private RevCommit B;
+ private RevTag v1_0;
+
+ @Before
+ public void setUp() throws Exception {
+ repo = new InMemRefTreeRepo(new DfsRepositoryDescription("test"));
+ bootstrap = refdb.getBootstrap();
+
+ testRepo = new TestRepository<>(repo);
+ A = testRepo.commit().create();
+ B = testRepo.commit(testRepo.getRevWalk().parseCommit(A));
+ v1_0 = testRepo.tag("v1_0", B);
+ testRepo.getRevWalk().parseBody(v1_0);
+ }
+
+ @Test
+ public void testSupportsAtomic() {
+ assertTrue(refdb.performsAtomicTransactions());
+ }
+
+ @Test
+ public void testGetRefs_EmptyDatabase() throws IOException {
+ assertTrue("no references", refdb.getRefs(ALL).isEmpty());
+ assertTrue("no references", refdb.getRefs(R_HEADS).isEmpty());
+ assertTrue("no references", refdb.getRefs(R_TAGS).isEmpty());
+ }
+
+ @Test
+ public void testGetRefs_HeadOnOneBranch() throws IOException {
+ symref(HEAD, "refs/heads/master");
+ update("refs/heads/master", A);
+
+ Map<String, Ref> all = refdb.getRefs(ALL);
+ assertEquals(2, all.size());
+ assertTrue("has HEAD", all.containsKey(HEAD));
+ assertTrue("has master", all.containsKey("refs/heads/master"));
+
+ Ref head = all.get(HEAD);
+ Ref master = all.get("refs/heads/master");
+
+ assertEquals(HEAD, head.getName());
+ assertTrue(head.isSymbolic());
+ assertSame(LOOSE, head.getStorage());
+ assertSame("uses same ref as target", master, head.getTarget());
+
+ assertEquals("refs/heads/master", master.getName());
+ assertFalse(master.isSymbolic());
+ assertSame(PACKED, master.getStorage());
+ assertEquals(A, master.getObjectId());
+ }
+
+ @Test
+ public void testGetRefs_DetachedHead() throws IOException {
+ update(HEAD, A);
+
+ Map<String, Ref> all = refdb.getRefs(ALL);
+ assertEquals(1, all.size());
+ assertTrue("has HEAD", all.containsKey(HEAD));
+
+ Ref head = all.get(HEAD);
+ assertEquals(HEAD, head.getName());
+ assertFalse(head.isSymbolic());
+ assertSame(PACKED, head.getStorage());
+ assertEquals(A, head.getObjectId());
+ }
+
+ @Test
+ public void testGetRefs_DeeplyNestedBranch() throws IOException {
+ String name = "refs/heads/a/b/c/d/e/f/g/h/i/j/k";
+ update(name, A);
+
+ Map<String, Ref> all = refdb.getRefs(ALL);
+ assertEquals(1, all.size());
+
+ Ref r = all.get(name);
+ assertEquals(name, r.getName());
+ assertFalse(r.isSymbolic());
+ assertSame(PACKED, r.getStorage());
+ assertEquals(A, r.getObjectId());
+ }
+
+ @Test
+ public void testGetRefs_HeadBranchNotBorn() throws IOException {
+ update("refs/heads/A", A);
+ update("refs/heads/B", B);
+
+ Map<String, Ref> all = refdb.getRefs(ALL);
+ assertEquals(2, all.size());
+ assertFalse("no HEAD", all.containsKey(HEAD));
+
+ Ref a = all.get("refs/heads/A");
+ Ref b = all.get("refs/heads/B");
+
+ assertEquals(A, a.getObjectId());
+ assertEquals(B, b.getObjectId());
+
+ assertEquals("refs/heads/A", a.getName());
+ assertEquals("refs/heads/B", b.getName());
+ }
+
+ @Test
+ public void testGetRefs_HeadsOnly() throws IOException {
+ update("refs/heads/A", A);
+ update("refs/heads/B", B);
+ update("refs/tags/v1.0", v1_0);
+
+ Map<String, Ref> heads = refdb.getRefs(R_HEADS);
+ assertEquals(2, heads.size());
+
+ Ref a = heads.get("A");
+ Ref b = heads.get("B");
+
+ assertEquals("refs/heads/A", a.getName());
+ assertEquals("refs/heads/B", b.getName());
+
+ assertEquals(A, a.getObjectId());
+ assertEquals(B, b.getObjectId());
+ }
+
+ @Test
+ public void testGetRefs_TagsOnly() throws IOException {
+ update("refs/heads/A", A);
+ update("refs/heads/B", B);
+ update("refs/tags/v1.0", v1_0);
+
+ Map<String, Ref> tags = refdb.getRefs(R_TAGS);
+ assertEquals(1, tags.size());
+
+ Ref a = tags.get("v1.0");
+ assertEquals("refs/tags/v1.0", a.getName());
+ assertEquals(v1_0, a.getObjectId());
+ assertTrue(a.isPeeled());
+ assertEquals(v1_0.getObject(), a.getPeeledObjectId());
+ }
+
+ @Test
+ public void testGetRefs_HeadsSymref() throws IOException {
+ symref("refs/heads/other", "refs/heads/master");
+ update("refs/heads/master", A);
+
+ Map<String, Ref> heads = refdb.getRefs(R_HEADS);
+ assertEquals(2, heads.size());
+
+ Ref master = heads.get("master");
+ Ref other = heads.get("other");
+
+ assertEquals("refs/heads/master", master.getName());
+ assertEquals(A, master.getObjectId());
+
+ assertEquals("refs/heads/other", other.getName());
+ assertEquals(A, other.getObjectId());
+ assertSame(master, other.getTarget());
+ }
+
+ @Test
+ public void testGetRefs_InvalidPrefixes() throws IOException {
+ update("refs/heads/A", A);
+
+ assertTrue("empty refs/heads", refdb.getRefs("refs/heads").isEmpty());
+ assertTrue("empty objects", refdb.getRefs("objects").isEmpty());
+ assertTrue("empty objects/", refdb.getRefs("objects/").isEmpty());
+ }
+
+ @Test
+ public void testGetRefs_DiscoversNew() throws IOException {
+ update("refs/heads/master", A);
+ Map<String, Ref> orig = refdb.getRefs(ALL);
+
+ update("refs/heads/next", B);
+ Map<String, Ref> next = refdb.getRefs(ALL);
+
+ assertEquals(1, orig.size());
+ assertEquals(2, next.size());
+
+ assertFalse(orig.containsKey("refs/heads/next"));
+ assertTrue(next.containsKey("refs/heads/next"));
+
+ assertEquals(A, next.get("refs/heads/master").getObjectId());
+ assertEquals(B, next.get("refs/heads/next").getObjectId());
+ }
+
+ @Test
+ public void testGetRefs_DiscoversModified() throws IOException {
+ symref(HEAD, "refs/heads/master");
+ update("refs/heads/master", A);
+
+ Map<String, Ref> all = refdb.getRefs(ALL);
+ assertEquals(A, all.get(HEAD).getObjectId());
+
+ update("refs/heads/master", B);
+ all = refdb.getRefs(ALL);
+ assertEquals(B, all.get(HEAD).getObjectId());
+ assertEquals(B, refdb.exactRef(HEAD).getObjectId());
+ }
+
+ @Test
+ public void testGetRefs_CycleInSymbolicRef() throws IOException {
+ symref("refs/1", "refs/2");
+ symref("refs/2", "refs/3");
+ symref("refs/3", "refs/4");
+ symref("refs/4", "refs/5");
+ symref("refs/5", "refs/end");
+ update("refs/end", A);
+
+ Map<String, Ref> all = refdb.getRefs(ALL);
+ Ref r = all.get("refs/1");
+ assertNotNull("has 1", r);
+
+ assertEquals("refs/1", r.getName());
+ assertEquals(A, r.getObjectId());
+ assertTrue(r.isSymbolic());
+
+ r = r.getTarget();
+ assertEquals("refs/2", r.getName());
+ assertEquals(A, r.getObjectId());
+ assertTrue(r.isSymbolic());
+
+ r = r.getTarget();
+ assertEquals("refs/3", r.getName());
+ assertEquals(A, r.getObjectId());
+ assertTrue(r.isSymbolic());
+
+ r = r.getTarget();
+ assertEquals("refs/4", r.getName());
+ assertEquals(A, r.getObjectId());
+ assertTrue(r.isSymbolic());
+
+ r = r.getTarget();
+ assertEquals("refs/5", r.getName());
+ assertEquals(A, r.getObjectId());
+ assertTrue(r.isSymbolic());
+
+ r = r.getTarget();
+ assertEquals("refs/end", r.getName());
+ assertEquals(A, r.getObjectId());
+ assertFalse(r.isSymbolic());
+
+ symref("refs/5", "refs/6");
+ symref("refs/6", "refs/end");
+ all = refdb.getRefs(ALL);
+ assertNull("mising 1 due to cycle", all.get("refs/1"));
+ assertEquals(A, all.get("refs/2").getObjectId());
+ assertEquals(A, all.get("refs/3").getObjectId());
+ assertEquals(A, all.get("refs/4").getObjectId());
+ assertEquals(A, all.get("refs/5").getObjectId());
+ assertEquals(A, all.get("refs/6").getObjectId());
+ assertEquals(A, all.get("refs/end").getObjectId());
+ }
+
+ @Test
+ public void testGetRef_NonExistingBranchConfig() throws IOException {
+ assertNull("find branch config", refdb.getRef("config"));
+ assertNull("find branch config", refdb.getRef("refs/heads/config"));
+ }
+
+ @Test
+ public void testGetRef_FindBranchConfig() throws IOException {
+ update("refs/heads/config", A);
+
+ for (String t : new String[] { "config", "refs/heads/config" }) {
+ Ref r = refdb.getRef(t);
+ assertNotNull("find branch config (" + t + ")", r);
+ assertEquals("for " + t, "refs/heads/config", r.getName());
+ assertEquals("for " + t, A, r.getObjectId());
+ }
+ }
+
+ @Test
+ public void testFirstExactRef() throws IOException {
+ update("refs/heads/A", A);
+ update("refs/tags/v1.0", v1_0);
+
+ Ref a = refdb.firstExactRef("refs/heads/A", "refs/tags/v1.0");
+ Ref one = refdb.firstExactRef("refs/tags/v1.0", "refs/heads/A");
+
+ assertEquals("refs/heads/A", a.getName());
+ assertEquals("refs/tags/v1.0", one.getName());
+
+ assertEquals(A, a.getObjectId());
+ assertEquals(v1_0, one.getObjectId());
+ }
+
+ @Test
+ public void testExactRef_DiscoversModified() throws IOException {
+ symref(HEAD, "refs/heads/master");
+ update("refs/heads/master", A);
+ assertEquals(A, refdb.exactRef(HEAD).getObjectId());
+
+ update("refs/heads/master", B);
+ assertEquals(B, refdb.exactRef(HEAD).getObjectId());
+ }
+
+ @Test
+ public void testIsNameConflicting() throws IOException {
+ update("refs/heads/a/b", A);
+ update("refs/heads/q", B);
+
+ // new references cannot replace an existing container
+ assertTrue(refdb.isNameConflicting("refs"));
+ assertTrue(refdb.isNameConflicting("refs/heads"));
+ assertTrue(refdb.isNameConflicting("refs/heads/a"));
+
+ // existing reference is not conflicting
+ assertFalse(refdb.isNameConflicting("refs/heads/a/b"));
+
+ // new references are not conflicting
+ assertFalse(refdb.isNameConflicting("refs/heads/a/d"));
+ assertFalse(refdb.isNameConflicting("refs/heads/master"));
+
+ // existing reference must not be used as a container
+ assertTrue(refdb.isNameConflicting("refs/heads/a/b/c"));
+ assertTrue(refdb.isNameConflicting("refs/heads/q/master"));
+
+ // refs/txn/ names always conflict.
+ assertTrue(refdb.isNameConflicting(refdb.getTxnCommitted()));
+ assertTrue(refdb.isNameConflicting("refs/txn/foo"));
+ }
+
+ @Test
+ public void testUpdate_RefusesRefsTxnNamespace() throws IOException {
+ ObjectId txnId = getTxnCommitted();
+
+ RefUpdate u = refdb.newUpdate("refs/txn/tmp", false);
+ u.setNewObjectId(B);
+ assertEquals(RefUpdate.Result.LOCK_FAILURE, u.update());
+ assertEquals(txnId, getTxnCommitted());
+
+ ReceiveCommand cmd = command(null, B, "refs/txn/tmp");
+ BatchRefUpdate batch = refdb.newBatchUpdate();
+ batch.addCommand(cmd);
+ batch.execute(new RevWalk(repo), NullProgressMonitor.INSTANCE);
+
+ assertEquals(REJECTED_OTHER_REASON, cmd.getResult());
+ assertEquals(MessageFormat.format(JGitText.get().invalidRefName,
+ "refs/txn/tmp"), cmd.getMessage());
+ assertEquals(txnId, getTxnCommitted());
+ }
+
+ @Test
+ public void testUpdate_RefusesDotLockInRefName() throws IOException {
+ ObjectId txnId = getTxnCommitted();
+
+ RefUpdate u = refdb.newUpdate("refs/heads/pu.lock", false);
+ u.setNewObjectId(B);
+ assertEquals(RefUpdate.Result.REJECTED, u.update());
+ assertEquals(txnId, getTxnCommitted());
+
+ ReceiveCommand cmd = command(null, B, "refs/heads/pu.lock");
+ BatchRefUpdate batch = refdb.newBatchUpdate();
+ batch.addCommand(cmd);
+ batch.execute(new RevWalk(repo), NullProgressMonitor.INSTANCE);
+
+ assertEquals(REJECTED_OTHER_REASON, cmd.getResult());
+ assertEquals(JGitText.get().funnyRefname, cmd.getMessage());
+ assertEquals(txnId, getTxnCommitted());
+ }
+
+ @Test
+ public void testBatchRefUpdate_NonFastForwardAborts() throws IOException {
+ update("refs/heads/master", A);
+ update("refs/heads/masters", B);
+ ObjectId txnId = getTxnCommitted();
+
+ List<ReceiveCommand> commands = Arrays.asList(
+ command(A, B, "refs/heads/master"),
+ command(B, A, "refs/heads/masters"));
+ BatchRefUpdate batchUpdate = refdb.newBatchUpdate();
+ batchUpdate.addCommand(commands);
+ batchUpdate.execute(new RevWalk(repo), NullProgressMonitor.INSTANCE);
+ assertEquals(txnId, getTxnCommitted());
+
+ assertEquals(REJECTED_NONFASTFORWARD,
+ commands.get(1).getResult());
+ assertEquals(REJECTED_OTHER_REASON,
+ commands.get(0).getResult());
+ assertEquals(JGitText.get().transactionAborted,
+ commands.get(0).getMessage());
+ }
+
+ @Test
+ public void testBatchRefUpdate_ForceUpdate() throws IOException {
+ update("refs/heads/master", A);
+ update("refs/heads/masters", B);
+ ObjectId txnId = getTxnCommitted();
+
+ List<ReceiveCommand> commands = Arrays.asList(
+ command(A, B, "refs/heads/master"),
+ command(B, A, "refs/heads/masters"));
+ BatchRefUpdate batchUpdate = refdb.newBatchUpdate();
+ batchUpdate.setAllowNonFastForwards(true);
+ batchUpdate.addCommand(commands);
+ batchUpdate.execute(new RevWalk(repo), NullProgressMonitor.INSTANCE);
+ assertNotEquals(txnId, getTxnCommitted());
+
+ Map<String, Ref> refs = refdb.getRefs(ALL);
+ assertEquals(OK, commands.get(0).getResult());
+ assertEquals(OK, commands.get(1).getResult());
+ assertEquals(
+ "[refs/heads/master, refs/heads/masters]",
+ refs.keySet().toString());
+ assertEquals(B.getId(), refs.get("refs/heads/master").getObjectId());
+ assertEquals(A.getId(), refs.get("refs/heads/masters").getObjectId());
+ }
+
+ @Test
+ public void testBatchRefUpdate_NonFastForwardDoesNotDoExpensiveMergeCheck()
+ throws IOException {
+ update("refs/heads/master", B);
+ ObjectId txnId = getTxnCommitted();
+
+ List<ReceiveCommand> commands = Arrays.asList(
+ command(B, A, "refs/heads/master"));
+ BatchRefUpdate batchUpdate = refdb.newBatchUpdate();
+ batchUpdate.setAllowNonFastForwards(true);
+ batchUpdate.addCommand(commands);
+ batchUpdate.execute(new RevWalk(repo) {
+ @Override
+ public boolean isMergedInto(RevCommit base, RevCommit tip) {
+ fail("isMergedInto() should not be called");
+ return false;
+ }
+ }, NullProgressMonitor.INSTANCE);
+ assertNotEquals(txnId, getTxnCommitted());
+
+ Map<String, Ref> refs = refdb.getRefs(ALL);
+ assertEquals(OK, commands.get(0).getResult());
+ assertEquals(A.getId(), refs.get("refs/heads/master").getObjectId());
+ }
+
+ @Test
+ public void testBatchRefUpdate_ConflictCausesAbort() throws IOException {
+ update("refs/heads/master", A);
+ update("refs/heads/masters", B);
+ ObjectId txnId = getTxnCommitted();
+
+ List<ReceiveCommand> commands = Arrays.asList(
+ command(A, B, "refs/heads/master"),
+ command(null, A, "refs/heads/master/x"),
+ command(null, A, "refs/heads"));
+ BatchRefUpdate batchUpdate = refdb.newBatchUpdate();
+ batchUpdate.setAllowNonFastForwards(true);
+ batchUpdate.addCommand(commands);
+ batchUpdate.execute(new RevWalk(repo), NullProgressMonitor.INSTANCE);
+ assertEquals(txnId, getTxnCommitted());
+
+ assertEquals(LOCK_FAILURE, commands.get(0).getResult());
+
+ assertEquals(REJECTED_OTHER_REASON, commands.get(1).getResult());
+ assertEquals(JGitText.get().transactionAborted,
+ commands.get(1).getMessage());
+
+ assertEquals(REJECTED_OTHER_REASON, commands.get(2).getResult());
+ assertEquals(JGitText.get().transactionAborted,
+ commands.get(2).getMessage());
+ }
+
+ @Test
+ public void testBatchRefUpdate_NoConflictIfDeleted() throws IOException {
+ update("refs/heads/master", A);
+ update("refs/heads/masters", B);
+ ObjectId txnId = getTxnCommitted();
+
+ List<ReceiveCommand> commands = Arrays.asList(
+ command(A, B, "refs/heads/master"),
+ command(null, A, "refs/heads/masters/x"),
+ command(B, null, "refs/heads/masters"));
+ BatchRefUpdate batchUpdate = refdb.newBatchUpdate();
+ batchUpdate.setAllowNonFastForwards(true);
+ batchUpdate.addCommand(commands);
+ batchUpdate.execute(new RevWalk(repo), NullProgressMonitor.INSTANCE);
+ assertNotEquals(txnId, getTxnCommitted());
+
+ assertEquals(OK, commands.get(0).getResult());
+ assertEquals(OK, commands.get(1).getResult());
+ assertEquals(OK, commands.get(2).getResult());
+
+ Map<String, Ref> refs = refdb.getRefs(ALL);
+ assertEquals(
+ "[refs/heads/master, refs/heads/masters/x]",
+ refs.keySet().toString());
+ assertEquals(A.getId(), refs.get("refs/heads/masters/x").getObjectId());
+ }
+
+ private ObjectId getTxnCommitted() throws IOException {
+ Ref r = bootstrap.exactRef(refdb.getTxnCommitted());
+ if (r != null && r.getObjectId() != null) {
+ return r.getObjectId();
+ }
+ return ObjectId.zeroId();
+ }
+
+ private static ReceiveCommand command(AnyObjectId a, AnyObjectId b,
+ String name) {
+ return new ReceiveCommand(
+ a != null ? a.copy() : ObjectId.zeroId(),
+ b != null ? b.copy() : ObjectId.zeroId(),
+ name);
+ }
+
+ private void symref(final String name, final String dst)
+ throws IOException {
+ commit(new Function() {
+ @Override
+ public boolean apply(ObjectReader reader, RefTree tree)
+ throws IOException {
+ Ref old = tree.exactRef(reader, name);
+ Command n = new Command(
+ old,
+ new SymbolicRef(
+ name,
+ new ObjectIdRef.Unpeeled(Ref.Storage.NEW, dst, null)));
+ return tree.apply(Collections.singleton(n));
+ }
+ });
+ }
+
+ private void update(final String name, final ObjectId id)
+ throws IOException {
+ commit(new Function() {
+ @Override
+ public boolean apply(ObjectReader reader, RefTree tree)
+ throws IOException {
+ Ref old = tree.exactRef(reader, name);
+ Command n;
+ try (RevWalk rw = new RevWalk(repo)) {
+ n = new Command(old, Command.toRef(rw, id, name, true));
+ }
+ return tree.apply(Collections.singleton(n));
+ }
+ });
+ }
+
+ interface Function {
+ boolean apply(ObjectReader reader, RefTree tree) throws IOException;
+ }
+
+ private void commit(Function fun) throws IOException {
+ try (ObjectReader reader = repo.newObjectReader();
+ ObjectInserter inserter = repo.newObjectInserter();
+ RevWalk rw = new RevWalk(reader)) {
+ RefUpdate u = bootstrap.newUpdate(refdb.getTxnCommitted(), false);
+ CommitBuilder cb = new CommitBuilder();
+ testRepo.setAuthorAndCommitter(cb);
+
+ Ref ref = bootstrap.exactRef(refdb.getTxnCommitted());
+ RefTree tree;
+ if (ref != null && ref.getObjectId() != null) {
+ tree = RefTree.read(reader, rw.parseTree(ref.getObjectId()));
+ cb.setParentId(ref.getObjectId());
+ u.setExpectedOldObjectId(ref.getObjectId());
+ } else {
+ tree = RefTree.newEmptyTree();
+ u.setExpectedOldObjectId(ObjectId.zeroId());
+ }
+
+ assertTrue(fun.apply(reader, tree));
+ cb.setTreeId(tree.writeTree(inserter));
+ u.setNewObjectId(inserter.insert(cb));
+ inserter.flush();
+ switch (u.update(rw)) {
+ case NEW:
+ case FAST_FORWARD:
+ break;
+ default:
+ fail("Expected " + u.getName() + " to update");
+ }
+ }
+ }
+
+ private class InMemRefTreeRepo extends InMemoryRepository {
+ private final RefTreeDatabase refs;
+
+ InMemRefTreeRepo(DfsRepositoryDescription repoDesc) {
+ super(repoDesc);
+ refs = new RefTreeDatabase(this, super.getRefDatabase(),
+ "refs/txn/committed");
+ RefTreeDatabaseTest.this.refdb = refs;
+ }
+
+ public RefDatabase getRefDatabase() {
+ return refs;
+ }
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeTest.java
new file mode 100644
index 0000000..8e0f38c
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftree/RefTreeTest.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.reftree;
+
+import static org.eclipse.jgit.lib.Constants.HEAD;
+import static org.eclipse.jgit.lib.Constants.R_HEADS;
+import static org.eclipse.jgit.lib.Constants.R_TAGS;
+import static org.eclipse.jgit.lib.Ref.Storage.LOOSE;
+import static org.eclipse.jgit.lib.Ref.Storage.NEW;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.LOCK_FAILURE;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.SymbolicRef;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.revwalk.RevTag;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
+import org.junit.Before;
+import org.junit.Test;
+
+public class RefTreeTest {
+ private static final String R_MASTER = R_HEADS + "master";
+ private InMemoryRepository repo;
+ private TestRepository<InMemoryRepository> git;
+
+ @Before
+ public void setUp() throws IOException {
+ repo = new InMemoryRepository(new DfsRepositoryDescription("RefTree"));
+ git = new TestRepository<>(repo);
+ }
+
+ @Test
+ public void testEmptyTree() throws IOException {
+ RefTree tree = RefTree.newEmptyTree();
+ try (ObjectReader reader = repo.newObjectReader()) {
+ assertNull(HEAD, tree.exactRef(reader, HEAD));
+ assertNull("master", tree.exactRef(reader, R_MASTER));
+ }
+ }
+
+ @Test
+ public void testApplyThenReadMaster() throws Exception {
+ RefTree tree = RefTree.newEmptyTree();
+ RevBlob id = git.blob("A");
+ Command cmd = new Command(null, ref(R_MASTER, id));
+ assertTrue(tree.apply(Collections.singletonList(cmd)));
+ assertSame(NOT_ATTEMPTED, cmd.getResult());
+
+ try (ObjectReader reader = repo.newObjectReader()) {
+ Ref m = tree.exactRef(reader, R_MASTER);
+ assertNotNull(R_MASTER, m);
+ assertEquals(R_MASTER, m.getName());
+ assertEquals(id, m.getObjectId());
+ assertTrue("peeled", m.isPeeled());
+ }
+ }
+
+ @Test
+ public void testUpdateMaster() throws Exception {
+ RefTree tree = RefTree.newEmptyTree();
+ RevBlob id1 = git.blob("A");
+ Command cmd1 = new Command(null, ref(R_MASTER, id1));
+ assertTrue(tree.apply(Collections.singletonList(cmd1)));
+ assertSame(NOT_ATTEMPTED, cmd1.getResult());
+
+ RevBlob id2 = git.blob("B");
+ Command cmd2 = new Command(ref(R_MASTER, id1), ref(R_MASTER, id2));
+ assertTrue(tree.apply(Collections.singletonList(cmd2)));
+ assertSame(NOT_ATTEMPTED, cmd2.getResult());
+
+ try (ObjectReader reader = repo.newObjectReader()) {
+ Ref m = tree.exactRef(reader, R_MASTER);
+ assertNotNull(R_MASTER, m);
+ assertEquals(R_MASTER, m.getName());
+ assertEquals(id2, m.getObjectId());
+ assertTrue("peeled", m.isPeeled());
+ }
+ }
+
+ @Test
+ public void testHeadSymref() throws Exception {
+ RefTree tree = RefTree.newEmptyTree();
+ RevBlob id = git.blob("A");
+ Command cmd1 = new Command(null, ref(R_MASTER, id));
+ Command cmd2 = new Command(null, symref(HEAD, R_MASTER));
+ assertTrue(tree.apply(Arrays.asList(new Command[] { cmd1, cmd2 })));
+ assertSame(NOT_ATTEMPTED, cmd1.getResult());
+ assertSame(NOT_ATTEMPTED, cmd2.getResult());
+
+ try (ObjectReader reader = repo.newObjectReader()) {
+ Ref m = tree.exactRef(reader, HEAD);
+ assertNotNull(HEAD, m);
+ assertEquals(HEAD, m.getName());
+ assertTrue("symbolic", m.isSymbolic());
+ assertNotNull(m.getTarget());
+ assertEquals(R_MASTER, m.getTarget().getName());
+ assertEquals(id, m.getTarget().getObjectId());
+ }
+
+ // Writing flushes some buffers, re-read from blob.
+ ObjectId newId = write(tree);
+ try (ObjectReader reader = repo.newObjectReader();
+ RevWalk rw = new RevWalk(reader)) {
+ tree = RefTree.read(reader, rw.parseTree(newId));
+ Ref m = tree.exactRef(reader, HEAD);
+ assertEquals(R_MASTER, m.getTarget().getName());
+ }
+ }
+
+ @Test
+ public void testTagIsPeeled() throws Exception {
+ String name = "v1.0";
+ RefTree tree = RefTree.newEmptyTree();
+ RevBlob id = git.blob("A");
+ RevTag tag = git.tag(name, id);
+
+ String ref = R_TAGS + name;
+ Command cmd = create(ref, tag);
+ assertTrue(tree.apply(Collections.singletonList(cmd)));
+ assertSame(NOT_ATTEMPTED, cmd.getResult());
+
+ try (ObjectReader reader = repo.newObjectReader()) {
+ Ref m = tree.exactRef(reader, ref);
+ assertNotNull(ref, m);
+ assertEquals(ref, m.getName());
+ assertEquals(tag, m.getObjectId());
+ assertTrue("peeled", m.isPeeled());
+ assertEquals(id, m.getPeeledObjectId());
+ }
+ }
+
+ @Test
+ public void testApplyAlreadyExists() throws Exception {
+ RefTree tree = RefTree.newEmptyTree();
+ RevBlob a = git.blob("A");
+ Command cmd = new Command(null, ref(R_MASTER, a));
+ assertTrue(tree.apply(Collections.singletonList(cmd)));
+ ObjectId treeId = write(tree);
+
+ RevBlob b = git.blob("B");
+ Command cmd1 = create(R_MASTER, b);
+ Command cmd2 = create(R_MASTER, b);
+ assertFalse(tree.apply(Arrays.asList(new Command[] { cmd1, cmd2 })));
+ assertSame(LOCK_FAILURE, cmd1.getResult());
+ assertSame(REJECTED_OTHER_REASON, cmd2.getResult());
+ assertEquals(JGitText.get().transactionAborted, cmd2.getMessage());
+ assertEquals(treeId, write(tree));
+ }
+
+ @Test
+ public void testApplyWrongOldId() throws Exception {
+ RefTree tree = RefTree.newEmptyTree();
+ RevBlob a = git.blob("A");
+ Command cmd = new Command(null, ref(R_MASTER, a));
+ assertTrue(tree.apply(Collections.singletonList(cmd)));
+ ObjectId treeId = write(tree);
+
+ RevBlob b = git.blob("B");
+ RevBlob c = git.blob("C");
+ Command cmd1 = update(R_MASTER, b, c);
+ Command cmd2 = create(R_MASTER, b);
+ assertFalse(tree.apply(Arrays.asList(new Command[] { cmd1, cmd2 })));
+ assertSame(LOCK_FAILURE, cmd1.getResult());
+ assertSame(REJECTED_OTHER_REASON, cmd2.getResult());
+ assertEquals(JGitText.get().transactionAborted, cmd2.getMessage());
+ assertEquals(treeId, write(tree));
+ }
+
+ @Test
+ public void testApplyWrongOldIdButAlreadyCurrentIsNoOp() throws Exception {
+ RefTree tree = RefTree.newEmptyTree();
+ RevBlob a = git.blob("A");
+ Command cmd = new Command(null, ref(R_MASTER, a));
+ assertTrue(tree.apply(Collections.singletonList(cmd)));
+ ObjectId treeId = write(tree);
+
+ RevBlob b = git.blob("B");
+ cmd = update(R_MASTER, b, a);
+ assertTrue(tree.apply(Collections.singletonList(cmd)));
+ assertEquals(treeId, write(tree));
+ }
+
+ @Test
+ public void testApplyCannotCreateSubdirectory() throws Exception {
+ RefTree tree = RefTree.newEmptyTree();
+ RevBlob a = git.blob("A");
+ Command cmd = new Command(null, ref(R_MASTER, a));
+ assertTrue(tree.apply(Collections.singletonList(cmd)));
+ ObjectId treeId = write(tree);
+
+ RevBlob b = git.blob("B");
+ Command cmd1 = create(R_MASTER + "/fail", b);
+ assertFalse(tree.apply(Collections.singletonList(cmd1)));
+ assertSame(LOCK_FAILURE, cmd1.getResult());
+ assertEquals(treeId, write(tree));
+ }
+
+ @Test
+ public void testApplyCannotCreateParentRef() throws Exception {
+ RefTree tree = RefTree.newEmptyTree();
+ RevBlob a = git.blob("A");
+ Command cmd = new Command(null, ref(R_MASTER, a));
+ assertTrue(tree.apply(Collections.singletonList(cmd)));
+ ObjectId treeId = write(tree);
+
+ RevBlob b = git.blob("B");
+ Command cmd1 = create("refs/heads", b);
+ assertFalse(tree.apply(Collections.singletonList(cmd1)));
+ assertSame(LOCK_FAILURE, cmd1.getResult());
+ assertEquals(treeId, write(tree));
+ }
+
+ private static Ref ref(String name, ObjectId id) {
+ return new ObjectIdRef.PeeledNonTag(LOOSE, name, id);
+ }
+
+ private static Ref symref(String name, String dest) {
+ Ref d = new ObjectIdRef.PeeledNonTag(NEW, dest, null);
+ return new SymbolicRef(name, d);
+ }
+
+ private Command create(String name, ObjectId id)
+ throws MissingObjectException, IOException {
+ return update(name, ObjectId.zeroId(), id);
+ }
+
+ private Command update(String name, ObjectId oldId, ObjectId newId)
+ throws MissingObjectException, IOException {
+ try (RevWalk rw = new RevWalk(repo)) {
+ return new Command(rw, new ReceiveCommand(oldId, newId, name));
+ }
+ }
+
+ private ObjectId write(RefTree tree) throws IOException {
+ try (ObjectInserter ins = repo.newObjectInserter()) {
+ ObjectId id = tree.writeTree(ins);
+ ins.flush();
+ return id;
+ }
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/junit/TestRepositoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/junit/TestRepositoryTest.java
new file mode 100644
index 0000000..8b54dab
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/junit/TestRepositoryTest.java
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.junit;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Date;
+import java.util.regex.Pattern;
+
+import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestRepositoryTest {
+ private TestRepository<InMemoryRepository> tr;
+ private InMemoryRepository repo;
+ private RevWalk rw;
+
+ @Before
+ public void setUp() throws Exception {
+ tr = new TestRepository<>(new InMemoryRepository(
+ new DfsRepositoryDescription("test")));
+ repo = tr.getRepository();
+ rw = tr.getRevWalk();
+ }
+
+ @After
+ public void tearDown() {
+ rw.close();
+ repo.close();
+ }
+
+ @Test
+ public void insertChangeId() throws Exception {
+ RevCommit c1 = tr.commit().message("message").insertChangeId().create();
+ rw.parseBody(c1);
+ assertTrue(Pattern.matches(
+ "^message\n\nChange-Id: I[0-9a-f]{40}\n$", c1.getFullMessage()));
+
+ RevCommit c2 = tr.commit().message("").insertChangeId().create();
+ rw.parseBody(c2);
+ assertEquals("\n\nChange-Id: I0000000000000000000000000000000000000000\n",
+ c2.getFullMessage());
+ }
+
+ @Test
+ public void insertChangeIdIgnoresExisting() throws Exception {
+ String msg = "message\n"
+ + "\n"
+ + "Change-Id: Ideadbeefdeadbeefdeadbeefdeadbeefdeadbeef\n";
+ RevCommit c = tr.commit().message(msg).insertChangeId().create();
+ rw.parseBody(c);
+ assertEquals(msg, c.getFullMessage());
+ }
+
+ @Test
+ public void insertExplicitChangeId() throws Exception {
+ RevCommit c = tr.commit().message("message")
+ .insertChangeId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")
+ .create();
+ rw.parseBody(c);
+ assertEquals("message\n\n"
+ + "Change-Id: Ideadbeefdeadbeefdeadbeefdeadbeefdeadbeef\n"
+ , c.getFullMessage());
+ }
+
+ @Test
+ public void resetFromSymref() throws Exception {
+ repo.updateRef("HEAD").link("refs/heads/master");
+ Ref head = repo.exactRef("HEAD");
+ RevCommit master = tr.branch("master").commit().create();
+ RevCommit branch = tr.branch("branch").commit().create();
+ RevCommit detached = tr.commit().create();
+
+ assertTrue(head.isSymbolic());
+ assertEquals("refs/heads/master", head.getTarget().getName());
+ assertEquals(master, repo.exactRef("refs/heads/master").getObjectId());
+ assertEquals(branch, repo.exactRef("refs/heads/branch").getObjectId());
+
+ // Reset to branches preserves symref.
+ tr.reset("master");
+ head = repo.exactRef("HEAD");
+ assertEquals(master, head.getObjectId());
+ assertTrue(head.isSymbolic());
+ assertEquals("refs/heads/master", head.getTarget().getName());
+
+ tr.reset("branch");
+ head = repo.exactRef("HEAD");
+ assertEquals(branch, head.getObjectId());
+ assertTrue(head.isSymbolic());
+ assertEquals("refs/heads/master", head.getTarget().getName());
+ ObjectId lastHeadBeforeDetach = head.getObjectId().copy();
+
+ // Reset to a SHA-1 detaches.
+ tr.reset(detached);
+ head = repo.exactRef("HEAD");
+ assertEquals(detached, head.getObjectId());
+ assertFalse(head.isSymbolic());
+
+ tr.reset(detached.name());
+ head = repo.exactRef("HEAD");
+ assertEquals(detached, head.getObjectId());
+ assertFalse(head.isSymbolic());
+
+ // Reset back to a branch remains detached.
+ tr.reset("master");
+ head = repo.exactRef("HEAD");
+ assertEquals(lastHeadBeforeDetach, head.getObjectId());
+ assertFalse(head.isSymbolic());
+ }
+
+ @Test
+ public void resetFromDetachedHead() throws Exception {
+ Ref head = repo.exactRef("HEAD");
+ RevCommit master = tr.branch("master").commit().create();
+ RevCommit branch = tr.branch("branch").commit().create();
+ RevCommit detached = tr.commit().create();
+
+ assertNull(head);
+ assertEquals(master, repo.exactRef("refs/heads/master").getObjectId());
+ assertEquals(branch, repo.exactRef("refs/heads/branch").getObjectId());
+
+ tr.reset("master");
+ head = repo.exactRef("HEAD");
+ assertEquals(master, head.getObjectId());
+ assertFalse(head.isSymbolic());
+
+ tr.reset("branch");
+ head = repo.exactRef("HEAD");
+ assertEquals(branch, head.getObjectId());
+ assertFalse(head.isSymbolic());
+
+ tr.reset(detached);
+ head = repo.exactRef("HEAD");
+ assertEquals(detached, head.getObjectId());
+ assertFalse(head.isSymbolic());
+
+ tr.reset(detached.name());
+ head = repo.exactRef("HEAD");
+ assertEquals(detached, head.getObjectId());
+ assertFalse(head.isSymbolic());
+ }
+
+ @Test
+ public void amendRef() throws Exception {
+ RevCommit root = tr.commit()
+ .add("todelete", "to be deleted")
+ .create();
+ RevCommit orig = tr.commit().parent(root)
+ .rm("todelete")
+ .add("foo", "foo contents")
+ .add("bar", "bar contents")
+ .add("dir/baz", "baz contents")
+ .create();
+ rw.parseBody(orig);
+ tr.branch("master").update(orig);
+ assertEquals("foo contents", blobAsString(orig, "foo"));
+ assertEquals("bar contents", blobAsString(orig, "bar"));
+ assertEquals("baz contents", blobAsString(orig, "dir/baz"));
+
+ RevCommit amended = tr.amendRef("master")
+ .tick(3)
+ .add("bar", "fixed bar contents")
+ .create();
+ assertEquals(amended, repo.exactRef("refs/heads/master").getObjectId());
+ rw.parseBody(amended);
+
+ assertEquals(1, amended.getParentCount());
+ assertEquals(root, amended.getParent(0));
+ assertEquals(orig.getFullMessage(), amended.getFullMessage());
+ assertEquals(orig.getAuthorIdent(), amended.getAuthorIdent());
+
+ // Committer name/email is the same, but time was incremented.
+ assertEquals(new PersonIdent(orig.getCommitterIdent(), new Date(0)),
+ new PersonIdent(amended.getCommitterIdent(), new Date(0)));
+ assertTrue(orig.getCommitTime() < amended.getCommitTime());
+
+ assertEquals("foo contents", blobAsString(amended, "foo"));
+ assertEquals("fixed bar contents", blobAsString(amended, "bar"));
+ assertEquals("baz contents", blobAsString(amended, "dir/baz"));
+ assertNull(TreeWalk.forPath(repo, "todelete", amended.getTree()));
+ }
+
+ @Test
+ public void amendHead() throws Exception {
+ repo.updateRef("HEAD").link("refs/heads/master");
+ RevCommit root = tr.commit()
+ .add("foo", "foo contents")
+ .create();
+ RevCommit orig = tr.commit().parent(root)
+ .message("original message")
+ .add("bar", "bar contents")
+ .create();
+ tr.branch("master").update(orig);
+
+ RevCommit amended = tr.amendRef("HEAD")
+ .add("foo", "fixed foo contents")
+ .create();
+
+ Ref head = repo.exactRef(Constants.HEAD);
+ assertEquals(amended, head.getObjectId());
+ assertTrue(head.isSymbolic());
+ assertEquals("refs/heads/master", head.getTarget().getName());
+
+ rw.parseBody(amended);
+ assertEquals("original message", amended.getFullMessage());
+ assertEquals("fixed foo contents", blobAsString(amended, "foo"));
+ assertEquals("bar contents", blobAsString(amended, "bar"));
+ }
+
+ @Test
+ public void amendCommit() throws Exception {
+ RevCommit root = tr.commit()
+ .add("foo", "foo contents")
+ .create();
+ RevCommit orig = tr.commit().parent(root)
+ .message("original message")
+ .add("bar", "bar contents")
+ .create();
+ RevCommit amended = tr.amend(orig.copy())
+ .add("foo", "fixed foo contents")
+ .create();
+
+ rw.parseBody(amended);
+ assertEquals("original message", amended.getFullMessage());
+ assertEquals("fixed foo contents", blobAsString(amended, "foo"));
+ assertEquals("bar contents", blobAsString(amended, "bar"));
+ }
+
+ @Test
+ public void commitToUnbornHead() throws Exception {
+ repo.updateRef("HEAD").link("refs/heads/master");
+ RevCommit root = tr.branch("HEAD").commit().create();
+ Ref ref = repo.exactRef(Constants.HEAD);
+ assertEquals(root, ref.getObjectId());
+ assertTrue(ref.isSymbolic());
+ assertEquals("refs/heads/master", ref.getTarget().getName());
+ }
+
+ @Test
+ public void cherryPick() throws Exception {
+ repo.updateRef("HEAD").link("refs/heads/master");
+ RevCommit head = tr.branch("master").commit()
+ .add("foo", "foo contents\n")
+ .create();
+ rw.parseBody(head);
+ RevCommit toPick = tr.commit()
+ .parent(tr.commit().create()) // Can't cherry-pick root.
+ .author(new PersonIdent("Cherrypick Author", "cpa at example.com",
+ tr.getDate(), tr.getTimeZone()))
+ .author(new PersonIdent("Cherrypick Committer", "cpc at example.com",
+ tr.getDate(), tr.getTimeZone()))
+ .message("message to cherry-pick")
+ .add("bar", "bar contents\n")
+ .create();
+ RevCommit result = tr.cherryPick(toPick);
+ rw.parseBody(result);
+
+ Ref headRef = tr.getRepository().exactRef("HEAD");
+ assertEquals(result, headRef.getObjectId());
+ assertTrue(headRef.isSymbolic());
+ assertEquals("refs/heads/master", headRef.getLeaf().getName());
+
+ assertEquals(1, result.getParentCount());
+ assertEquals(head, result.getParent(0));
+ assertEquals(toPick.getAuthorIdent(), result.getAuthorIdent());
+
+ // Committer name/email matches default, and time was incremented.
+ assertEquals(new PersonIdent(head.getCommitterIdent(), new Date(0)),
+ new PersonIdent(result.getCommitterIdent(), new Date(0)));
+ assertTrue(toPick.getCommitTime() < result.getCommitTime());
+
+ assertEquals("message to cherry-pick", result.getFullMessage());
+ assertEquals("foo contents\n", blobAsString(result, "foo"));
+ assertEquals("bar contents\n", blobAsString(result, "bar"));
+ }
+
+ @Test
+ public void cherryPickWithContentMerge() throws Exception {
+ RevCommit base = tr.branch("HEAD").commit()
+ .add("foo", "foo contents\n\n")
+ .create();
+ tr.branch("HEAD").commit()
+ .add("foo", "foo contents\n\nlast line\n")
+ .create();
+ RevCommit toPick = tr.commit()
+ .message("message to cherry-pick")
+ .parent(base)
+ .add("foo", "changed foo contents\n\n")
+ .create();
+ RevCommit result = tr.cherryPick(toPick);
+ rw.parseBody(result);
+
+ assertEquals("message to cherry-pick", result.getFullMessage());
+ assertEquals("changed foo contents\n\nlast line\n",
+ blobAsString(result, "foo"));
+ }
+
+ @Test
+ public void cherryPickWithIdenticalContents() throws Exception {
+ RevCommit base = tr.branch("HEAD").commit().add("foo", "foo contents\n")
+ .create();
+ RevCommit head = tr.branch("HEAD").commit()
+ .parent(base)
+ .add("bar", "bar contents\n")
+ .create();
+ RevCommit toPick = tr.commit()
+ .parent(base)
+ .message("message to cherry-pick")
+ .add("bar", "bar contents\n")
+ .create();
+ assertNotEquals(head, toPick);
+ assertNull(tr.cherryPick(toPick));
+ assertEquals(head, repo.exactRef("HEAD").getObjectId());
+ }
+
+ private String blobAsString(AnyObjectId treeish, String path)
+ throws Exception {
+ RevObject obj = tr.get(rw.parseTree(treeish), path);
+ assertSame(RevBlob.class, obj.getClass());
+ ObjectLoader loader = rw.getObjectReader().open(obj);
+ return new String(loader.getCachedBytes(), UTF_8);
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
index db31fd3..6238a35 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
@@ -51,7 +51,6 @@ package org.eclipse.jgit.lib;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
@@ -503,27 +502,6 @@ public class ConfigTest {
}
@Test
- public void testEmptyString() throws ConfigInvalidException {
- Config c = parse("[my]\n\tempty =\n");
- assertNull(c.getString("my", null, "empty"));
-
- String[] values = c.getStringList("my", null, "empty");
- assertNotNull(values);
- assertEquals(1, values.length);
- assertNull(values[0]);
-
- // always matches the default, because its non-boolean
- assertTrue(c.getBoolean("my", "empty", true));
- assertFalse(c.getBoolean("my", "empty", false));
-
- assertEquals("[my]\n\tempty =\n", c.toText());
-
- c = new Config();
- c.setStringList("my", null, "empty", Arrays.asList(values));
- assertEquals("[my]\n\tempty =\n", c.toText());
- }
-
- @Test
public void testUnsetBranchSection() throws ConfigInvalidException {
Config c = parse("" //
+ "[branch \"keep\"]\n"
@@ -699,6 +677,68 @@ public class ConfigTest {
assertEquals("1", c.getString("a", null, "y"));
}
+ @Test
+ public void testExplicitlySetEmptyString() throws Exception {
+ Config c = new Config();
+ c.setString("a", null, "x", "0");
+ c.setString("a", null, "y", "");
+
+ assertEquals("0", c.getString("a", null, "x"));
+ assertEquals(0, c.getInt("a", null, "x", 1));
+
+ assertEquals("", c.getString("a", null, "y"));
+ assertArrayEquals(new String[]{""}, c.getStringList("a", null, "y"));
+ try {
+ c.getInt("a", null, "y", 1);
+ } catch (IllegalArgumentException e) {
+ assertEquals("Invalid integer value: a.y=", e.getMessage());
+ }
+
+ assertNull(c.getString("a", null, "z"));
+ assertArrayEquals(new String[]{}, c.getStringList("a", null, "z"));
+ }
+
+ @Test
+ public void testParsedEmptyString() throws Exception {
+ Config c = parse("[a]\n"
+ + "x = 0\n"
+ + "y =\n");
+
+ assertEquals("0", c.getString("a", null, "x"));
+ assertEquals(0, c.getInt("a", null, "x", 1));
+
+ assertNull(c.getString("a", null, "y"));
+ assertArrayEquals(new String[]{null}, c.getStringList("a", null, "y"));
+ try {
+ c.getInt("a", null, "y", 1);
+ } catch (IllegalArgumentException e) {
+ assertEquals("Invalid integer value: a.y=", e.getMessage());
+ }
+
+ assertNull(c.getString("a", null, "z"));
+ assertArrayEquals(new String[]{}, c.getStringList("a", null, "z"));
+ }
+
+ @Test
+ public void testSetStringListWithEmptyValue() throws Exception {
+ Config c = new Config();
+ c.setStringList("a", null, "x", Arrays.asList(""));
+ assertArrayEquals(new String[]{""}, c.getStringList("a", null, "x"));
+ }
+
+ @Test
+ public void testEmptyValueAtEof() throws Exception {
+ String text = "[a]\nx =";
+ Config c = parse(text);
+ assertNull(c.getString("a", null, "x"));
+ assertArrayEquals(new String[]{null},
+ c.getStringList("a", null, "x"));
+ c = parse(text + "\n");
+ assertNull(c.getString("a", null, "x"));
+ assertArrayEquals(new String[]{null},
+ c.getStringList("a", null, "x"));
+ }
+
private static void assertReadLong(long exp) throws ConfigInvalidException {
assertReadLong(exp, String.valueOf(exp));
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
index 48debae..92901f8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
@@ -78,6 +78,8 @@ import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.FileUtils;
+import org.junit.Assume;
import org.junit.Test;
public class DirCacheCheckoutTest extends RepositoryTestCase {
@@ -111,7 +113,7 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
return dco.getRemoved();
}
- private Map<String, ObjectId> getUpdated() {
+ private Map<String, String> getUpdated() {
return dco.getUpdated();
}
@@ -266,8 +268,6 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
@Test
public void testRules1thru3_NoIndexEntry() throws IOException {
ObjectId head = buildTree(mk("foo"));
- TreeWalk tw = TreeWalk.forPath(db, "foo", head);
- ObjectId objectId = tw.getObjectId(0);
ObjectId merge = db.newObjectInserter().insert(Constants.OBJ_TREE,
new byte[0]);
@@ -277,10 +277,9 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
prescanTwoTrees(merge, head);
- assertEquals(objectId, getUpdated().get("foo"));
+ assertTrue(getUpdated().containsKey("foo"));
merge = buildTree(mkmap("foo", "a"));
- tw = TreeWalk.forPath(db, "foo", merge);
prescanTwoTrees(head, merge);
@@ -356,15 +355,12 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
}
ObjectId genSha1(String data) {
- ObjectInserter w = db.newObjectInserter();
- try {
+ try (ObjectInserter w = db.newObjectInserter()) {
ObjectId id = w.insert(Constants.OBJ_BLOB, data.getBytes());
w.flush();
return id;
} catch (IOException e) {
fail(e.toString());
- } finally {
- w.release();
}
return null;
}
@@ -926,6 +922,301 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
}
@Test
+ public void testCheckoutChangeLinkToEmptyDir() throws Exception {
+ Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
+ String fname = "was_file";
+ Git git = Git.wrap(db);
+
+ // Add a file
+ writeTrashFile(fname, "a");
+ git.add().addFilepattern(fname).call();
+
+ // Add a link to file
+ String linkName = "link";
+ File link = writeLink(linkName, fname).toFile();
+ git.add().addFilepattern(linkName).call();
+ git.commit().setMessage("Added file and link").call();
+
+ assertWorkDir(mkmap(linkName, "a", fname, "a"));
+
+ // replace link with empty directory
+ FileUtils.delete(link);
+ FileUtils.mkdir(link);
+ assertTrue("Link must be a directory now", link.isDirectory());
+
+ // modify file
+ writeTrashFile(fname, "b");
+ assertWorkDir(mkmap(fname, "b", linkName, "/"));
+
+ // revert both paths to HEAD state
+ git.checkout().setStartPoint(Constants.HEAD)
+ .addPath(fname).addPath(linkName).call();
+
+ assertWorkDir(mkmap(fname, "a", linkName, "a"));
+
+ Status st = git.status().call();
+ assertTrue(st.isClean());
+ }
+
+ @Test
+ public void testCheckoutChangeLinkToEmptyDirs() throws Exception {
+ Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
+ String fname = "was_file";
+ Git git = Git.wrap(db);
+
+ // Add a file
+ writeTrashFile(fname, "a");
+ git.add().addFilepattern(fname).call();
+
+ // Add a link to file
+ String linkName = "link";
+ File link = writeLink(linkName, fname).toFile();
+ git.add().addFilepattern(linkName).call();
+ git.commit().setMessage("Added file and link").call();
+
+ assertWorkDir(mkmap(linkName, "a", fname, "a"));
+
+ // replace link with directory containing only directories, no files
+ FileUtils.delete(link);
+ FileUtils.mkdirs(new File(link, "dummyDir"));
+ assertTrue("Link must be a directory now", link.isDirectory());
+
+ assertFalse("Must not delete non empty directory", link.delete());
+
+ // modify file
+ writeTrashFile(fname, "b");
+ assertWorkDir(mkmap(fname, "b", linkName + "/dummyDir", "/"));
+
+ // revert both paths to HEAD state
+ git.checkout().setStartPoint(Constants.HEAD)
+ .addPath(fname).addPath(linkName).call();
+
+ assertWorkDir(mkmap(fname, "a", linkName, "a"));
+
+ Status st = git.status().call();
+ assertTrue(st.isClean());
+ }
+
+ @Test
+ public void testCheckoutChangeLinkToNonEmptyDirs() throws Exception {
+ Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
+ String fname = "file";
+ Git git = Git.wrap(db);
+
+ // Add a file
+ writeTrashFile(fname, "a");
+ git.add().addFilepattern(fname).call();
+
+ // Add a link to file
+ String linkName = "link";
+ File link = writeLink(linkName, fname).toFile();
+ git.add().addFilepattern(linkName).call();
+ git.commit().setMessage("Added file and link").call();
+
+ assertWorkDir(mkmap(linkName, "a", fname, "a"));
+
+ // replace link with directory containing only directories, no files
+ FileUtils.delete(link);
+
+ // create but do not add a file in the new directory to the index
+ writeTrashFile(linkName + "/dir1", "file1", "c");
+
+ // create but do not add a file in the new directory to the index
+ writeTrashFile(linkName + "/dir2", "file2", "d");
+
+ assertTrue("File must be a directory now", link.isDirectory());
+ assertFalse("Must not delete non empty directory", link.delete());
+
+ // 2 extra files are created
+ assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c",
+ linkName + "/dir2/file2", "d"));
+
+ // revert path to HEAD state
+ git.checkout().setStartPoint(Constants.HEAD).addPath(linkName).call();
+
+ // expect only the one added to the index
+ assertWorkDir(mkmap(linkName, "a", fname, "a"));
+
+ Status st = git.status().call();
+ assertTrue(st.isClean());
+ }
+
+ @Test
+ public void testCheckoutChangeLinkToNonEmptyDirsAndNewIndexEntry()
+ throws Exception {
+ Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
+ String fname = "file";
+ Git git = Git.wrap(db);
+
+ // Add a file
+ writeTrashFile(fname, "a");
+ git.add().addFilepattern(fname).call();
+
+ // Add a link to file
+ String linkName = "link";
+ File link = writeLink(linkName, fname).toFile();
+ git.add().addFilepattern(linkName).call();
+ git.commit().setMessage("Added file and link").call();
+
+ assertWorkDir(mkmap(linkName, "a", fname, "a"));
+
+ // replace link with directory containing only directories, no files
+ FileUtils.delete(link);
+
+ // create and add a file in the new directory to the index
+ writeTrashFile(linkName + "/dir1", "file1", "c");
+ git.add().addFilepattern(linkName + "/dir1/file1").call();
+
+ // create but do not add a file in the new directory to the index
+ writeTrashFile(linkName + "/dir2", "file2", "d");
+
+ assertTrue("File must be a directory now", link.isDirectory());
+ assertFalse("Must not delete non empty directory", link.delete());
+
+ // 2 extra files are created
+ assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c",
+ linkName + "/dir2/file2", "d"));
+
+ // revert path to HEAD state
+ git.checkout().setStartPoint(Constants.HEAD).addPath(linkName).call();
+
+ // original file and link
+ assertWorkDir(mkmap(linkName, "a", fname, "a"));
+
+ Status st = git.status().call();
+ assertTrue(st.isClean());
+ }
+
+ @Test
+ public void testCheckoutChangeFileToEmptyDir() throws Exception {
+ String fname = "was_file";
+ Git git = Git.wrap(db);
+
+ // Add a file
+ File file = writeTrashFile(fname, "a");
+ git.add().addFilepattern(fname).call();
+ git.commit().setMessage("Added file").call();
+
+ // replace file with empty directory
+ FileUtils.delete(file);
+ FileUtils.mkdir(file);
+ assertTrue("File must be a directory now", file.isDirectory());
+
+ assertWorkDir(mkmap(fname, "/"));
+
+ // revert path to HEAD state
+ git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call();
+
+ assertWorkDir(mkmap(fname, "a"));
+
+ Status st = git.status().call();
+ assertTrue(st.isClean());
+ }
+
+ @Test
+ public void testCheckoutChangeFileToEmptyDirs() throws Exception {
+ String fname = "was_file";
+ Git git = Git.wrap(db);
+
+ // Add a file
+ File file = writeTrashFile(fname, "a");
+ git.add().addFilepattern(fname).call();
+ git.commit().setMessage("Added file").call();
+
+ // replace file with directory containing only directories, no files
+ FileUtils.delete(file);
+ FileUtils.mkdirs(new File(file, "dummyDir"));
+ assertTrue("File must be a directory now", file.isDirectory());
+ assertFalse("Must not delete non empty directory", file.delete());
+
+ assertWorkDir(mkmap(fname + "/dummyDir", "/"));
+
+ // revert path to HEAD state
+ git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call();
+
+ assertWorkDir(mkmap(fname, "a"));
+
+ Status st = git.status().call();
+ assertTrue(st.isClean());
+ }
+
+ @Test
+ public void testCheckoutChangeFileToNonEmptyDirs() throws Exception {
+ String fname = "was_file";
+ Git git = Git.wrap(db);
+
+ // Add a file
+ File file = writeTrashFile(fname, "a");
+ git.add().addFilepattern(fname).call();
+ git.commit().setMessage("Added file").call();
+
+ assertWorkDir(mkmap(fname, "a"));
+
+ // replace file with directory containing only directories, no files
+ FileUtils.delete(file);
+
+ // create but do not add a file in the new directory to the index
+ writeTrashFile(fname + "/dir1", "file1", "c");
+
+ // create but do not add a file in the new directory to the index
+ writeTrashFile(fname + "/dir2", "file2", "d");
+
+ assertTrue("File must be a directory now", file.isDirectory());
+ assertFalse("Must not delete non empty directory", file.delete());
+
+ // 2 extra files are created
+ assertWorkDir(
+ mkmap(fname + "/dir1/file1", "c", fname + "/dir2/file2", "d"));
+
+ // revert path to HEAD state
+ git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call();
+
+ // expect only the one added to the index
+ assertWorkDir(mkmap(fname, "a"));
+
+ Status st = git.status().call();
+ assertTrue(st.isClean());
+ }
+
+ @Test
+ public void testCheckoutChangeFileToNonEmptyDirsAndNewIndexEntry()
+ throws Exception {
+ String fname = "was_file";
+ Git git = Git.wrap(db);
+
+ // Add a file
+ File file = writeTrashFile(fname, "a");
+ git.add().addFilepattern(fname).call();
+ git.commit().setMessage("Added file").call();
+
+ assertWorkDir(mkmap(fname, "a"));
+
+ // replace file with directory containing only directories, no files
+ FileUtils.delete(file);
+
+ // create and add a file in the new directory to the index
+ writeTrashFile(fname + "/dir", "file1", "c");
+ git.add().addFilepattern(fname + "/dir/file1").call();
+
+ // create but do not add a file in the new directory to the index
+ writeTrashFile(fname + "/dir", "file2", "d");
+
+ assertTrue("File must be a directory now", file.isDirectory());
+ assertFalse("Must not delete non empty directory", file.delete());
+
+ // 2 extra files are created
+ assertWorkDir(
+ mkmap(fname + "/dir/file1", "c", fname + "/dir/file2", "d"));
+
+ // revert path to HEAD state
+ git.checkout().setStartPoint(Constants.HEAD).addPath(fname).call();
+ assertWorkDir(mkmap(fname, "a"));
+
+ Status st = git.status().call();
+ assertTrue(st.isClean());
+ }
+
+ @Test
public void testCheckoutOutChangesAutoCRLFfalse() throws IOException {
setupCase(mk("foo"), mkmap("foo/bar", "foo\nbar"), mk("foo"));
checkout();
@@ -986,6 +1277,14 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
}
@Test
+ public void testDontOverwriteEmptyFolder() throws IOException {
+ setupCase(mk("foo"), mk("foo"), mk("foo"));
+ FileUtils.mkdir(new File(db.getWorkTree(), "d"));
+ checkout();
+ assertWorkDir(mkmap("foo", "foo", "d", "/"));
+ }
+
+ @Test
public void testOverwriteUntrackedIgnoredFile() throws IOException,
GitAPIException {
String fname="file.txt";
@@ -1018,6 +1317,103 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
}
@Test
+ public void testOverwriteUntrackedFileModeChange()
+ throws IOException, GitAPIException {
+ String fname = "file.txt";
+ Git git = Git.wrap(db);
+
+ // Add a file
+ File file = writeTrashFile(fname, "a");
+ git.add().addFilepattern(fname).call();
+ git.commit().setMessage("create file").call();
+ assertWorkDir(mkmap(fname, "a"));
+
+ // Create branch
+ git.branchCreate().setName("side").call();
+
+ // Switch branches
+ git.checkout().setName("side").call();
+
+ // replace file with directory containing files
+ FileUtils.delete(file);
+
+ // create and add a file in the new directory to the index
+ writeTrashFile(fname + "/dir1", "file1", "c");
+ git.add().addFilepattern(fname + "/dir1/file1").call();
+
+ // create but do not add a file in the new directory to the index
+ writeTrashFile(fname + "/dir2", "file2", "d");
+
+ assertTrue("File must be a directory now", file.isDirectory());
+ assertFalse("Must not delete non empty directory", file.delete());
+
+ // 2 extra files are created
+ assertWorkDir(
+ mkmap(fname + "/dir1/file1", "c", fname + "/dir2/file2", "d"));
+
+ try {
+ git.checkout().setName("master").call();
+ fail("did not throw exception");
+ } catch (Exception e) {
+ // 2 extra files are still there
+ assertWorkDir(mkmap(fname + "/dir1/file1", "c",
+ fname + "/dir2/file2", "d"));
+ }
+ }
+
+ @Test
+ public void testOverwriteUntrackedLinkModeChange()
+ throws Exception {
+ Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
+ String fname = "file.txt";
+ Git git = Git.wrap(db);
+
+ // Add a file
+ writeTrashFile(fname, "a");
+ git.add().addFilepattern(fname).call();
+
+ // Add a link to file
+ String linkName = "link";
+ File link = writeLink(linkName, fname).toFile();
+ git.add().addFilepattern(linkName).call();
+ git.commit().setMessage("Added file and link").call();
+
+ assertWorkDir(mkmap(linkName, "a", fname, "a"));
+
+ // Create branch
+ git.branchCreate().setName("side").call();
+
+ // Switch branches
+ git.checkout().setName("side").call();
+
+ // replace link with directory containing files
+ FileUtils.delete(link);
+
+ // create and add a file in the new directory to the index
+ writeTrashFile(linkName + "/dir1", "file1", "c");
+ git.add().addFilepattern(linkName + "/dir1/file1").call();
+
+ // create but do not add a file in the new directory to the index
+ writeTrashFile(linkName + "/dir2", "file2", "d");
+
+ assertTrue("Link must be a directory now", link.isDirectory());
+ assertFalse("Must not delete non empty directory", link.delete());
+
+ // 2 extra files are created
+ assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c",
+ linkName + "/dir2/file2", "d"));
+
+ try {
+ git.checkout().setName("master").call();
+ fail("did not throw exception");
+ } catch (Exception e) {
+ // 2 extra files are still there
+ assertWorkDir(mkmap(fname, "a", linkName + "/dir1/file1", "c",
+ linkName + "/dir2/file2", "d"));
+ }
+ }
+
+ @Test
public void testFileModeChangeWithNoContentChangeUpdate() throws Exception {
if (!FS.DETECTED.supportsExecute())
return;
@@ -1213,10 +1609,11 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
assertNotNull(git.checkout().setName(Constants.MASTER).call());
}
- public void assertWorkDir(HashMap<String, String> i) throws CorruptObjectException,
+ public void assertWorkDir(Map<String, String> i)
+ throws CorruptObjectException,
IOException {
TreeWalk walk = new TreeWalk(db);
- walk.setRecursive(true);
+ walk.setRecursive(false);
walk.addTree(new FileTreeIterator(db));
String expectedValue;
String path;
@@ -1226,11 +1623,11 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
ft = walk.getTree(0, FileTreeIterator.class);
path = ft.getEntryPathString();
expectedValue = i.get(path);
- assertNotNull("found unexpected file for path " + path
- + " in workdir", expectedValue);
File file = new File(db.getWorkTree(), path);
assertTrue(file.exists());
if (file.isFile()) {
+ assertNotNull("found unexpected file for path " + path
+ + " in workdir", expectedValue);
FileInputStream is = new FileInputStream(file);
byte[] buffer = new byte[(int) file.length()];
int offset = 0;
@@ -1244,6 +1641,15 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
assertArrayEquals("unexpected content for path " + path
+ " in workDir. ", buffer, i.get(path).getBytes());
nrFiles++;
+ } else if (file.isDirectory()) {
+ if (file.list().length == 0) {
+ assertEquals("found unexpected empty folder for path "
+ + path + " in workDir. ", "/", i.get(path));
+ nrFiles++;
+ }
+ }
+ if (walk.isSubtree()) {
+ walk.enterSubtree();
}
}
assertEquals("WorkDir has not the right size.", i.size(), nrFiles);
diff --git a/org.eclipse.jgit.java7.test/src/org/eclipse/jgit/lib/DirCacheCheckoutTestWithSymlinks.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTestWithSymlinks.java
similarity index 100%
rename from org.eclipse.jgit.java7.test/src/org/eclipse/jgit/lib/DirCacheCheckoutTestWithSymlinks.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTestWithSymlinks.java
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java
index 863d79d..3259f62 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java
@@ -87,7 +87,7 @@ public class IndexDiffSubmoduleTest extends RepositoryTestCase {
.call();
submodule_db = (FileRepository) Git.wrap(db).submoduleAdd()
- .setPath("submodule")
+ .setPath("modules/submodule")
.setURI(submoduleStandalone.getDirectory().toURI().toString())
.call();
submoduleStandalone.close();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
index a85198f..7fcee3d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffTest.java
@@ -99,8 +99,7 @@ public class IndexDiffTest extends RepositoryTestCase {
public void testAdded() throws IOException {
writeTrashFile("file1", "file1");
writeTrashFile("dir/subfile", "dir/subfile");
- Tree tree = new Tree(db);
- tree.setId(insertTree(tree));
+ ObjectId tree = insertTree(new TreeFormatter());
DirCache index = db.lockDirCache();
DirCacheEditor editor = index.editor();
@@ -108,7 +107,7 @@ public class IndexDiffTest extends RepositoryTestCase {
editor.add(add(db, trash, "dir/subfile"));
editor.commit();
FileTreeIterator iterator = new FileTreeIterator(db);
- IndexDiff diff = new IndexDiff(db, tree.getId(), iterator);
+ IndexDiff diff = new IndexDiff(db, tree, iterator);
diff.diff();
assertEquals(2, diff.getAdded().size());
assertTrue(diff.getAdded().contains("file1"));
@@ -124,18 +123,16 @@ public class IndexDiffTest extends RepositoryTestCase {
writeTrashFile("file2", "file2");
writeTrashFile("dir/file3", "dir/file3");
- Tree tree = new Tree(db);
- tree.addFile("file2");
- tree.addFile("dir/file3");
- assertEquals(2, tree.memberCount());
- tree.findBlobMember("file2").setId(ObjectId.fromString("30d67d4672d5c05833b7192cc77a79eaafb5c7ad"));
- Tree tree2 = (Tree) tree.findTreeMember("dir");
- tree2.findBlobMember("file3").setId(ObjectId.fromString("873fb8d667d05436d728c52b1d7a09528e6eb59b"));
- tree2.setId(insertTree(tree2));
- tree.setId(insertTree(tree));
+ TreeFormatter dir = new TreeFormatter();
+ dir.append("file3", FileMode.REGULAR_FILE, ObjectId.fromString("873fb8d667d05436d728c52b1d7a09528e6eb59b"));
+
+ TreeFormatter tree = new TreeFormatter();
+ tree.append("file2", FileMode.REGULAR_FILE, ObjectId.fromString("30d67d4672d5c05833b7192cc77a79eaafb5c7ad"));
+ tree.append("dir", FileMode.TREE, insertTree(dir));
+ ObjectId treeId = insertTree(tree);
FileTreeIterator iterator = new FileTreeIterator(db);
- IndexDiff diff = new IndexDiff(db, tree.getId(), iterator);
+ IndexDiff diff = new IndexDiff(db, treeId, iterator);
diff.diff();
assertEquals(2, diff.getRemoved().size());
assertTrue(diff.getRemoved().contains("file2"));
@@ -157,16 +154,16 @@ public class IndexDiffTest extends RepositoryTestCase {
writeTrashFile("dir/file3", "changed");
- Tree tree = new Tree(db);
- tree.addFile("file2").setId(ObjectId.fromString("0123456789012345678901234567890123456789"));
- tree.addFile("dir/file3").setId(ObjectId.fromString("0123456789012345678901234567890123456789"));
- assertEquals(2, tree.memberCount());
+ TreeFormatter dir = new TreeFormatter();
+ dir.append("file3", FileMode.REGULAR_FILE, ObjectId.fromString("0123456789012345678901234567890123456789"));
+
+ TreeFormatter tree = new TreeFormatter();
+ tree.append("dir", FileMode.TREE, insertTree(dir));
+ tree.append("file2", FileMode.REGULAR_FILE, ObjectId.fromString("0123456789012345678901234567890123456789"));
+ ObjectId treeId = insertTree(tree);
- Tree tree2 = (Tree) tree.findTreeMember("dir");
- tree2.setId(insertTree(tree2));
- tree.setId(insertTree(tree));
FileTreeIterator iterator = new FileTreeIterator(db);
- IndexDiff diff = new IndexDiff(db, tree.getId(), iterator);
+ IndexDiff diff = new IndexDiff(db, treeId, iterator);
diff.diff();
assertEquals(2, diff.getChanged().size());
assertTrue(diff.getChanged().contains("file2"));
@@ -314,17 +311,16 @@ public class IndexDiffTest extends RepositoryTestCase {
git.add().addFilepattern("a=c").call();
git.add().addFilepattern("a=d").call();
- Tree tree = new Tree(db);
+ TreeFormatter tree = new TreeFormatter();
// got the hash id'd from the data using echo -n a.b|git hash-object -t blob --stdin
- tree.addFile("a.b").setId(ObjectId.fromString("f6f28df96c2b40c951164286e08be7c38ec74851"));
- tree.addFile("a.c").setId(ObjectId.fromString("6bc0e647512d2a0bef4f26111e484dc87df7f5ca"));
- tree.addFile("a=c").setId(ObjectId.fromString("06022365ddbd7fb126761319633bf73517770714"));
- tree.addFile("a=d").setId(ObjectId.fromString("fa6414df3da87840700e9eeb7fc261dd77ccd5c2"));
-
- tree.setId(insertTree(tree));
+ tree.append("a.b", FileMode.REGULAR_FILE, ObjectId.fromString("f6f28df96c2b40c951164286e08be7c38ec74851"));
+ tree.append("a.c", FileMode.REGULAR_FILE, ObjectId.fromString("6bc0e647512d2a0bef4f26111e484dc87df7f5ca"));
+ tree.append("a=c", FileMode.REGULAR_FILE, ObjectId.fromString("06022365ddbd7fb126761319633bf73517770714"));
+ tree.append("a=d", FileMode.REGULAR_FILE, ObjectId.fromString("fa6414df3da87840700e9eeb7fc261dd77ccd5c2"));
+ ObjectId treeId = insertTree(tree);
FileTreeIterator iterator = new FileTreeIterator(db);
- IndexDiff diff = new IndexDiff(db, tree.getId(), iterator);
+ IndexDiff diff = new IndexDiff(db, treeId, iterator);
diff.diff();
assertEquals(0, diff.getChanged().size());
assertEquals(0, diff.getAdded().size());
@@ -356,24 +352,27 @@ public class IndexDiffTest extends RepositoryTestCase {
.addFilepattern("a/c").addFilepattern("a=c")
.addFilepattern("a=d").call();
- Tree tree = new Tree(db);
+
// got the hash id'd from the data using echo -n a.b|git hash-object -t blob --stdin
- tree.addFile("a.b").setId(ObjectId.fromString("f6f28df96c2b40c951164286e08be7c38ec74851"));
- tree.addFile("a.c").setId(ObjectId.fromString("6bc0e647512d2a0bef4f26111e484dc87df7f5ca"));
- tree.addFile("a/b.b/b").setId(ObjectId.fromString("8d840bd4e2f3a48ff417c8e927d94996849933fd"));
- tree.addFile("a/b").setId(ObjectId.fromString("db89c972fc57862eae378f45b74aca228037d415"));
- tree.addFile("a/c").setId(ObjectId.fromString("52ad142a008aeb39694bafff8e8f1be75ed7f007"));
- tree.addFile("a=c").setId(ObjectId.fromString("06022365ddbd7fb126761319633bf73517770714"));
- tree.addFile("a=d").setId(ObjectId.fromString("fa6414df3da87840700e9eeb7fc261dd77ccd5c2"));
-
- Tree tree3 = (Tree) tree.findTreeMember("a/b.b");
- tree3.setId(insertTree(tree3));
- Tree tree2 = (Tree) tree.findTreeMember("a");
- tree2.setId(insertTree(tree2));
- tree.setId(insertTree(tree));
+ TreeFormatter bb = new TreeFormatter();
+ bb.append("b", FileMode.REGULAR_FILE, ObjectId.fromString("8d840bd4e2f3a48ff417c8e927d94996849933fd"));
+
+ TreeFormatter a = new TreeFormatter();
+ a.append("b", FileMode.REGULAR_FILE, ObjectId
+ .fromString("db89c972fc57862eae378f45b74aca228037d415"));
+ a.append("b.b", FileMode.TREE, insertTree(bb));
+ a.append("c", FileMode.REGULAR_FILE, ObjectId.fromString("52ad142a008aeb39694bafff8e8f1be75ed7f007"));
+
+ TreeFormatter tree = new TreeFormatter();
+ tree.append("a.b", FileMode.REGULAR_FILE, ObjectId.fromString("f6f28df96c2b40c951164286e08be7c38ec74851"));
+ tree.append("a.c", FileMode.REGULAR_FILE, ObjectId.fromString("6bc0e647512d2a0bef4f26111e484dc87df7f5ca"));
+ tree.append("a", FileMode.TREE, insertTree(a));
+ tree.append("a=c", FileMode.REGULAR_FILE, ObjectId.fromString("06022365ddbd7fb126761319633bf73517770714"));
+ tree.append("a=d", FileMode.REGULAR_FILE, ObjectId.fromString("fa6414df3da87840700e9eeb7fc261dd77ccd5c2"));
+ ObjectId treeId = insertTree(tree);
FileTreeIterator iterator = new FileTreeIterator(db);
- IndexDiff diff = new IndexDiff(db, tree.getId(), iterator);
+ IndexDiff diff = new IndexDiff(db, treeId, iterator);
diff.diff();
assertEquals(0, diff.getChanged().size());
assertEquals(0, diff.getAdded().size());
@@ -383,14 +382,11 @@ public class IndexDiffTest extends RepositoryTestCase {
assertEquals(Collections.EMPTY_SET, diff.getUntrackedFolders());
}
- private ObjectId insertTree(Tree tree) throws IOException {
- ObjectInserter oi = db.newObjectInserter();
- try {
- ObjectId id = oi.insert(Constants.OBJ_TREE, tree.format());
+ private ObjectId insertTree(TreeFormatter tree) throws IOException {
+ try (ObjectInserter oi = db.newObjectInserter()) {
+ ObjectId id = oi.insert(tree);
oi.flush();
return id;
- } finally {
- oi.release();
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectCheckerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectCheckerTest.java
index c6578cc..43160fb 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectCheckerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectCheckerTest.java
@@ -45,8 +45,25 @@
package org.eclipse.jgit.lib;
import static java.lang.Integer.valueOf;
-import static java.lang.Long.valueOf;
+import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
+import static org.eclipse.jgit.lib.Constants.OBJ_BAD;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
+import static org.eclipse.jgit.lib.Constants.OBJ_TAG;
+import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
+import static org.eclipse.jgit.lib.Constants.encode;
+import static org.eclipse.jgit.lib.Constants.encodeASCII;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.DUPLICATE_ENTRIES;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.EMPTY_NAME;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.FULL_PATHNAME;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.HAS_DOT;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.HAS_DOTDOT;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.HAS_DOTGIT;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.NULL_SHA1;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.TREE_NOT_SORTED;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.ZERO_PADDED_FILEMODE;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;
import java.io.UnsupportedEncodingException;
@@ -67,15 +84,10 @@ public class ObjectCheckerTest {
@Test
public void testInvalidType() {
- try {
- checker.check(Constants.OBJ_BAD, new byte[0]);
- fail("Did not throw CorruptObjectException");
- } catch (CorruptObjectException e) {
- final String m = e.getMessage();
- assertEquals(MessageFormat.format(
- JGitText.get().corruptObjectInvalidType2,
- valueOf(Constants.OBJ_BAD)), m);
- }
+ String msg = MessageFormat.format(
+ JGitText.get().corruptObjectInvalidType2,
+ valueOf(OBJ_BAD));
+ assertCorrupt(msg, OBJ_BAD, new byte[0]);
}
@Test
@@ -84,13 +96,13 @@ public class ObjectCheckerTest {
checker.checkBlob(new byte[0]);
checker.checkBlob(new byte[1]);
- checker.check(Constants.OBJ_BLOB, new byte[0]);
- checker.check(Constants.OBJ_BLOB, new byte[1]);
+ checker.check(OBJ_BLOB, new byte[0]);
+ checker.check(OBJ_BLOB, new byte[1]);
}
@Test
public void testValidCommitNoParent() throws CorruptObjectException {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
@@ -99,14 +111,14 @@ public class ObjectCheckerTest {
b.append("author A. U. Thor <author at localhost> 1 +0000\n");
b.append("committer A. U. Thor <author at localhost> 1 +0000\n");
- final byte[] data = Constants.encodeASCII(b.toString());
+ byte[] data = encodeASCII(b.toString());
checker.checkCommit(data);
- checker.check(Constants.OBJ_COMMIT, data);
+ checker.check(OBJ_COMMIT, data);
}
@Test
public void testValidCommitBlankAuthor() throws CorruptObjectException {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
@@ -115,14 +127,46 @@ public class ObjectCheckerTest {
b.append("author <> 0 +0000\n");
b.append("committer <> 0 +0000\n");
- final byte[] data = Constants.encodeASCII(b.toString());
+ byte[] data = encodeASCII(b.toString());
+ checker.checkCommit(data);
+ checker.check(OBJ_COMMIT, data);
+ }
+
+ @Test
+ public void testCommitCorruptAuthor() throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
+ b.append("tree be9bfa841874ccc9f2ef7c48d0c76226f89b7189\n");
+ b.append("author b <b at c> <b at c> 0 +0000\n");
+ b.append("committer <> 0 +0000\n");
+
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("bad date", OBJ_COMMIT, data);
+ checker.setAllowInvalidPersonIdent(true);
+ checker.checkCommit(data);
+
+ checker.setAllowInvalidPersonIdent(false);
+ assertSkipListAccepts(OBJ_COMMIT, data);
+ }
+
+ @Test
+ public void testCommitCorruptCommitter() throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
+ b.append("tree be9bfa841874ccc9f2ef7c48d0c76226f89b7189\n");
+ b.append("author <> 0 +0000\n");
+ b.append("committer b <b at c> <b at c> 0 +0000\n");
+
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("bad date", OBJ_COMMIT, data);
+ checker.setAllowInvalidPersonIdent(true);
checker.checkCommit(data);
- checker.check(Constants.OBJ_COMMIT, data);
+
+ checker.setAllowInvalidPersonIdent(false);
+ assertSkipListAccepts(OBJ_COMMIT, data);
}
@Test
public void testValidCommit1Parent() throws CorruptObjectException {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
@@ -135,14 +179,14 @@ public class ObjectCheckerTest {
b.append("author A. U. Thor <author at localhost> 1 +0000\n");
b.append("committer A. U. Thor <author at localhost> 1 +0000\n");
- final byte[] data = Constants.encodeASCII(b.toString());
+ byte[] data = encodeASCII(b.toString());
checker.checkCommit(data);
- checker.check(Constants.OBJ_COMMIT, data);
+ checker.check(OBJ_COMMIT, data);
}
@Test
public void testValidCommit2Parent() throws CorruptObjectException {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
@@ -159,14 +203,14 @@ public class ObjectCheckerTest {
b.append("author A. U. Thor <author at localhost> 1 +0000\n");
b.append("committer A. U. Thor <author at localhost> 1 +0000\n");
- final byte[] data = Constants.encodeASCII(b.toString());
+ byte[] data = encodeASCII(b.toString());
checker.checkCommit(data);
- checker.check(Constants.OBJ_COMMIT, data);
+ checker.check(OBJ_COMMIT, data);
}
@Test
public void testValidCommit128Parent() throws CorruptObjectException {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
@@ -181,15 +225,15 @@ public class ObjectCheckerTest {
b.append("author A. U. Thor <author at localhost> 1 +0000\n");
b.append("committer A. U. Thor <author at localhost> 1 +0000\n");
- final byte[] data = Constants.encodeASCII(b.toString());
+ byte[] data = encodeASCII(b.toString());
checker.checkCommit(data);
- checker.check(Constants.OBJ_COMMIT, data);
+ checker.check(OBJ_COMMIT, data);
}
@Test
public void testValidCommitNormalTime() throws CorruptObjectException {
- final StringBuilder b = new StringBuilder();
- final String when = "1222757360 -0730";
+ StringBuilder b = new StringBuilder();
+ String when = "1222757360 -0730";
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
@@ -198,840 +242,539 @@ public class ObjectCheckerTest {
b.append("author A. U. Thor <author at localhost> " + when + "\n");
b.append("committer A. U. Thor <author at localhost> " + when + "\n");
- final byte[] data = Constants.encodeASCII(b.toString());
+ byte[] data = encodeASCII(b.toString());
checker.checkCommit(data);
- checker.check(Constants.OBJ_COMMIT, data);
+ checker.check(OBJ_COMMIT, data);
}
@Test
public void testInvalidCommitNoTree1() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("parent ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- assertEquals("no tree header", e.getMessage());
- }
+ assertCorrupt("no tree header", OBJ_COMMIT, b);
}
@Test
public void testInvalidCommitNoTree2() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("trie ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- assertEquals("no tree header", e.getMessage());
- }
+ assertCorrupt("no tree header", OBJ_COMMIT, b);
}
@Test
public void testInvalidCommitNoTree3() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("tree");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- assertEquals("no tree header", e.getMessage());
- }
+ assertCorrupt("no tree header", OBJ_COMMIT, b);
}
@Test
public void testInvalidCommitNoTree4() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("tree\t");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- assertEquals("no tree header", e.getMessage());
- }
+ assertCorrupt("no tree header", OBJ_COMMIT, b);
}
@Test
public void testInvalidCommitInvalidTree1() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("zzzzfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- assertEquals("invalid tree", e.getMessage());
- }
+ assertCorrupt("invalid tree", OBJ_COMMIT, b);
}
@Test
public void testInvalidCommitInvalidTree2() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append("z\n");
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- assertEquals("invalid tree", e.getMessage());
- }
+ assertCorrupt("invalid tree", OBJ_COMMIT, b);
}
@Test
public void testInvalidCommitInvalidTree3() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9b");
b.append("\n");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- assertEquals("invalid tree", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("invalid tree", OBJ_COMMIT, data);
}
@Test
public void testInvalidCommitInvalidTree4() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- assertEquals("invalid tree", e.getMessage());
- }
+ assertCorrupt("invalid tree", OBJ_COMMIT, b);
}
@Test
public void testInvalidCommitInvalidParent1() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("parent ");
b.append("\n");
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- assertEquals("invalid parent", e.getMessage());
- }
+ assertCorrupt("invalid parent", OBJ_COMMIT, b);
}
@Test
public void testInvalidCommitInvalidParent2() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("parent ");
b.append("zzzzfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append("\n");
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- assertEquals("invalid parent", e.getMessage());
- }
+ assertCorrupt("invalid parent", OBJ_COMMIT, b);
}
@Test
public void testInvalidCommitInvalidParent3() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("parent ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append("\n");
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- assertEquals("invalid parent", e.getMessage());
- }
+ assertCorrupt("invalid parent", OBJ_COMMIT, b);
}
@Test
public void testInvalidCommitInvalidParent4() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("parent ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append("z\n");
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- assertEquals("invalid parent", e.getMessage());
- }
+ assertCorrupt("invalid parent", OBJ_COMMIT, b);
}
@Test
public void testInvalidCommitInvalidParent5() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("parent\t");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append("\n");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- // Yes, really, we complain about author not being
- // found as the invalid parent line wasn't consumed.
- assertEquals("no author", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ // Yes, really, we complain about author not being
+ // found as the invalid parent line wasn't consumed.
+ assertCorrupt("no author", OBJ_COMMIT, data);
}
@Test
- public void testInvalidCommitNoAuthor() {
- final StringBuilder b = new StringBuilder();
-
+ public void testInvalidCommitNoAuthor() throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("committer A. U. Thor <author at localhost> 1 +0000\n");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- // Yes, really, we complain about author not being
- // found as the invalid parent line wasn't consumed.
- assertEquals("no author", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("no author", OBJ_COMMIT, data);
+ assertSkipListAccepts(OBJ_COMMIT, data);
}
@Test
- public void testInvalidCommitNoCommitter1() {
- final StringBuilder b = new StringBuilder();
-
+ public void testInvalidCommitNoCommitter1() throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("author A. U. Thor <author at localhost> 1 +0000\n");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- // Yes, really, we complain about author not being
- // found as the invalid parent line wasn't consumed.
- assertEquals("no committer", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("no committer", OBJ_COMMIT, data);
+ assertSkipListAccepts(OBJ_COMMIT, data);
}
@Test
- public void testInvalidCommitNoCommitter2() {
- final StringBuilder b = new StringBuilder();
-
+ public void testInvalidCommitNoCommitter2() throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("author A. U. Thor <author at localhost> 1 +0000\n");
b.append("\n");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- // Yes, really, we complain about author not being
- // found as the invalid parent line wasn't consumed.
- assertEquals("no committer", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("no committer", OBJ_COMMIT, data);
+ assertSkipListAccepts(OBJ_COMMIT, data);
}
@Test
- public void testInvalidCommitInvalidAuthor1() {
- final StringBuilder b = new StringBuilder();
-
+ public void testInvalidCommitInvalidAuthor1()
+ throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("author A. U. Thor <foo 1 +0000\n");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- // Yes, really, we complain about author not being
- // found as the invalid parent line wasn't consumed.
- assertEquals("invalid author", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("bad email", OBJ_COMMIT, data);
+ assertSkipListAccepts(OBJ_COMMIT, data);
}
@Test
- public void testInvalidCommitInvalidAuthor2() {
- final StringBuilder b = new StringBuilder();
-
+ public void testInvalidCommitInvalidAuthor2()
+ throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("author A. U. Thor foo> 1 +0000\n");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- // Yes, really, we complain about author not being
- // found as the invalid parent line wasn't consumed.
- assertEquals("invalid author", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("missing email", OBJ_COMMIT, data);
+ assertSkipListAccepts(OBJ_COMMIT, data);
}
@Test
- public void testInvalidCommitInvalidAuthor3() {
- final StringBuilder b = new StringBuilder();
-
+ public void testInvalidCommitInvalidAuthor3()
+ throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("author 1 +0000\n");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- // Yes, really, we complain about author not being
- // found as the invalid parent line wasn't consumed.
- assertEquals("invalid author", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("missing email", OBJ_COMMIT, data);
+ assertSkipListAccepts(OBJ_COMMIT, data);
}
@Test
- public void testInvalidCommitInvalidAuthor4() {
- final StringBuilder b = new StringBuilder();
-
+ public void testInvalidCommitInvalidAuthor4()
+ throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("author a <b> +0000\n");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- // Yes, really, we complain about author not being
- // found as the invalid parent line wasn't consumed.
- assertEquals("invalid author", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("bad date", OBJ_COMMIT, data);
+ assertSkipListAccepts(OBJ_COMMIT, data);
}
@Test
- public void testInvalidCommitInvalidAuthor5() {
- final StringBuilder b = new StringBuilder();
-
+ public void testInvalidCommitInvalidAuthor5()
+ throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("author a <b>\n");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- // Yes, really, we complain about author not being
- // found as the invalid parent line wasn't consumed.
- assertEquals("invalid author", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("bad date", OBJ_COMMIT, data);
+ assertSkipListAccepts(OBJ_COMMIT, data);
}
@Test
- public void testInvalidCommitInvalidAuthor6() {
- final StringBuilder b = new StringBuilder();
-
+ public void testInvalidCommitInvalidAuthor6()
+ throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("author a <b> z");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- // Yes, really, we complain about author not being
- // found as the invalid parent line wasn't consumed.
- assertEquals("invalid author", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("bad date", OBJ_COMMIT, data);
+ assertSkipListAccepts(OBJ_COMMIT, data);
}
@Test
- public void testInvalidCommitInvalidAuthor7() {
- final StringBuilder b = new StringBuilder();
-
+ public void testInvalidCommitInvalidAuthor7()
+ throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("author a <b> 1 z");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- // Yes, really, we complain about author not being
- // found as the invalid parent line wasn't consumed.
- assertEquals("invalid author", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("bad time zone", OBJ_COMMIT, data);
+ assertSkipListAccepts(OBJ_COMMIT, data);
}
@Test
- public void testInvalidCommitInvalidCommitter() {
- final StringBuilder b = new StringBuilder();
-
+ public void testInvalidCommitInvalidCommitter()
+ throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
b.append("tree ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("author a <b> 1 +0000\n");
b.append("committer a <");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkCommit(data);
- fail("Did not catch corrupt object");
- } catch (CorruptObjectException e) {
- // Yes, really, we complain about author not being
- // found as the invalid parent line wasn't consumed.
- assertEquals("invalid committer", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("bad email", OBJ_COMMIT, data);
+ assertSkipListAccepts(OBJ_COMMIT, data);
}
@Test
public void testValidTag() throws CorruptObjectException {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("object ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("type commit\n");
b.append("tag test-tag\n");
b.append("tagger A. U. Thor <author at localhost> 1 +0000\n");
- final byte[] data = Constants.encodeASCII(b.toString());
+ byte[] data = encodeASCII(b.toString());
checker.checkTag(data);
- checker.check(Constants.OBJ_TAG, data);
+ checker.check(OBJ_TAG, data);
}
@Test
public void testInvalidTagNoObject1() {
- final StringBuilder b = new StringBuilder();
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTag(data);
- fail("incorrectly accepted invalid tag");
- } catch (CorruptObjectException e) {
- assertEquals("no object header", e.getMessage());
- }
+ assertCorrupt("no object header", OBJ_TAG, new byte[0]);
}
@Test
public void testInvalidTagNoObject2() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("object\t");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTag(data);
- fail("incorrectly accepted invalid tag");
- } catch (CorruptObjectException e) {
- assertEquals("no object header", e.getMessage());
- }
+ assertCorrupt("no object header", OBJ_TAG, b);
}
@Test
public void testInvalidTagNoObject3() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("obejct ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTag(data);
- fail("incorrectly accepted invalid tag");
- } catch (CorruptObjectException e) {
- assertEquals("no object header", e.getMessage());
- }
+ assertCorrupt("no object header", OBJ_TAG, b);
}
@Test
public void testInvalidTagNoObject4() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("object ");
b.append("zz9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTag(data);
- fail("incorrectly accepted invalid tag");
- } catch (CorruptObjectException e) {
- assertEquals("invalid object", e.getMessage());
- }
+ assertCorrupt("invalid object", OBJ_TAG, b);
}
@Test
public void testInvalidTagNoObject5() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("object ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append(" \n");
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTag(data);
- fail("incorrectly accepted invalid tag");
- } catch (CorruptObjectException e) {
- assertEquals("invalid object", e.getMessage());
- }
+ assertCorrupt("invalid object", OBJ_TAG, b);
}
@Test
public void testInvalidTagNoObject6() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("object ");
b.append("be9");
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTag(data);
- fail("incorrectly accepted invalid tag");
- } catch (CorruptObjectException e) {
- assertEquals("invalid object", e.getMessage());
- }
+ assertCorrupt("invalid object", OBJ_TAG, b);
}
@Test
public void testInvalidTagNoType1() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("object ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTag(data);
- fail("incorrectly accepted invalid tag");
- } catch (CorruptObjectException e) {
- assertEquals("no type header", e.getMessage());
- }
+ assertCorrupt("no type header", OBJ_TAG, b);
}
@Test
public void testInvalidTagNoType2() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("object ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("type\tcommit\n");
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTag(data);
- fail("incorrectly accepted invalid tag");
- } catch (CorruptObjectException e) {
- assertEquals("no type header", e.getMessage());
- }
+ assertCorrupt("no type header", OBJ_TAG, b);
}
@Test
public void testInvalidTagNoType3() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("object ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("tpye commit\n");
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTag(data);
- fail("incorrectly accepted invalid tag");
- } catch (CorruptObjectException e) {
- assertEquals("no type header", e.getMessage());
- }
+ assertCorrupt("no type header", OBJ_TAG, b);
}
@Test
public void testInvalidTagNoType4() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("object ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("type commit");
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTag(data);
- fail("incorrectly accepted invalid tag");
- } catch (CorruptObjectException e) {
- assertEquals("no tag header", e.getMessage());
- }
+ assertCorrupt("no tag header", OBJ_TAG, b);
}
@Test
public void testInvalidTagNoTagHeader1() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("object ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("type commit\n");
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTag(data);
- fail("incorrectly accepted invalid tag");
- } catch (CorruptObjectException e) {
- assertEquals("no tag header", e.getMessage());
- }
+ assertCorrupt("no tag header", OBJ_TAG, b);
}
@Test
public void testInvalidTagNoTagHeader2() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("object ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("type commit\n");
b.append("tag\tfoo\n");
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTag(data);
- fail("incorrectly accepted invalid tag");
- } catch (CorruptObjectException e) {
- assertEquals("no tag header", e.getMessage());
- }
+ assertCorrupt("no tag header", OBJ_TAG, b);
}
@Test
public void testInvalidTagNoTagHeader3() {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("object ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("type commit\n");
b.append("tga foo\n");
-
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTag(data);
- fail("incorrectly accepted invalid tag");
- } catch (CorruptObjectException e) {
- assertEquals("no tag header", e.getMessage());
- }
+ assertCorrupt("no tag header", OBJ_TAG, b);
}
@Test
public void testValidTagHasNoTaggerHeader() throws CorruptObjectException {
- final StringBuilder b = new StringBuilder();
-
+ StringBuilder b = new StringBuilder();
b.append("object ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("type commit\n");
b.append("tag foo\n");
-
- checker.checkTag(Constants.encodeASCII(b.toString()));
+ checker.checkTag(encodeASCII(b.toString()));
}
@Test
- public void testInvalidTagInvalidTaggerHeader1() {
- final StringBuilder b = new StringBuilder();
-
+ public void testInvalidTagInvalidTaggerHeader1()
+ throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
b.append("object ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("type commit\n");
b.append("tag foo\n");
b.append("tagger \n");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTag(data);
- fail("incorrectly accepted invalid tag");
- } catch (CorruptObjectException e) {
- assertEquals("invalid tagger", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("missing email", OBJ_TAG, data);
+ checker.setAllowInvalidPersonIdent(true);
+ checker.checkTag(data);
+
+ checker.setAllowInvalidPersonIdent(false);
+ assertSkipListAccepts(OBJ_TAG, data);
}
@Test
- public void testInvalidTagInvalidTaggerHeader3() {
- final StringBuilder b = new StringBuilder();
-
+ public void testInvalidTagInvalidTaggerHeader3()
+ throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
b.append("object ");
b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
b.append('\n');
-
b.append("type commit\n");
b.append("tag foo\n");
b.append("tagger a < 1 +000\n");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTag(data);
- fail("incorrectly accepted invalid tag");
- } catch (CorruptObjectException e) {
- assertEquals("invalid tagger", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("bad email", OBJ_TAG, data);
+ assertSkipListAccepts(OBJ_TAG, data);
}
@Test
public void testValidEmptyTree() throws CorruptObjectException {
checker.checkTree(new byte[0]);
- checker.check(Constants.OBJ_TREE, new byte[0]);
+ checker.check(OBJ_TREE, new byte[0]);
}
@Test
public void testValidTree1() throws CorruptObjectException {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
entry(b, "100644 regular-file");
- final byte[] data = Constants.encodeASCII(b.toString());
- checker.checkTree(data);
+ checker.checkTree(encodeASCII(b.toString()));
}
@Test
public void testValidTree2() throws CorruptObjectException {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
entry(b, "100755 executable");
- final byte[] data = Constants.encodeASCII(b.toString());
- checker.checkTree(data);
+ checker.checkTree(encodeASCII(b.toString()));
}
@Test
public void testValidTree3() throws CorruptObjectException {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
entry(b, "40000 tree");
- final byte[] data = Constants.encodeASCII(b.toString());
- checker.checkTree(data);
+ checker.checkTree(encodeASCII(b.toString()));
}
@Test
public void testValidTree4() throws CorruptObjectException {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
entry(b, "120000 symlink");
- final byte[] data = Constants.encodeASCII(b.toString());
- checker.checkTree(data);
+ checker.checkTree(encodeASCII(b.toString()));
}
@Test
public void testValidTree5() throws CorruptObjectException {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
entry(b, "160000 git link");
- final byte[] data = Constants.encodeASCII(b.toString());
- checker.checkTree(data);
+ checker.checkTree(encodeASCII(b.toString()));
}
@Test
public void testValidTree6() throws CorruptObjectException {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
entry(b, "100644 .a");
- final byte[] data = Constants.encodeASCII(b.toString());
+ checker.checkTree(encodeASCII(b.toString()));
+ }
+
+ @Test
+ public void testNullSha1InTreeEntry() throws CorruptObjectException {
+ byte[] data = concat(
+ encodeASCII("100644 A"), new byte[] { '\0' },
+ new byte[OBJECT_ID_LENGTH]);
+ assertCorrupt("entry points to null SHA-1", OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(NULL_SHA1, true);
checker.checkTree(data);
}
@@ -1045,357 +788,326 @@ public class ObjectCheckerTest {
@Test
public void testValidTreeSorting1() throws CorruptObjectException {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
entry(b, "100644 fooaaa");
entry(b, "100755 foobar");
- final byte[] data = Constants.encodeASCII(b.toString());
- checker.checkTree(data);
+ checker.checkTree(encodeASCII(b.toString()));
}
@Test
public void testValidTreeSorting2() throws CorruptObjectException {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
entry(b, "100755 fooaaa");
entry(b, "100644 foobar");
- final byte[] data = Constants.encodeASCII(b.toString());
- checker.checkTree(data);
+ checker.checkTree(encodeASCII(b.toString()));
}
@Test
public void testValidTreeSorting3() throws CorruptObjectException {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
entry(b, "40000 a");
entry(b, "100644 b");
- final byte[] data = Constants.encodeASCII(b.toString());
- checker.checkTree(data);
+ checker.checkTree(encodeASCII(b.toString()));
}
@Test
public void testValidTreeSorting4() throws CorruptObjectException {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
entry(b, "100644 a");
entry(b, "40000 b");
- final byte[] data = Constants.encodeASCII(b.toString());
- checker.checkTree(data);
+ checker.checkTree(encodeASCII(b.toString()));
}
@Test
public void testValidTreeSorting5() throws CorruptObjectException {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
entry(b, "100644 a.c");
entry(b, "40000 a");
entry(b, "100644 a0c");
- final byte[] data = Constants.encodeASCII(b.toString());
- checker.checkTree(data);
+ checker.checkTree(encodeASCII(b.toString()));
}
@Test
public void testValidTreeSorting6() throws CorruptObjectException {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
entry(b, "40000 a");
entry(b, "100644 apple");
- final byte[] data = Constants.encodeASCII(b.toString());
- checker.checkTree(data);
+ checker.checkTree(encodeASCII(b.toString()));
}
@Test
public void testValidTreeSorting7() throws CorruptObjectException {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
entry(b, "40000 an orang");
entry(b, "40000 an orange");
- final byte[] data = Constants.encodeASCII(b.toString());
- checker.checkTree(data);
+ checker.checkTree(encodeASCII(b.toString()));
}
@Test
public void testValidTreeSorting8() throws CorruptObjectException {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
entry(b, "100644 a");
entry(b, "100644 a0c");
entry(b, "100644 b");
- final byte[] data = Constants.encodeASCII(b.toString());
- checker.checkTree(data);
+ checker.checkTree(encodeASCII(b.toString()));
}
@Test
public void testAcceptTreeModeWithZero() throws CorruptObjectException {
StringBuilder b = new StringBuilder();
entry(b, "040000 a");
+ byte[] data = encodeASCII(b.toString());
checker.setAllowLeadingZeroFileMode(true);
- checker.checkTree(Constants.encodeASCII(b.toString()));
+ checker.checkTree(data);
+
+ checker.setAllowLeadingZeroFileMode(false);
+ assertSkipListAccepts(OBJ_TREE, data);
+
+ checker.setIgnore(ZERO_PADDED_FILEMODE, true);
+ checker.checkTree(data);
}
@Test
public void testInvalidTreeModeStartsWithZero1() {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
entry(b, "0 a");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("mode starts with '0'", e.getMessage());
- }
+ assertCorrupt("mode starts with '0'", OBJ_TREE, b);
}
@Test
public void testInvalidTreeModeStartsWithZero2() {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
entry(b, "0100644 a");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("mode starts with '0'", e.getMessage());
- }
+ assertCorrupt("mode starts with '0'", OBJ_TREE, b);
}
@Test
public void testInvalidTreeModeStartsWithZero3() {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
entry(b, "040000 a");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("mode starts with '0'", e.getMessage());
- }
+ assertCorrupt("mode starts with '0'", OBJ_TREE, b);
}
@Test
public void testInvalidTreeModeNotOctal1() {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
entry(b, "8 a");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("invalid mode character", e.getMessage());
- }
+ assertCorrupt("invalid mode character", OBJ_TREE, b);
}
@Test
public void testInvalidTreeModeNotOctal2() {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
entry(b, "Z a");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("invalid mode character", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("invalid mode character", OBJ_TREE, data);
+ assertSkipListRejects("invalid mode character", OBJ_TREE, data);
}
@Test
public void testInvalidTreeModeNotSupportedMode1() {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
entry(b, "1 a");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("invalid mode 1", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("invalid mode 1", OBJ_TREE, data);
+ assertSkipListRejects("invalid mode 1", OBJ_TREE, data);
}
@Test
public void testInvalidTreeModeNotSupportedMode2() {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
entry(b, "170000 a");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("invalid mode " + 0170000, e.getMessage());
- }
+ assertCorrupt("invalid mode " + 0170000, OBJ_TREE, b);
}
@Test
public void testInvalidTreeModeMissingName() {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
b.append("100644");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("truncated in mode", e.getMessage());
- }
+ assertCorrupt("truncated in mode", OBJ_TREE, b);
}
@Test
- public void testInvalidTreeNameContainsSlash() {
- final StringBuilder b = new StringBuilder();
+ public void testInvalidTreeNameContainsSlash()
+ throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
entry(b, "100644 a/b");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("name contains '/'", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("name contains '/'", OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(FULL_PATHNAME, true);
+ checker.checkTree(data);
}
@Test
- public void testInvalidTreeNameIsEmpty() {
- final StringBuilder b = new StringBuilder();
+ public void testInvalidTreeNameIsEmpty() throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
entry(b, "100644 ");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("zero length name", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("zero length name", OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(EMPTY_NAME, true);
+ checker.checkTree(data);
}
@Test
- public void testInvalidTreeNameIsDot() {
- final StringBuilder b = new StringBuilder();
+ public void testInvalidTreeNameIsDot() throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
entry(b, "100644 .");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("invalid name '.'", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("invalid name '.'", OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(HAS_DOT, true);
+ checker.checkTree(data);
}
@Test
- public void testInvalidTreeNameIsDotDot() {
- final StringBuilder b = new StringBuilder();
+ public void testInvalidTreeNameIsDotDot() throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
entry(b, "100644 ..");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("invalid name '..'", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("invalid name '..'", OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(HAS_DOTDOT, true);
+ checker.checkTree(data);
}
@Test
- public void testInvalidTreeNameIsGit() {
+ public void testInvalidTreeNameIsGit() throws CorruptObjectException {
StringBuilder b = new StringBuilder();
entry(b, "100644 .git");
- byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("invalid name '.git'", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("invalid name '.git'", OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(HAS_DOTGIT, true);
+ checker.checkTree(data);
}
@Test
- public void testInvalidTreeNameIsMixedCaseGit() {
+ public void testInvalidTreeNameIsMixedCaseGit()
+ throws CorruptObjectException {
StringBuilder b = new StringBuilder();
entry(b, "100644 .GiT");
- byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("invalid name '.GiT'", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("invalid name '.GiT'", OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(HAS_DOTGIT, true);
+ checker.checkTree(data);
}
@Test
- public void testInvalidTreeNameIsMacHFSGit() {
+ public void testInvalidTreeNameIsMacHFSGit() throws CorruptObjectException {
StringBuilder b = new StringBuilder();
entry(b, "100644 .gi\u200Ct");
- byte[] data = Constants.encode(b.toString());
- try {
- checker.setSafeForMacOS(true);
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals(
- "invalid name '.gi\u200Ct' contains ignorable Unicode characters",
- e.getMessage());
- }
+ byte[] data = encode(b.toString());
+
+ // Fine on POSIX.
+ checker.checkTree(data);
+
+ // Rejected on Mac OS.
+ checker.setSafeForMacOS(true);
+ assertCorrupt(
+ "invalid name '.gi\u200Ct' contains ignorable Unicode characters",
+ OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(HAS_DOTGIT, true);
+ checker.checkTree(data);
}
@Test
- public void testInvalidTreeNameIsMacHFSGit2() {
+ public void testInvalidTreeNameIsMacHFSGit2()
+ throws CorruptObjectException {
StringBuilder b = new StringBuilder();
entry(b, "100644 \u206B.git");
- byte[] data = Constants.encode(b.toString());
- try {
- checker.setSafeForMacOS(true);
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals(
- "invalid name '\u206B.git' contains ignorable Unicode characters",
- e.getMessage());
- }
+ byte[] data = encode(b.toString());
+
+ // Fine on POSIX.
+ checker.checkTree(data);
+
+ // Rejected on Mac OS.
+ checker.setSafeForMacOS(true);
+ assertCorrupt(
+ "invalid name '\u206B.git' contains ignorable Unicode characters",
+ OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(HAS_DOTGIT, true);
+ checker.checkTree(data);
}
@Test
- public void testInvalidTreeNameIsMacHFSGit3() {
+ public void testInvalidTreeNameIsMacHFSGit3()
+ throws CorruptObjectException {
StringBuilder b = new StringBuilder();
entry(b, "100644 .git\uFEFF");
- byte[] data = Constants.encode(b.toString());
- try {
- checker.setSafeForMacOS(true);
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals(
- "invalid name '.git\uFEFF' contains ignorable Unicode characters",
- e.getMessage());
- }
+ byte[] data = encode(b.toString());
+
+ // Fine on POSIX.
+ checker.checkTree(data);
+
+ // Rejected on Mac OS.
+ checker.setSafeForMacOS(true);
+ assertCorrupt(
+ "invalid name '.git\uFEFF' contains ignorable Unicode characters",
+ OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(HAS_DOTGIT, true);
+ checker.checkTree(data);
}
- private static byte[] concat(byte[] b1, byte[] b2) {
- byte[] data = new byte[b1.length + b2.length];
- System.arraycopy(b1, 0, data, 0, b1.length);
- System.arraycopy(b2, 0, data, b1.length, b2.length);
+ private static byte[] concat(byte[]... b) {
+ int n = 0;
+ for (byte[] a : b) {
+ n += a.length;
+ }
+
+ byte[] data = new byte[n];
+ n = 0;
+ for (byte[] a : b) {
+ System.arraycopy(a, 0, data, n, a.length);
+ n += a.length;
+ }
return data;
}
@Test
- public void testInvalidTreeNameIsMacHFSGitCorruptUTF8AtEnd() {
- byte[] data = concat(Constants.encode("100644 .git"),
+ public void testInvalidTreeNameIsMacHFSGitCorruptUTF8AtEnd()
+ throws CorruptObjectException {
+ byte[] data = concat(encode("100644 .git"),
new byte[] { (byte) 0xef });
StringBuilder b = new StringBuilder();
entry(b, "");
- data = concat(data, Constants.encode(b.toString()));
- try {
- checker.setSafeForMacOS(true);
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals(
- "invalid name contains byte sequence '0xef' which is not a valid UTF-8 character",
- e.getMessage());
- }
+ data = concat(data, encode(b.toString()));
+
+ // Fine on POSIX.
+ checker.checkTree(data);
+
+ // Rejected on Mac OS.
+ checker.setSafeForMacOS(true);
+ assertCorrupt(
+ "invalid name contains byte sequence '0xef' which is not a valid UTF-8 character",
+ OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
}
@Test
- public void testInvalidTreeNameIsMacHFSGitCorruptUTF8AtEnd2() {
- byte[] data = concat(Constants.encode("100644 .git"), new byte[] {
+ public void testInvalidTreeNameIsMacHFSGitCorruptUTF8AtEnd2()
+ throws CorruptObjectException {
+ byte[] data = concat(encode("100644 .git"),
+ new byte[] {
(byte) 0xe2, (byte) 0xab });
StringBuilder b = new StringBuilder();
entry(b, "");
- data = concat(data, Constants.encode(b.toString()));
- try {
- checker.setSafeForMacOS(true);
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals(
- "invalid name contains byte sequence '0xe2ab' which is not a valid UTF-8 character",
- e.getMessage());
- }
+ data = concat(data, encode(b.toString()));
+
+ // Fine on POSIX.
+ checker.checkTree(data);
+
+ // Rejected on Mac OS.
+ checker.setSafeForMacOS(true);
+ assertCorrupt(
+ "invalid name contains byte sequence '0xe2ab' which is not a valid UTF-8 character",
+ OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
}
@Test
@@ -1403,7 +1115,7 @@ public class ObjectCheckerTest {
throws CorruptObjectException {
StringBuilder b = new StringBuilder();
entry(b, "100644 .git\u200Cx");
- byte[] data = Constants.encode(b.toString());
+ byte[] data = encode(b.toString());
checker.setSafeForMacOS(true);
checker.checkTree(data);
}
@@ -1413,7 +1125,7 @@ public class ObjectCheckerTest {
throws CorruptObjectException {
StringBuilder b = new StringBuilder();
entry(b, "100644 .kit\u200C");
- byte[] data = Constants.encode(b.toString());
+ byte[] data = encode(b.toString());
checker.setSafeForMacOS(true);
checker.checkTree(data);
}
@@ -1423,21 +1135,19 @@ public class ObjectCheckerTest {
throws CorruptObjectException {
StringBuilder b = new StringBuilder();
entry(b, "100644 .git\u200C");
- byte[] data = Constants.encode(b.toString());
+ byte[] data = encode(b.toString());
checker.checkTree(data);
}
@Test
- public void testInvalidTreeNameIsDotGitDot() {
+ public void testInvalidTreeNameIsDotGitDot() throws CorruptObjectException {
StringBuilder b = new StringBuilder();
entry(b, "100644 .git.");
- byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("invalid name '.git.'", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("invalid name '.git.'", OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(HAS_DOTGIT, true);
+ checker.checkTree(data);
}
@Test
@@ -1445,20 +1155,19 @@ public class ObjectCheckerTest {
throws CorruptObjectException {
StringBuilder b = new StringBuilder();
entry(b, "100644 .git..");
- checker.checkTree(Constants.encodeASCII(b.toString()));
+ checker.checkTree(encodeASCII(b.toString()));
}
@Test
- public void testInvalidTreeNameIsDotGitSpace() {
+ public void testInvalidTreeNameIsDotGitSpace()
+ throws CorruptObjectException {
StringBuilder b = new StringBuilder();
entry(b, "100644 .git ");
- byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("invalid name '.git '", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("invalid name '.git '", OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(HAS_DOTGIT, true);
+ checker.checkTree(data);
}
@Test
@@ -1466,7 +1175,7 @@ public class ObjectCheckerTest {
throws CorruptObjectException {
StringBuilder b = new StringBuilder();
entry(b, "100644 .gitfoobar");
- byte[] data = Constants.encodeASCII(b.toString());
+ byte[] data = encodeASCII(b.toString());
checker.checkTree(data);
}
@@ -1475,7 +1184,7 @@ public class ObjectCheckerTest {
throws CorruptObjectException {
StringBuilder b = new StringBuilder();
entry(b, "100644 .gitfoo bar");
- byte[] data = Constants.encodeASCII(b.toString());
+ byte[] data = encodeASCII(b.toString());
checker.checkTree(data);
}
@@ -1484,7 +1193,7 @@ public class ObjectCheckerTest {
throws CorruptObjectException {
StringBuilder b = new StringBuilder();
entry(b, "100644 .gitfoobar.");
- byte[] data = Constants.encodeASCII(b.toString());
+ byte[] data = encodeASCII(b.toString());
checker.checkTree(data);
}
@@ -1493,251 +1202,236 @@ public class ObjectCheckerTest {
throws CorruptObjectException {
StringBuilder b = new StringBuilder();
entry(b, "100644 .gitfoobar..");
- byte[] data = Constants.encodeASCII(b.toString());
+ byte[] data = encodeASCII(b.toString());
checker.checkTree(data);
}
@Test
- public void testInvalidTreeNameIsDotGitDotSpace() {
+ public void testInvalidTreeNameIsDotGitDotSpace()
+ throws CorruptObjectException {
StringBuilder b = new StringBuilder();
entry(b, "100644 .git. ");
- byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("invalid name '.git. '", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("invalid name '.git. '", OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(HAS_DOTGIT, true);
+ checker.checkTree(data);
}
@Test
- public void testInvalidTreeNameIsDotGitSpaceDot() {
+ public void testInvalidTreeNameIsDotGitSpaceDot()
+ throws CorruptObjectException {
StringBuilder b = new StringBuilder();
entry(b, "100644 .git . ");
- byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("invalid name '.git . '", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("invalid name '.git . '", OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(HAS_DOTGIT, true);
+ checker.checkTree(data);
}
@Test
- public void testInvalidTreeNameIsGITTilde1() {
+ public void testInvalidTreeNameIsGITTilde1() throws CorruptObjectException {
StringBuilder b = new StringBuilder();
entry(b, "100644 GIT~1");
- byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("invalid name 'GIT~1'", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("invalid name 'GIT~1'", OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(HAS_DOTGIT, true);
+ checker.checkTree(data);
}
@Test
- public void testInvalidTreeNameIsGiTTilde1() {
+ public void testInvalidTreeNameIsGiTTilde1() throws CorruptObjectException {
StringBuilder b = new StringBuilder();
entry(b, "100644 GiT~1");
- byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("invalid name 'GiT~1'", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("invalid name 'GiT~1'", OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(HAS_DOTGIT, true);
+ checker.checkTree(data);
}
@Test
public void testValidTreeNameIsGitTilde11() throws CorruptObjectException {
StringBuilder b = new StringBuilder();
entry(b, "100644 GIT~11");
- byte[] data = Constants.encodeASCII(b.toString());
+ byte[] data = encodeASCII(b.toString());
checker.checkTree(data);
}
@Test
public void testInvalidTreeTruncatedInName() {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
b.append("100644 b");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("truncated in name", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("truncated in name", OBJ_TREE, data);
+ assertSkipListRejects("truncated in name", OBJ_TREE, data);
}
@Test
public void testInvalidTreeTruncatedInObjectId() {
- final StringBuilder b = new StringBuilder();
+ StringBuilder b = new StringBuilder();
b.append("100644 b\0\1\2");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("truncated in object id", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("truncated in object id", OBJ_TREE, data);
+ assertSkipListRejects("truncated in object id", OBJ_TREE, data);
}
@Test
- public void testInvalidTreeBadSorting1() {
- final StringBuilder b = new StringBuilder();
+ public void testInvalidTreeBadSorting1() throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
entry(b, "100644 foobar");
entry(b, "100644 fooaaa");
- final byte[] data = Constants.encodeASCII(b.toString());
+ byte[] data = encodeASCII(b.toString());
+
+ assertCorrupt("incorrectly sorted", OBJ_TREE, data);
+
+ ObjectId id = idFor(OBJ_TREE, data);
try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
+ checker.check(id, OBJ_TREE, data);
+ fail("Did not throw CorruptObjectException");
} catch (CorruptObjectException e) {
- assertEquals("incorrectly sorted", e.getMessage());
+ assertSame(TREE_NOT_SORTED, e.getErrorType());
+ assertEquals("treeNotSorted: object " + id.name()
+ + ": incorrectly sorted", e.getMessage());
}
+
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(TREE_NOT_SORTED, true);
+ checker.checkTree(data);
}
@Test
- public void testInvalidTreeBadSorting2() {
- final StringBuilder b = new StringBuilder();
+ public void testInvalidTreeBadSorting2() throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
entry(b, "40000 a");
entry(b, "100644 a.c");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("incorrectly sorted", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("incorrectly sorted", OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(TREE_NOT_SORTED, true);
+ checker.checkTree(data);
}
@Test
- public void testInvalidTreeBadSorting3() {
- final StringBuilder b = new StringBuilder();
+ public void testInvalidTreeBadSorting3() throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
entry(b, "100644 a0c");
entry(b, "40000 a");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("incorrectly sorted", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("incorrectly sorted", OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(TREE_NOT_SORTED, true);
+ checker.checkTree(data);
}
@Test
- public void testInvalidTreeDuplicateNames1() {
- final StringBuilder b = new StringBuilder();
+ public void testInvalidTreeDuplicateNames1_File()
+ throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
entry(b, "100644 a");
entry(b, "100644 a");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("duplicate entry names", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("duplicate entry names", OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(DUPLICATE_ENTRIES, true);
+ checker.checkTree(data);
+ }
+
+ @Test
+ public void testInvalidTreeDuplicateNames1_Tree()
+ throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
+ entry(b, "40000 a");
+ entry(b, "40000 a");
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("duplicate entry names", OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(DUPLICATE_ENTRIES, true);
+ checker.checkTree(data);
}
@Test
- public void testInvalidTreeDuplicateNames2() {
- final StringBuilder b = new StringBuilder();
+ public void testInvalidTreeDuplicateNames2() throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
entry(b, "100644 a");
entry(b, "100755 a");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("duplicate entry names", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("duplicate entry names", OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(DUPLICATE_ENTRIES, true);
+ checker.checkTree(data);
}
@Test
- public void testInvalidTreeDuplicateNames3() {
- final StringBuilder b = new StringBuilder();
+ public void testInvalidTreeDuplicateNames3() throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
entry(b, "100644 a");
entry(b, "40000 a");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("duplicate entry names", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("duplicate entry names", OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(DUPLICATE_ENTRIES, true);
+ checker.checkTree(data);
}
@Test
- public void testInvalidTreeDuplicateNames4() {
- final StringBuilder b = new StringBuilder();
+ public void testInvalidTreeDuplicateNames4() throws CorruptObjectException {
+ StringBuilder b = new StringBuilder();
entry(b, "100644 a");
entry(b, "100644 a.c");
entry(b, "100644 a.d");
entry(b, "100644 a.e");
entry(b, "40000 a");
entry(b, "100644 zoo");
- final byte[] data = Constants.encodeASCII(b.toString());
- try {
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("duplicate entry names", e.getMessage());
- }
+ byte[] data = encodeASCII(b.toString());
+ assertCorrupt("duplicate entry names", OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(DUPLICATE_ENTRIES, true);
+ checker.checkTree(data);
}
@Test
public void testInvalidTreeDuplicateNames5()
- throws UnsupportedEncodingException {
+ throws UnsupportedEncodingException, CorruptObjectException {
StringBuilder b = new StringBuilder();
- entry(b, "100644 a");
entry(b, "100644 A");
+ entry(b, "100644 a");
byte[] data = b.toString().getBytes("UTF-8");
- try {
- checker.setSafeForWindows(true);
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("duplicate entry names", e.getMessage());
- }
+ checker.setSafeForWindows(true);
+ assertCorrupt("duplicate entry names", OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(DUPLICATE_ENTRIES, true);
+ checker.checkTree(data);
}
@Test
public void testInvalidTreeDuplicateNames6()
- throws UnsupportedEncodingException {
+ throws UnsupportedEncodingException, CorruptObjectException {
StringBuilder b = new StringBuilder();
- entry(b, "100644 a");
entry(b, "100644 A");
+ entry(b, "100644 a");
byte[] data = b.toString().getBytes("UTF-8");
- try {
- checker.setSafeForMacOS(true);
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("duplicate entry names", e.getMessage());
- }
+ checker.setSafeForMacOS(true);
+ assertCorrupt("duplicate entry names", OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(DUPLICATE_ENTRIES, true);
+ checker.checkTree(data);
}
@Test
public void testInvalidTreeDuplicateNames7()
- throws UnsupportedEncodingException {
- try {
- Class.forName("java.text.Normalizer");
- } catch (ClassNotFoundException e) {
- // Ignore this test on Java 5 platform.
- return;
- }
-
+ throws UnsupportedEncodingException, CorruptObjectException {
StringBuilder b = new StringBuilder();
entry(b, "100644 \u0065\u0301");
entry(b, "100644 \u00e9");
byte[] data = b.toString().getBytes("UTF-8");
- try {
- checker.setSafeForMacOS(true);
- checker.checkTree(data);
- fail("incorrectly accepted an invalid tree");
- } catch (CorruptObjectException e) {
- assertEquals("duplicate entry names", e.getMessage());
- }
+ checker.setSafeForMacOS(true);
+ assertCorrupt("duplicate entry names", OBJ_TREE, data);
+ assertSkipListAccepts(OBJ_TREE, data);
+ checker.setIgnore(DUPLICATE_ENTRIES, true);
+ checker.checkTree(data);
}
@Test
@@ -1752,7 +1446,7 @@ public class ObjectCheckerTest {
@Test
public void testRejectNulInPathSegment() {
try {
- checker.checkPathSegment(Constants.encodeASCII("a\u0000b"), 0, 3);
+ checker.checkPathSegment(encodeASCII("a\u0000b"), 0, 3);
fail("incorrectly accepted NUL in middle of name");
} catch (CorruptObjectException e) {
assertEquals("name contains byte 0x00", e.getMessage());
@@ -1771,6 +1465,17 @@ public class ObjectCheckerTest {
}
@Test
+ public void testBug477090() throws CorruptObjectException {
+ checker.setSafeForMacOS(true);
+ final byte[] bytes = {
+ // U+221E 0xe2889e INFINITY ∞
+ (byte) 0xe2, (byte) 0x88, (byte) 0x9e,
+ // .html
+ 0x2e, 0x68, 0x74, 0x6d, 0x6c };
+ checker.checkPathSegment(bytes, 0, bytes.length);
+ }
+
+ @Test
public void testRejectDotAtEndOnWindows() {
checker.setSafeForWindows(true);
try {
@@ -1843,13 +1548,65 @@ public class ObjectCheckerTest {
private void checkOneName(String name) throws CorruptObjectException {
StringBuilder b = new StringBuilder();
entry(b, "100644 " + name);
- checker.checkTree(Constants.encodeASCII(b.toString()));
+ checker.checkTree(encodeASCII(b.toString()));
}
- private static void entry(final StringBuilder b, final String modeName) {
+ private static void entry(StringBuilder b, final String modeName) {
b.append(modeName);
b.append('\0');
- for (int i = 0; i < Constants.OBJECT_ID_LENGTH; i++)
+ for (int i = 0; i < OBJECT_ID_LENGTH; i++)
b.append((char) i);
}
+
+ private void assertCorrupt(String msg, int type, StringBuilder b) {
+ assertCorrupt(msg, type, encodeASCII(b.toString()));
+ }
+
+ private void assertCorrupt(String msg, int type, byte[] data) {
+ try {
+ checker.check(type, data);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException e) {
+ assertEquals(msg, e.getMessage());
+ }
+ }
+
+ private void assertSkipListAccepts(int type, byte[] data)
+ throws CorruptObjectException {
+ ObjectId id = idFor(type, data);
+ checker.setSkipList(set(id));
+ checker.check(id, type, data);
+ checker.setSkipList(null);
+ }
+
+ private void assertSkipListRejects(String msg, int type, byte[] data) {
+ ObjectId id = idFor(type, data);
+ checker.setSkipList(set(id));
+ try {
+ checker.check(id, type, data);
+ fail("Did not throw CorruptObjectException");
+ } catch (CorruptObjectException e) {
+ assertEquals(msg, e.getMessage());
+ }
+ checker.setSkipList(null);
+ }
+
+ private static ObjectIdSet set(final ObjectId... ids) {
+ return new ObjectIdSet() {
+ @Override
+ public boolean contains(AnyObjectId objectId) {
+ for (ObjectId id : ids) {
+ if (id.equals(objectId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+ }
+
+ @SuppressWarnings("resource")
+ private static ObjectId idFor(int type, byte[] raw) {
+ return new ObjectInserter.Formatter().idFor(type, raw);
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdTest.java
index abf57d6..2198b87 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdTest.java
@@ -49,6 +49,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import org.eclipse.jgit.errors.InvalidObjectIdException;
+
import org.junit.Test;
public class ObjectIdTest {
@@ -124,6 +126,21 @@ public class ObjectIdTest {
assertEquals(x.toLowerCase(), oid.name());
}
+ @Test(expected = InvalidObjectIdException.class)
+ public void testFromString_short() {
+ ObjectId.fromString("cafe1234");
+ }
+
+ @Test(expected = InvalidObjectIdException.class)
+ public void testFromString_nonHex() {
+ ObjectId.fromString("0123456789abcdefghij0123456789abcdefghij");
+ }
+
+ @Test(expected = InvalidObjectIdException.class)
+ public void testFromString_shortNonHex() {
+ ObjectId.fromString("6789ghij");
+ }
+
@Test
public void testGetByte() {
byte[] raw = new byte[20];
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
index f2ed684..707757b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
@@ -84,6 +84,12 @@ public class RefTest extends SampleDataRepositoryTestCase {
}
}
+ private void writeNewRef(String name, ObjectId value) throws IOException {
+ RefUpdate updateRef = db.updateRef(name);
+ updateRef.setNewObjectId(value);
+ assertEquals(RefUpdate.Result.NEW, updateRef.update());
+ }
+
@Test
public void testRemoteNames() throws Exception {
FileBasedConfig config = db.getConfig();
@@ -157,7 +163,7 @@ public class RefTest extends SampleDataRepositoryTestCase {
@Test
public void testReadSymRefToPacked() throws IOException {
writeSymref("HEAD", "refs/heads/b");
- Ref ref = db.getRef("HEAD");
+ Ref ref = db.exactRef("HEAD");
assertEquals(Ref.Storage.LOOSE, ref.getStorage());
assertTrue("is symref", ref.isSymbolic());
ref = ref.getTarget();
@@ -175,7 +181,7 @@ public class RefTest extends SampleDataRepositoryTestCase {
assertEquals(Result.FORCED, update); // internal
writeSymref("HEAD", "refs/heads/master");
- Ref ref = db.getRef("HEAD");
+ Ref ref = db.exactRef("HEAD");
assertEquals(Ref.Storage.LOOSE, ref.getStorage());
ref = ref.getTarget();
assertEquals("refs/heads/master", ref.getName());
@@ -188,10 +194,54 @@ public class RefTest extends SampleDataRepositoryTestCase {
updateRef.setNewObjectId(db.resolve("refs/heads/master"));
Result update = updateRef.update();
assertEquals(Result.NEW, update);
- Ref ref = db.getRef("ref/heads/new");
+ Ref ref = db.exactRef("ref/heads/new");
assertEquals(Storage.LOOSE, ref.getStorage());
}
+ @Test
+ public void testGetShortRef() throws IOException {
+ Ref ref = db.exactRef("refs/heads/master");
+ assertEquals("refs/heads/master", ref.getName());
+ assertEquals(db.resolve("refs/heads/master"), ref.getObjectId());
+ }
+
+ @Test
+ public void testGetShortExactRef() throws IOException {
+ assertNull(db.getRefDatabase().exactRef("master"));
+
+ Ref ref = db.getRefDatabase().exactRef("HEAD");
+ assertEquals("HEAD", ref.getName());
+ assertEquals("refs/heads/master", ref.getTarget().getName());
+ assertEquals(db.resolve("refs/heads/master"), ref.getObjectId());
+ }
+
+ @Test
+ public void testRefsUnderRefs() throws IOException {
+ ObjectId masterId = db.resolve("refs/heads/master");
+ writeNewRef("refs/heads/refs/foo/bar", masterId);
+
+ assertNull(db.getRefDatabase().exactRef("refs/foo/bar"));
+
+ Ref ref = db.findRef("refs/foo/bar");
+ assertEquals("refs/heads/refs/foo/bar", ref.getName());
+ assertEquals(db.resolve("refs/heads/master"), ref.getObjectId());
+ }
+
+ @Test
+ public void testAmbiguousRefsUnderRefs() throws IOException {
+ ObjectId masterId = db.resolve("refs/heads/master");
+ writeNewRef("refs/foo/bar", masterId);
+ writeNewRef("refs/heads/refs/foo/bar", masterId);
+
+ Ref exactRef = db.getRefDatabase().exactRef("refs/foo/bar");
+ assertEquals("refs/foo/bar", exactRef.getName());
+ assertEquals(masterId, exactRef.getObjectId());
+
+ Ref ref = db.findRef("refs/foo/bar");
+ assertEquals("refs/foo/bar", ref.getName());
+ assertEquals(masterId, ref.getObjectId());
+ }
+
/**
* Let an "outsider" create a loose ref with the same name as a packed one
*
@@ -201,7 +251,7 @@ public class RefTest extends SampleDataRepositoryTestCase {
@Test
public void testReadLoosePackedRef() throws IOException,
InterruptedException {
- Ref ref = db.getRef("refs/heads/master");
+ Ref ref = db.exactRef("refs/heads/master");
assertEquals(Storage.PACKED, ref.getStorage());
FileOutputStream os = new FileOutputStream(new File(db.getDirectory(),
"refs/heads/master"));
@@ -209,7 +259,7 @@ public class RefTest extends SampleDataRepositoryTestCase {
os.write('\n');
os.close();
- ref = db.getRef("refs/heads/master");
+ ref = db.exactRef("refs/heads/master");
assertEquals(Storage.LOOSE, ref.getStorage());
}
@@ -221,7 +271,7 @@ public class RefTest extends SampleDataRepositoryTestCase {
*/
@Test
public void testReadSimplePackedRefSameRepo() throws IOException {
- Ref ref = db.getRef("refs/heads/master");
+ Ref ref = db.exactRef("refs/heads/master");
ObjectId pid = db.resolve("refs/heads/master^");
assertEquals(Storage.PACKED, ref.getStorage());
RefUpdate updateRef = db.updateRef("refs/heads/master");
@@ -230,19 +280,19 @@ public class RefTest extends SampleDataRepositoryTestCase {
Result update = updateRef.update();
assertEquals(Result.FORCED, update);
- ref = db.getRef("refs/heads/master");
+ ref = db.exactRef("refs/heads/master");
assertEquals(Storage.LOOSE, ref.getStorage());
}
@Test
public void testResolvedNamesBranch() throws IOException {
- Ref ref = db.getRef("a");
+ Ref ref = db.findRef("a");
assertEquals("refs/heads/a", ref.getName());
}
@Test
public void testResolvedSymRef() throws IOException {
- Ref ref = db.getRef(Constants.HEAD);
+ Ref ref = db.exactRef(Constants.HEAD);
assertEquals(Constants.HEAD, ref.getName());
assertTrue("is symbolic ref", ref.isSymbolic());
assertSame(Ref.Storage.LOOSE, ref.getStorage());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java
index cf8d42f..7b6d7d4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ReflogConfigTest.java
@@ -109,14 +109,11 @@ public class ReflogConfigTest extends RepositoryTestCase {
commit.setAuthor(author);
commit.setCommitter(committer);
commit.setMessage(commitMsg);
- ObjectInserter inserter = db.newObjectInserter();
ObjectId id;
- try {
+ try (ObjectInserter inserter = db.newObjectInserter()) {
commit.setTreeId(inserter.insert(new TreeFormatter()));
id = inserter.insert(commit);
inserter.flush();
- } finally {
- inserter.release();
}
int nl = commitMsg.indexOf('\n');
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryCacheTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryCacheTest.java
index 0cab987..6c62925 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryCacheTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryCacheTest.java
@@ -43,11 +43,13 @@
package org.eclipse.jgit.lib;
+import static org.hamcrest.CoreMatchers.hasItem;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -147,4 +149,28 @@ public class RepositoryCacheTest extends RepositoryTestCase {
d2.close();
d2.close();
}
+
+ @Test
+ public void testGetRegisteredWhenEmpty() {
+ assertEquals(0, RepositoryCache.getRegisteredKeys().size());
+ }
+
+ @Test
+ public void testGetRegistered() {
+ RepositoryCache.register(db);
+
+ assertThat(RepositoryCache.getRegisteredKeys(),
+ hasItem(FileKey.exact(db.getDirectory(), db.getFS())));
+ assertEquals(1, RepositoryCache.getRegisteredKeys().size());
+ }
+
+ @Test
+ public void testUnregister() {
+ RepositoryCache.register(db);
+ RepositoryCache
+ .unregister(FileKey.exact(db.getDirectory(), db.getFS()));
+
+ assertEquals(0, RepositoryCache.getRegisteredKeys().size());
+ }
+
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0001_PersonIdentTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0001_PersonIdentTest.java
index 315c495..1515a07 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0001_PersonIdentTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0001_PersonIdentTest.java
@@ -75,11 +75,13 @@ public class T0001_PersonIdentTest {
p.toExternalString());
}
+ @SuppressWarnings("unused")
@Test(expected = IllegalArgumentException.class)
public void nullForNameShouldThrowIllegalArgumentException() {
new PersonIdent(null, "author at example.com");
}
+ @SuppressWarnings("unused")
@Test(expected = IllegalArgumentException.class)
public void nullForEmailShouldThrowIllegalArgumentException() {
new PersonIdent("A U Thor", null);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0002_TreeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0002_TreeTest.java
deleted file mode 100644
index 651e62c..0000000
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0002_TreeTest.java
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg at dewire.com>
- * Copyright (C) 2006-2008, Shawn O. Pearce <spearce at spearce.org>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
-import org.junit.Test;
-
- at SuppressWarnings("deprecation")
-public class T0002_TreeTest extends SampleDataRepositoryTestCase {
- private static final ObjectId SOME_FAKE_ID = ObjectId.fromString(
- "0123456789abcdef0123456789abcdef01234567");
-
- private static int compareNamesUsingSpecialCompare(String a, String b)
- throws UnsupportedEncodingException {
- char lasta = '\0';
- byte[] abytes;
- if (a.length() > 0 && a.charAt(a.length()-1) == '/') {
- lasta = '/';
- a = a.substring(0, a.length() - 1);
- }
- abytes = a.getBytes("ISO-8859-1");
- char lastb = '\0';
- byte[] bbytes;
- if (b.length() > 0 && b.charAt(b.length()-1) == '/') {
- lastb = '/';
- b = b.substring(0, b.length() - 1);
- }
- bbytes = b.getBytes("ISO-8859-1");
- return Tree.compareNames(abytes, bbytes, lasta, lastb);
- }
-
- @Test
- public void test000_sort_01() throws UnsupportedEncodingException {
- assertEquals(0, compareNamesUsingSpecialCompare("a","a"));
- }
-
- @Test
- public void test000_sort_02() throws UnsupportedEncodingException {
- assertEquals(-1, compareNamesUsingSpecialCompare("a","b"));
- assertEquals(1, compareNamesUsingSpecialCompare("b","a"));
- }
-
- @Test
- public void test000_sort_03() throws UnsupportedEncodingException {
- assertEquals(1, compareNamesUsingSpecialCompare("a:","a"));
- assertEquals(1, compareNamesUsingSpecialCompare("a/","a"));
- assertEquals(-1, compareNamesUsingSpecialCompare("a","a/"));
- assertEquals(-1, compareNamesUsingSpecialCompare("a","a:"));
- assertEquals(1, compareNamesUsingSpecialCompare("a:","a/"));
- assertEquals(-1, compareNamesUsingSpecialCompare("a/","a:"));
- }
-
- @Test
- public void test000_sort_04() throws UnsupportedEncodingException {
- assertEquals(-1, compareNamesUsingSpecialCompare("a.a","a/a"));
- assertEquals(1, compareNamesUsingSpecialCompare("a/a","a.a"));
- }
-
- @Test
- public void test000_sort_05() throws UnsupportedEncodingException {
- assertEquals(-1, compareNamesUsingSpecialCompare("a.","a/"));
- assertEquals(1, compareNamesUsingSpecialCompare("a/","a."));
-
- }
-
- @Test
- public void test001_createEmpty() throws IOException {
- final Tree t = new Tree(db);
- assertTrue("isLoaded", t.isLoaded());
- assertTrue("isModified", t.isModified());
- assertTrue("no parent", t.getParent() == null);
- assertTrue("isRoot", t.isRoot());
- assertTrue("no name", t.getName() == null);
- assertTrue("no nameUTF8", t.getNameUTF8() == null);
- assertTrue("has entries array", t.members() != null);
- assertEquals("entries is empty", 0, t.members().length);
- assertEquals("full name is empty", "", t.getFullName());
- assertTrue("no id", t.getId() == null);
- assertTrue("database is r", t.getRepository() == db);
- assertTrue("no foo child", t.findTreeMember("foo") == null);
- assertTrue("no foo child", t.findBlobMember("foo") == null);
- }
-
- @Test
- public void test002_addFile() throws IOException {
- final Tree t = new Tree(db);
- t.setId(SOME_FAKE_ID);
- assertTrue("has id", t.getId() != null);
- assertFalse("not modified", t.isModified());
-
- final String n = "bob";
- final FileTreeEntry f = t.addFile(n);
- assertNotNull("have file", f);
- assertEquals("name matches", n, f.getName());
- assertEquals("name matches", f.getName(), new String(f.getNameUTF8(),
- "UTF-8"));
- assertEquals("full name matches", n, f.getFullName());
- assertTrue("no id", f.getId() == null);
- assertTrue("is modified", t.isModified());
- assertTrue("has no id", t.getId() == null);
- assertTrue("found bob", t.findBlobMember(f.getName()) == f);
-
- final TreeEntry[] i = t.members();
- assertNotNull("members array not null", i);
- assertTrue("iterator is not empty", i != null && i.length > 0);
- assertTrue("iterator returns file", i != null && i[0] == f);
- assertTrue("iterator is empty", i != null && i.length == 1);
- }
-
- @Test
- public void test004_addTree() throws IOException {
- final Tree t = new Tree(db);
- t.setId(SOME_FAKE_ID);
- assertTrue("has id", t.getId() != null);
- assertFalse("not modified", t.isModified());
-
- final String n = "bob";
- final Tree f = t.addTree(n);
- assertNotNull("have tree", f);
- assertEquals("name matches", n, f.getName());
- assertEquals("name matches", f.getName(), new String(f.getNameUTF8(),
- "UTF-8"));
- assertEquals("full name matches", n, f.getFullName());
- assertTrue("no id", f.getId() == null);
- assertTrue("parent matches", f.getParent() == t);
- assertTrue("repository matches", f.getRepository() == db);
- assertTrue("isLoaded", f.isLoaded());
- assertFalse("has items", f.members().length > 0);
- assertFalse("is root", f.isRoot());
- assertTrue("parent is modified", t.isModified());
- assertTrue("parent has no id", t.getId() == null);
- assertTrue("found bob child", t.findTreeMember(f.getName()) == f);
-
- final TreeEntry[] i = t.members();
- assertTrue("iterator is not empty", i.length > 0);
- assertTrue("iterator returns file", i[0] == f);
- assertEquals("iterator is empty", 1, i.length);
- }
-
- @Test
- public void test005_addRecursiveFile() throws IOException {
- final Tree t = new Tree(db);
- final FileTreeEntry f = t.addFile("a/b/c");
- assertNotNull("created f", f);
- assertEquals("c", f.getName());
- assertEquals("b", f.getParent().getName());
- assertEquals("a", f.getParent().getParent().getName());
- assertTrue("t is great-grandparent", t == f.getParent().getParent()
- .getParent());
- }
-
- @Test
- public void test005_addRecursiveTree() throws IOException {
- final Tree t = new Tree(db);
- final Tree f = t.addTree("a/b/c");
- assertNotNull("created f", f);
- assertEquals("c", f.getName());
- assertEquals("b", f.getParent().getName());
- assertEquals("a", f.getParent().getParent().getName());
- assertTrue("t is great-grandparent", t == f.getParent().getParent()
- .getParent());
- }
-
- @Test
- public void test006_addDeepTree() throws IOException {
- final Tree t = new Tree(db);
-
- final Tree e = t.addTree("e");
- assertNotNull("have e", e);
- assertTrue("e.parent == t", e.getParent() == t);
- final Tree f = t.addTree("f");
- assertNotNull("have f", f);
- assertTrue("f.parent == t", f.getParent() == t);
- final Tree g = f.addTree("g");
- assertNotNull("have g", g);
- assertTrue("g.parent == f", g.getParent() == f);
- final Tree h = g.addTree("h");
- assertNotNull("have h", h);
- assertTrue("h.parent = g", h.getParent() == g);
-
- h.setId(SOME_FAKE_ID);
- assertTrue("h not modified", !h.isModified());
- g.setId(SOME_FAKE_ID);
- assertTrue("g not modified", !g.isModified());
- f.setId(SOME_FAKE_ID);
- assertTrue("f not modified", !f.isModified());
- e.setId(SOME_FAKE_ID);
- assertTrue("e not modified", !e.isModified());
- t.setId(SOME_FAKE_ID);
- assertTrue("t not modified.", !t.isModified());
-
- assertEquals("full path of h ok", "f/g/h", h.getFullName());
- assertTrue("Can find h", t.findTreeMember(h.getFullName()) == h);
- assertTrue("Can't find f/z", t.findBlobMember("f/z") == null);
- assertTrue("Can't find y/z", t.findBlobMember("y/z") == null);
-
- final FileTreeEntry i = h.addFile("i");
- assertNotNull(i);
- assertEquals("full path of i ok", "f/g/h/i", i.getFullName());
- assertTrue("Can find i", t.findBlobMember(i.getFullName()) == i);
- assertTrue("h modified", h.isModified());
- assertTrue("g modified", g.isModified());
- assertTrue("f modified", f.isModified());
- assertTrue("e not modified", !e.isModified());
- assertTrue("t modified", t.isModified());
-
- assertTrue("h no id", h.getId() == null);
- assertTrue("g no id", g.getId() == null);
- assertTrue("f no id", f.getId() == null);
- assertTrue("e has id", e.getId() != null);
- assertTrue("t no id", t.getId() == null);
- }
-
- @Test
- public void test007_manyFileLookup() throws IOException {
- final Tree t = new Tree(db);
- final List<FileTreeEntry> files = new ArrayList<FileTreeEntry>(26 * 26);
- for (char level1 = 'a'; level1 <= 'z'; level1++) {
- for (char level2 = 'a'; level2 <= 'z'; level2++) {
- final String n = "." + level1 + level2 + "9";
- final FileTreeEntry f = t.addFile(n);
- assertNotNull("File " + n + " added.", f);
- assertEquals(n, f.getName());
- files.add(f);
- }
- }
- assertEquals(files.size(), t.memberCount());
- final TreeEntry[] ents = t.members();
- assertNotNull(ents);
- assertEquals(files.size(), ents.length);
- for (int k = 0; k < ents.length; k++) {
- assertTrue("File " + files.get(k).getName()
- + " is at " + k + ".", files.get(k) == ents[k]);
- }
- }
-
- @Test
- public void test008_SubtreeInternalSorting() throws IOException {
- final Tree t = new Tree(db);
- final FileTreeEntry e0 = t.addFile("a-b");
- final FileTreeEntry e1 = t.addFile("a-");
- final FileTreeEntry e2 = t.addFile("a=b");
- final Tree e3 = t.addTree("a");
- final FileTreeEntry e4 = t.addFile("a=");
-
- final TreeEntry[] ents = t.members();
- assertSame(e1, ents[0]);
- assertSame(e0, ents[1]);
- assertSame(e3, ents[2]);
- assertSame(e4, ents[3]);
- assertSame(e2, ents[4]);
- }
-
- @Test
- public void test009_SymlinkAndGitlink() throws IOException {
- final Tree symlinkTree = mapTree("symlink");
- assertTrue("Symlink entry exists", symlinkTree.existsBlob("symlink.txt"));
- final Tree gitlinkTree = mapTree("gitlink");
- assertTrue("Gitlink entry exists", gitlinkTree.existsBlob("submodule"));
- }
-
- private Tree mapTree(String name) throws IOException {
- ObjectId id = db.resolve(name + "^{tree}");
- return new Tree(db, id, db.open(id).getCachedBytes());
- }
-}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmTest.java
index 8a33425..d4a3d62 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeAlgorithmTest.java
@@ -51,11 +51,25 @@ import java.io.IOException;
import org.eclipse.jgit.diff.RawText;
import org.eclipse.jgit.diff.RawTextComparator;
import org.eclipse.jgit.lib.Constants;
+import org.junit.Assume;
import org.junit.Test;
+import org.junit.experimental.theories.DataPoints;
+import org.junit.experimental.theories.Theories;
+import org.junit.runner.RunWith;
+ at RunWith(Theories.class)
public class MergeAlgorithmTest {
MergeFormatter fmt=new MergeFormatter();
+ private final boolean newlineAtEnd;
+
+ @DataPoints
+ public static boolean[] newlineAtEndDataPoints = { false, true };
+
+ public MergeAlgorithmTest(boolean newlineAtEnd) {
+ this.newlineAtEnd = newlineAtEnd;
+ }
+
/**
* Check for a conflict where the second text was changed similar to the
* first one, but the second texts modification covers one more line.
@@ -174,28 +188,55 @@ public class MergeAlgorithmTest {
}
@Test
- public void testSeperateModifications() throws IOException {
+ public void testSeparateModifications() throws IOException {
assertEquals(t("aZcYe"), merge("abcde", "aZcde", "abcYe"));
}
+ @Test
+ public void testBlankLines() throws IOException {
+ assertEquals(t("aZc\nYe"), merge("abc\nde", "aZc\nde", "abc\nYe"));
+ }
+
/**
* Test merging two contents which do one similar modification and one
- * insertion is only done by one side. Between modification and insertion is
- * a block which is common between the two contents and the common base
+ * insertion is only done by one side, in the middle. Between modification
+ * and insertion is a block which is common between the two contents and the
+ * common base
*
* @throws IOException
*/
@Test
public void testTwoSimilarModsAndOneInsert() throws IOException {
- assertEquals(t("IAAJ"), merge("iA", "IA", "IAAJ"));
assertEquals(t("aBcDde"), merge("abcde", "aBcde", "aBcDde"));
- assertEquals(t("IAJ"), merge("iA", "IA", "IAJ"));
- assertEquals(t("IAAAJ"), merge("iA", "IA", "IAAAJ"));
assertEquals(t("IAAAJCAB"), merge("iACAB", "IACAB", "IAAAJCAB"));
assertEquals(t("HIAAAJCAB"), merge("HiACAB", "HIACAB", "HIAAAJCAB"));
assertEquals(t("AGADEFHIAAAJCAB"),
merge("AGADEFHiACAB", "AGADEFHIACAB", "AGADEFHIAAAJCAB"));
+ }
+ /**
+ * Test merging two contents which do one similar modification and one
+ * insertion is only done by one side, at the end. Between modification and
+ * insertion is a block which is common between the two contents and the
+ * common base
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testTwoSimilarModsAndOneInsertAtEnd() throws IOException {
+ Assume.assumeTrue(newlineAtEnd);
+ assertEquals(t("IAAJ"), merge("iA", "IA", "IAAJ"));
+ assertEquals(t("IAJ"), merge("iA", "IA", "IAJ"));
+ assertEquals(t("IAAAJ"), merge("iA", "IA", "IAAAJ"));
+ }
+
+ @Test
+ public void testTwoSimilarModsAndOneInsertAtEndNoNewlineAtEnd()
+ throws IOException {
+ Assume.assumeFalse(newlineAtEnd);
+ assertEquals(t("I<A=AAJ>"), merge("iA", "IA", "IAAJ"));
+ assertEquals(t("I<A=AJ>"), merge("iA", "IA", "IAJ"));
+ assertEquals(t("I<A=AAAJ>"), merge("iA", "IA", "IAAAJ"));
}
/**
@@ -225,7 +266,7 @@ public class MergeAlgorithmTest {
return new String(bo.toByteArray(), Constants.CHARACTER_ENCODING);
}
- public static String t(String text) {
+ public String t(String text) {
StringBuilder r = new StringBuilder();
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
@@ -241,13 +282,14 @@ public class MergeAlgorithmTest {
break;
default:
r.append(c);
- r.append('\n');
+ if (newlineAtEnd || i < text.length() - 1)
+ r.append('\n');
}
}
return r.toString();
}
- public static RawText T(String text) {
+ public RawText T(String text) {
return new RawText(Constants.encode(t(text)));
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java
index 21ef747..6c90f7d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java
@@ -84,44 +84,44 @@ public class MergeMessageFormatterTest extends SampleDataRepositoryTestCase {
@Test
public void testOneBranch() throws IOException {
- Ref a = db.getRef("refs/heads/a");
- Ref master = db.getRef("refs/heads/master");
+ Ref a = db.exactRef("refs/heads/a");
+ Ref master = db.exactRef("refs/heads/master");
String message = formatter.format(Arrays.asList(a), master);
assertEquals("Merge branch 'a'", message);
}
@Test
public void testTwoBranches() throws IOException {
- Ref a = db.getRef("refs/heads/a");
- Ref b = db.getRef("refs/heads/b");
- Ref master = db.getRef("refs/heads/master");
+ Ref a = db.exactRef("refs/heads/a");
+ Ref b = db.exactRef("refs/heads/b");
+ Ref master = db.exactRef("refs/heads/master");
String message = formatter.format(Arrays.asList(a, b), master);
assertEquals("Merge branches 'a' and 'b'", message);
}
@Test
public void testThreeBranches() throws IOException {
- Ref c = db.getRef("refs/heads/c");
- Ref b = db.getRef("refs/heads/b");
- Ref a = db.getRef("refs/heads/a");
- Ref master = db.getRef("refs/heads/master");
+ Ref c = db.exactRef("refs/heads/c");
+ Ref b = db.exactRef("refs/heads/b");
+ Ref a = db.exactRef("refs/heads/a");
+ Ref master = db.exactRef("refs/heads/master");
String message = formatter.format(Arrays.asList(c, b, a), master);
assertEquals("Merge branches 'c', 'b' and 'a'", message);
}
@Test
public void testRemoteBranch() throws Exception {
- Ref remoteA = db.getRef("refs/remotes/origin/remote-a");
- Ref master = db.getRef("refs/heads/master");
+ Ref remoteA = db.exactRef("refs/remotes/origin/remote-a");
+ Ref master = db.exactRef("refs/heads/master");
String message = formatter.format(Arrays.asList(remoteA), master);
assertEquals("Merge remote-tracking branch 'origin/remote-a'", message);
}
@Test
public void testMixed() throws IOException {
- Ref c = db.getRef("refs/heads/c");
- Ref remoteA = db.getRef("refs/remotes/origin/remote-a");
- Ref master = db.getRef("refs/heads/master");
+ Ref c = db.exactRef("refs/heads/c");
+ Ref remoteA = db.exactRef("refs/remotes/origin/remote-a");
+ Ref master = db.exactRef("refs/heads/master");
String message = formatter.format(Arrays.asList(c, remoteA), master);
assertEquals("Merge branch 'c', remote-tracking branch 'origin/remote-a'",
message);
@@ -129,8 +129,8 @@ public class MergeMessageFormatterTest extends SampleDataRepositoryTestCase {
@Test
public void testTag() throws IOException {
- Ref tagA = db.getRef("refs/tags/A");
- Ref master = db.getRef("refs/heads/master");
+ Ref tagA = db.exactRef("refs/tags/A");
+ Ref master = db.exactRef("refs/heads/master");
String message = formatter.format(Arrays.asList(tagA), master);
assertEquals("Merge tag 'A'", message);
}
@@ -141,7 +141,7 @@ public class MergeMessageFormatterTest extends SampleDataRepositoryTestCase {
.fromString("6db9c2ebf75590eef973081736730a9ea169a0c4");
Ref commit = new ObjectIdRef.Unpeeled(Storage.LOOSE,
objectId.getName(), objectId);
- Ref master = db.getRef("refs/heads/master");
+ Ref master = db.exactRef("refs/heads/master");
String message = formatter.format(Arrays.asList(commit), master);
assertEquals("Merge commit '6db9c2ebf75590eef973081736730a9ea169a0c4'",
message);
@@ -154,7 +154,7 @@ public class MergeMessageFormatterTest extends SampleDataRepositoryTestCase {
.fromString("6db9c2ebf75590eef973081736730a9ea169a0c4");
Ref remoteBranch = new ObjectIdRef.Unpeeled(Storage.LOOSE, name,
objectId);
- Ref master = db.getRef("refs/heads/master");
+ Ref master = db.exactRef("refs/heads/master");
String message = formatter.format(Arrays.asList(remoteBranch), master);
assertEquals("Merge branch 'test' of http://egit.eclipse.org/jgit.git",
message);
@@ -162,16 +162,16 @@ public class MergeMessageFormatterTest extends SampleDataRepositoryTestCase {
@Test
public void testIntoOtherThanMaster() throws IOException {
- Ref a = db.getRef("refs/heads/a");
- Ref b = db.getRef("refs/heads/b");
+ Ref a = db.exactRef("refs/heads/a");
+ Ref b = db.exactRef("refs/heads/b");
String message = formatter.format(Arrays.asList(a), b);
assertEquals("Merge branch 'a' into b", message);
}
@Test
public void testIntoHeadOtherThanMaster() throws IOException {
- Ref a = db.getRef("refs/heads/a");
- Ref b = db.getRef("refs/heads/b");
+ Ref a = db.exactRef("refs/heads/a");
+ Ref b = db.exactRef("refs/heads/b");
SymbolicRef head = new SymbolicRef("HEAD", b);
String message = formatter.format(Arrays.asList(a), head);
assertEquals("Merge branch 'a' into b", message);
@@ -179,8 +179,8 @@ public class MergeMessageFormatterTest extends SampleDataRepositoryTestCase {
@Test
public void testIntoSymbolicRefHeadPointingToMaster() throws IOException {
- Ref a = db.getRef("refs/heads/a");
- Ref master = db.getRef("refs/heads/master");
+ Ref a = db.exactRef("refs/heads/a");
+ Ref master = db.exactRef("refs/heads/master");
SymbolicRef head = new SymbolicRef("HEAD", master);
String message = formatter.format(Arrays.asList(a), head);
assertEquals("Merge branch 'a'", message);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/RecursiveMergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/RecursiveMergerTest.java
index 37cc88b..7ef6448 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/RecursiveMergerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/RecursiveMergerTest.java
@@ -60,6 +60,7 @@ import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.NoMergeBaseException;
import org.eclipse.jgit.errors.NoMergeBaseException.MergeBaseFailureReason;
import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
@@ -177,6 +178,69 @@ public class RecursiveMergerTest extends RepositoryTestCase {
@Theory
/**
+ * Merging m2,s2 from the following topology. m1 and s1 are the two root
+ * commits of the repo. In master and side different files are touched.
+ * No need to do a real content merge.
+ *
+ * <pre>
+ * m1--m2
+ * \/
+ * /\
+ * s1--s2
+ * </pre>
+ */
+ public void crissCrossMerge_twoRoots(MergeStrategy strategy,
+ IndexState indexState, WorktreeState worktreeState)
+ throws Exception {
+ if (!validateStates(indexState, worktreeState))
+ return;
+ // fill the repo
+ BranchBuilder master = db_t.branch("master");
+ BranchBuilder side = db_t.branch("side");
+ RevCommit m1 = master.commit().add("m", "m1").message("m1").create();
+ db_t.getRevWalk().parseCommit(m1);
+
+ RevCommit s1 = side.commit().add("s", "s1").message("s1").create();
+ RevCommit s2 = side.commit().parent(m1).add("m", "m1")
+ .message("s2(merge)").create();
+ RevCommit m2 = master.commit().parent(s1).add("s", "s1")
+ .message("m2(merge)").create();
+
+ Git git = Git.wrap(db);
+ git.checkout().setName("master").call();
+ modifyWorktree(worktreeState, "m", "side");
+ modifyWorktree(worktreeState, "s", "side");
+ modifyIndex(indexState, "m", "side");
+ modifyIndex(indexState, "s", "side");
+
+ ResolveMerger merger = (ResolveMerger) strategy.newMerger(db,
+ worktreeState == WorktreeState.Bare);
+ if (worktreeState != WorktreeState.Bare)
+ merger.setWorkingTreeIterator(new FileTreeIterator(db));
+ try {
+ boolean expectSuccess = true;
+ if (!(indexState == IndexState.Bare
+ || indexState == IndexState.Missing
+ || indexState == IndexState.SameAsHead || indexState == IndexState.SameAsOther))
+ // index is dirty
+ expectSuccess = false;
+
+ assertEquals(Boolean.valueOf(expectSuccess),
+ Boolean.valueOf(merger.merge(new RevCommit[] { m2, s2 })));
+ assertEquals(MergeStrategy.RECURSIVE, strategy);
+ assertEquals("m1",
+ contentAsString(db, merger.getResultTreeId(), "m"));
+ assertEquals("s1",
+ contentAsString(db, merger.getResultTreeId(), "s"));
+ } catch (NoMergeBaseException e) {
+ assertEquals(MergeStrategy.RESOLVE, strategy);
+ assertEquals(e.getReason(),
+ MergeBaseFailureReason.MULTIPLE_MERGE_BASES_NOT_SUPPORTED);
+ }
+ }
+
+ @Theory
+ /**
* Merging m2,s2 from the following topology. The same file is modified
* in both branches. The modifications should be mergeable. m2 and s2
* contain branch specific conflict resolutions. Therefore m2 and s2 don't contain the same content.
@@ -245,7 +309,7 @@ public class RecursiveMergerTest extends RepositoryTestCase {
if (indexState != IndexState.Bare)
assertEquals(
"[f, mode:100644, content:1-master\n2\n3-res(master)\n4\n5\n6\n7-res(side)\n8\n9-side\n]",
- indexState(RepositoryTestCase.CONTENT));
+ indexState(LocalDiskRepositoryTestCase.CONTENT));
if (worktreeState != WorktreeState.Bare
&& worktreeState != WorktreeState.Missing)
assertEquals(
@@ -330,7 +394,7 @@ public class RecursiveMergerTest extends RepositoryTestCase {
if (indexState != IndexState.Bare)
assertEquals(
"[f, mode:100644, content:1-master-r\n2\n3-side-r\n]",
- indexState(RepositoryTestCase.CONTENT));
+ indexState(LocalDiskRepositoryTestCase.CONTENT));
if (worktreeState != WorktreeState.Bare
&& worktreeState != WorktreeState.Missing)
assertEquals(
@@ -415,7 +479,7 @@ public class RecursiveMergerTest extends RepositoryTestCase {
if (indexState != IndexState.Bare)
assertEquals(
"[f, mode:100644, content:1\nx(side)\n2\n3\ny(side-again)\n]",
- indexState(RepositoryTestCase.CONTENT));
+ indexState(LocalDiskRepositoryTestCase.CONTENT));
if (worktreeState != WorktreeState.Bare
&& worktreeState != WorktreeState.Missing)
assertEquals("1\nx(side)\n2\n3\ny(side-again)\n", read("f"));
@@ -498,7 +562,7 @@ public class RecursiveMergerTest extends RepositoryTestCase {
if (indexState != IndexState.Bare)
assertEquals(
"[f, mode:100644, content:1-master-r\n2\n3-side-r\n][m.c, mode:100644, content:0][m.m, mode:100644, content:1][s.c, mode:100644, content:0][s.m, mode:100644, content:1]",
- indexState(RepositoryTestCase.CONTENT));
+ indexState(LocalDiskRepositoryTestCase.CONTENT));
if (worktreeState != WorktreeState.Bare
&& worktreeState != WorktreeState.Missing) {
assertEquals(
@@ -575,7 +639,7 @@ public class RecursiveMergerTest extends RepositoryTestCase {
"[f, mode:100644, stage:1, content:1-master\n2\n3\n4\n5\n6\n7\n8\n9-side\n]"
+ "[f, mode:100644, stage:2, content:1-master\n2\n3\n4\n5\n6\n7-conflict\n8\n9-side\n]"
+ "[f, mode:100644, stage:3, content:1-master\n2\n3\n4\n5\n6\n7-res(side)\n8\n9-side\n]",
- indexState(RepositoryTestCase.CONTENT));
+ indexState(LocalDiskRepositoryTestCase.CONTENT));
assertEquals(
"1-master\n2\n3\n4\n5\n6\n<<<<<<< OURS\n7-conflict\n=======\n7-res(side)\n>>>>>>> THEIRS\n8\n9-side\n",
read("f"));
@@ -673,7 +737,7 @@ public class RecursiveMergerTest extends RepositoryTestCase {
if (indexState != IndexState.Bare)
assertEquals(
"[f, mode:100644, content:1-master\n2\n3-res(master)\n4\n5-other\n6\n7-res(side)\n8\n9-side\n]",
- indexState(RepositoryTestCase.CONTENT));
+ indexState(LocalDiskRepositoryTestCase.CONTENT));
if (worktreeState != WorktreeState.Bare
&& worktreeState != WorktreeState.Missing)
assertEquals(
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java
index dd06168..674619f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java
@@ -184,7 +184,7 @@ public class ResolveMergerTest extends RepositoryTestCase {
MergeResult mergeRes = git.merge().setStrategy(strategy)
.include(masterCommit).call();
assertEquals(MergeStatus.MERGED, mergeRes.getMergeStatus());
- assertEquals("[d/1, mode:100644, content:1master\n2\n3side\n]",
+ assertEquals("[d/1, mode:100644, content:1master\n2\n3side]",
indexState(CONTENT));
}
@@ -227,6 +227,80 @@ public class ResolveMergerTest extends RepositoryTestCase {
}
/**
+ * A tracked file is replaced by a folder in THEIRS.
+ *
+ * @param strategy
+ * @throws Exception
+ */
+ @Theory
+ public void checkFileReplacedByFolderInTheirs(MergeStrategy strategy)
+ throws Exception {
+ Git git = Git.wrap(db);
+
+ writeTrashFile("sub", "file");
+ git.add().addFilepattern("sub").call();
+ RevCommit first = git.commit().setMessage("initial").call();
+
+ git.checkout().setCreateBranch(true).setStartPoint(first)
+ .setName("side").call();
+
+ git.rm().addFilepattern("sub").call();
+ writeTrashFile("sub/file", "subfile");
+ git.add().addFilepattern("sub/file").call();
+ RevCommit masterCommit = git.commit().setMessage("file -> folder")
+ .call();
+
+ git.checkout().setName("master").call();
+ writeTrashFile("noop", "other");
+ git.add().addFilepattern("noop").call();
+ git.commit().setAll(true).setMessage("noop").call();
+
+ MergeResult mergeRes = git.merge().setStrategy(strategy)
+ .include(masterCommit).call();
+ assertEquals(MergeStatus.MERGED, mergeRes.getMergeStatus());
+ assertEquals(
+ "[noop, mode:100644, content:other][sub/file, mode:100644, content:subfile]",
+ indexState(CONTENT));
+ }
+
+ /**
+ * A tracked file is replaced by a folder in OURS.
+ *
+ * @param strategy
+ * @throws Exception
+ */
+ @Theory
+ public void checkFileReplacedByFolderInOurs(MergeStrategy strategy)
+ throws Exception {
+ Git git = Git.wrap(db);
+
+ writeTrashFile("sub", "file");
+ git.add().addFilepattern("sub").call();
+ RevCommit first = git.commit().setMessage("initial").call();
+
+ git.checkout().setCreateBranch(true).setStartPoint(first)
+ .setName("side").call();
+ writeTrashFile("noop", "other");
+ git.add().addFilepattern("noop").call();
+ RevCommit sideCommit = git.commit().setAll(true).setMessage("noop")
+ .call();
+
+ git.checkout().setName("master").call();
+ git.rm().addFilepattern("sub").call();
+ writeTrashFile("sub/file", "subfile");
+ git.add().addFilepattern("sub/file").call();
+ git.commit().setMessage("file -> folder")
+ .call();
+
+ MergeResult mergeRes = git.merge().setStrategy(strategy)
+ .include(sideCommit).call();
+ assertEquals(MergeStatus.MERGED, mergeRes.getMergeStatus());
+ assertEquals(
+ "[noop, mode:100644, content:other][sub/file, mode:100644, content:subfile]",
+ indexState(CONTENT));
+ }
+
+ /**
* An existing directory without tracked content should not prevent merging
* a file with that name.
*
@@ -556,12 +630,12 @@ public class ResolveMergerTest extends RepositoryTestCase {
// ResolveMerge
try {
MergeResult mergeResult = git.merge().setStrategy(strategy)
- .include(git.getRepository().getRef("refs/heads/side"))
+ .include(git.getRepository().exactRef("refs/heads/side"))
.call();
assertEquals(MergeStrategy.RECURSIVE, strategy);
assertEquals(MergeResult.MergeStatus.MERGED,
mergeResult.getMergeStatus());
- assertEquals("1master2\n2\n3side2\n", read("1"));
+ assertEquals("1master2\n2\n3side2", read("1"));
} catch (JGitInternalException e) {
assertEquals(MergeStrategy.RESOLVE, strategy);
assertTrue(e.getCause() instanceof NoMergeBaseException);
@@ -697,7 +771,7 @@ public class ResolveMergerTest extends RepositoryTestCase {
assertEquals(
"[0, mode:100644, content:master]" //
+ "[1, mode:100644, content:side]" //
- + "[2, mode:100644, content:1master\n2\n3side\n]" //
+ + "[2, mode:100644, content:1master\n2\n3side]" //
+ "[3, mode:100644, stage:1, content:orig][3, mode:100644, stage:2, content:side][3, mode:100644, stage:3, content:master]" //
+ "[4, mode:100644, content:orig]", //
indexState(CONTENT));
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SquashMessageFormatterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SquashMessageFormatterTest.java
index b7b2291..cddbbd5 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SquashMessageFormatterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SquashMessageFormatterTest.java
@@ -76,7 +76,7 @@ public class SquashMessageFormatterTest extends SampleDataRepositoryTestCase {
Git git = new Git(db);
revCommit = git.commit().setMessage("squash_me").call();
- Ref master = db.getRef("refs/heads/master");
+ Ref master = db.exactRef("refs/heads/master");
String message = msgFormatter.format(Arrays.asList(revCommit), master);
assertEquals(
"Squashed commit of the following:\n\ncommit "
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/nls/RootLocaleTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/nls/RootLocaleTest.java
index cae006c..631ed85 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/nls/RootLocaleTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/nls/RootLocaleTest.java
@@ -44,7 +44,6 @@
package org.eclipse.jgit.nls;
import org.eclipse.jgit.awtui.UIText;
-import org.eclipse.jgit.console.ConsoleText;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.pgm.internal.CLIText;
import org.junit.Before;
@@ -62,11 +61,6 @@ public class RootLocaleTest {
}
@Test
- public void testConsoleText() {
- NLS.getBundleFor(ConsoleText.class);
- }
-
- @Test
public void testCLIText() {
NLS.getBundleFor(CLIText.class);
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/DefaultNoteMergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/DefaultNoteMergerTest.java
index 91608ea..d3a6f18 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/DefaultNoteMergerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/DefaultNoteMergerTest.java
@@ -86,8 +86,8 @@ public class DefaultNoteMergerTest extends RepositoryTestCase {
@Override
@After
public void tearDown() throws Exception {
- reader.release();
- inserter.release();
+ reader.close();
+ inserter.close();
super.tearDown();
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/NoteMapMergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/NoteMapMergerTest.java
index 8010fb1..be7bead 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/NoteMapMergerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/NoteMapMergerTest.java
@@ -127,8 +127,8 @@ public class NoteMapMergerTest extends RepositoryTestCase {
@Override
@After
public void tearDown() throws Exception {
- reader.release();
- inserter.release();
+ reader.close();
+ inserter.close();
super.tearDown();
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/NoteMapTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/NoteMapTest.java
index c2f63d6..4539a01 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/NoteMapTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/NoteMapTest.java
@@ -91,8 +91,8 @@ public class NoteMapTest extends RepositoryTestCase {
@Override
@After
public void tearDown() throws Exception {
- reader.release();
- inserter.release();
+ reader.close();
+ inserter.close();
super.tearDown();
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java
index 0ad3b77..fbd5127 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java
@@ -360,10 +360,10 @@ public class FooterLineTest extends RepositoryTestCase {
buf.append("\n");
buf.append(msg);
- final RevWalk walk = new RevWalk(db);
- walk.setRetainBody(true);
- final RevCommit c = new RevCommit(ObjectId.zeroId());
- c.parseCanonical(walk, Constants.encode(buf.toString()));
- return c;
+ try (RevWalk walk = new RevWalk(db)) {
+ RevCommit c = new RevCommit(ObjectId.zeroId());
+ c.parseCanonical(walk, Constants.encode(buf.toString()));
+ return c;
+ }
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ObjectWalkFilterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ObjectWalkFilterTest.java
new file mode 100644
index 0000000..55117b7
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ObjectWalkFilterTest.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.revwalk;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Sets;
+import org.eclipse.jgit.revwalk.ObjectWalk;
+import org.eclipse.jgit.revwalk.filter.MessageRevFilter;
+import org.eclipse.jgit.revwalk.filter.NotRevFilter;
+import org.eclipse.jgit.revwalk.filter.ObjectFilter;
+
+import java.io.IOException;
+import java.util.Set;
+
+public class ObjectWalkFilterTest {
+ private TestRepository<InMemoryRepository> tr;
+ private ObjectWalk rw;
+
+ // 3 commits, 2 top-level trees, 4 subtrees, 3 blobs
+ private static final int OBJECT_COUNT = 12;
+
+ @Before
+ public void setUp() throws Exception {
+ tr = new TestRepository<>(new InMemoryRepository(
+ new DfsRepositoryDescription("test")));
+ rw = new ObjectWalk(tr.getRepository());
+
+ rw.markStart(tr.branch("master").commit()
+ .add("a/a", "1")
+ .add("b/b", "2")
+ .add("c/c", "3")
+ .message("initial commit")
+
+ .child()
+ .rm("a/a")
+ .add("a/A", "1")
+ .message("capitalize a/a")
+
+ .child()
+ .rm("a/A")
+ .add("a/a", "1")
+ .message("make a/A lowercase again")
+ .create());
+ }
+
+ @After
+ public void tearDown() {
+ rw.close();
+ tr.getRepository().close();
+ }
+
+ private static class BlacklistObjectFilter extends ObjectFilter {
+ final Set<AnyObjectId> badObjects;
+
+ BlacklistObjectFilter(Set<AnyObjectId> badObjects) {
+ this.badObjects = badObjects;
+ }
+
+ @Override
+ public boolean include(ObjectWalk walker, AnyObjectId o) {
+ return !badObjects.contains(o);
+ }
+ }
+
+ private AnyObjectId resolve(String revstr) throws Exception {
+ return tr.getRepository().resolve(revstr);
+ }
+
+ private int countObjects() throws IOException {
+ int n = 0;
+ while (rw.next() != null) {
+ n++;
+ }
+ while (rw.nextObject() != null) {
+ n++;
+ }
+ return n;
+ }
+
+ @Test
+ public void testDefaultFilter() throws Exception {
+ assertTrue("filter is ALL",
+ rw.getObjectFilter() == ObjectFilter.ALL);
+ assertEquals(OBJECT_COUNT, countObjects());
+ }
+
+ @Test
+ public void testObjectFilterCanFilterOutBlob() throws Exception {
+ AnyObjectId one = rw.parseAny(resolve("master:a/a"));
+ AnyObjectId two = rw.parseAny(resolve("master:b/b"));
+ rw.setObjectFilter(new BlacklistObjectFilter(Sets.of(one, two)));
+
+ // 2 blobs filtered out
+ assertEquals(OBJECT_COUNT - 2, countObjects());
+ }
+
+ @Test
+ public void testFilteringCommitsHasNoEffect() throws Exception {
+ AnyObjectId initial = rw.parseCommit(resolve("master^^"));
+ rw.setObjectFilter(new BlacklistObjectFilter(Sets.of(initial)));
+ assertEquals(OBJECT_COUNT, countObjects());
+ }
+
+ @Test
+ public void testRevFilterAndObjectFilterCanCombine() throws Exception {
+ AnyObjectId one = rw.parseAny(resolve("master:a/a"));
+ AnyObjectId two = rw.parseAny(resolve("master:b/b"));
+ rw.setObjectFilter(new BlacklistObjectFilter(Sets.of(one, two)));
+ rw.setRevFilter(NotRevFilter.create(
+ MessageRevFilter.create("capitalize")));
+
+ // 2 blobs, one commit, two trees filtered out
+ assertEquals(OBJECT_COUNT - 5, countObjects());
+ }
+
+ @Test
+ public void testFilteringTreeFiltersSubtrees() throws Exception {
+ AnyObjectId capitalizeTree = rw.parseAny(resolve("master^:"));
+ rw.setObjectFilter(new BlacklistObjectFilter(
+ Sets.of(capitalizeTree)));
+
+ // trees "master^:" and "master^:a" filtered out
+ assertEquals(OBJECT_COUNT - 2, countObjects());
+ }
+
+ @Test
+ public void testFilteringTreeFiltersReferencedBlobs() throws Exception {
+ AnyObjectId a1 = rw.parseAny(resolve("master:a"));
+ AnyObjectId a2 = rw.parseAny(resolve("master^:a"));
+ rw.setObjectFilter(new BlacklistObjectFilter(Sets.of(a1, a2)));
+
+ // 2 trees, one blob filtered out
+ assertEquals(OBJECT_COUNT - 3, countObjects());
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ObjectWalkTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ObjectWalkTest.java
index dfde7fc..9c9edc1 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ObjectWalkTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/ObjectWalkTest.java
@@ -47,11 +47,10 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.FileTreeEntry;
+import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.Tree;
+import org.eclipse.jgit.lib.TreeFormatter;
import org.junit.Test;
@SuppressWarnings("deprecation")
@@ -220,31 +219,24 @@ public class ObjectWalkTest extends RevWalkTestCase {
.fromString("abbbfafe3129f85747aba7bfac992af77134c607");
final RevTree tree_root, tree_A, tree_AB;
final RevCommit b;
- {
- Tree root = new Tree(db);
- Tree A = root.addTree("A");
- FileTreeEntry B = root.addFile("B");
- B.setId(bId);
-
- Tree A_A = A.addTree("A");
- Tree A_B = A.addTree("B");
-
- final ObjectInserter inserter = db.newObjectInserter();
- try {
- A_A.setId(inserter.insert(Constants.OBJ_TREE, A_A.format()));
- A_B.setId(inserter.insert(Constants.OBJ_TREE, A_B.format()));
- A.setId(inserter.insert(Constants.OBJ_TREE, A.format()));
- root.setId(inserter.insert(Constants.OBJ_TREE, root.format()));
- inserter.flush();
- } finally {
- inserter.release();
- }
-
- tree_root = rw.parseTree(root.getId());
- tree_A = rw.parseTree(A.getId());
- tree_AB = rw.parseTree(A_A.getId());
- assertSame(tree_AB, rw.parseTree(A_B.getId()));
- b = commit(rw.parseTree(root.getId()));
+ try (ObjectInserter inserter = db.newObjectInserter()) {
+ ObjectId empty = inserter.insert(new TreeFormatter());
+
+ TreeFormatter A = new TreeFormatter();
+ A.append("A", FileMode.TREE, empty);
+ A.append("B", FileMode.TREE, empty);
+ ObjectId idA = inserter.insert(A);
+
+ TreeFormatter root = new TreeFormatter();
+ root.append("A", FileMode.TREE, idA);
+ root.append("B", FileMode.REGULAR_FILE, bId);
+ ObjectId idRoot = inserter.insert(root);
+ inserter.flush();
+
+ tree_root = objw.parseTree(idRoot);
+ tree_A = objw.parseTree(idA);
+ tree_AB = objw.parseTree(empty);
+ b = commit(tree_root);
}
markStart(b);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java
index beda2a7..885c1b5 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java
@@ -43,13 +43,18 @@
package org.eclipse.jgit.revwalk;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
+import java.nio.charset.IllegalCharsetNameException;
+import java.nio.charset.UnsupportedCharsetException;
import java.util.TimeZone;
import org.eclipse.jgit.junit.RepositoryTestCase;
@@ -304,6 +309,86 @@ public class RevCommitParseTest extends RepositoryTestCase {
}
@Test
+ public void testParse_incorrectUtf8Name() throws Exception {
+ ByteArrayOutputStream b = new ByteArrayOutputStream();
+ b.write("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n"
+ .getBytes(UTF_8));
+ b.write("author au <a at example.com> 1218123387 +0700\n".getBytes(UTF_8));
+ b.write("committer co <c at example.com> 1218123390 -0500\n"
+ .getBytes(UTF_8));
+ b.write("encoding 'utf8'\n".getBytes(UTF_8));
+ b.write("\n".getBytes(UTF_8));
+ b.write("Sm\u00f6rg\u00e5sbord\n".getBytes(UTF_8));
+
+ RevCommit c = new RevCommit(
+ id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
+ c.parseCanonical(new RevWalk(db), b.toByteArray());
+ assertEquals("'utf8'", c.getEncodingName());
+ assertEquals("Sm\u00f6rg\u00e5sbord\n", c.getFullMessage());
+
+ try {
+ c.getEncoding();
+ fail("Expected " + IllegalCharsetNameException.class);
+ } catch (IllegalCharsetNameException badName) {
+ assertEquals("'utf8'", badName.getMessage());
+ }
+ }
+
+ @Test
+ public void testParse_illegalEncoding() throws Exception {
+ ByteArrayOutputStream b = new ByteArrayOutputStream();
+ b.write("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n".getBytes(UTF_8));
+ b.write("author au <a at example.com> 1218123387 +0700\n".getBytes(UTF_8));
+ b.write("committer co <c at example.com> 1218123390 -0500\n".getBytes(UTF_8));
+ b.write("encoding utf-8logoutputencoding=gbk\n".getBytes(UTF_8));
+ b.write("\n".getBytes(UTF_8));
+ b.write("message\n".getBytes(UTF_8));
+
+ RevCommit c = new RevCommit(
+ id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
+ c.parseCanonical(new RevWalk(db), b.toByteArray());
+ assertEquals("utf-8logoutputencoding=gbk", c.getEncodingName());
+ assertEquals("message\n", c.getFullMessage());
+ assertEquals("message", c.getShortMessage());
+ assertTrue(c.getFooterLines().isEmpty());
+ assertEquals("au", c.getAuthorIdent().getName());
+
+ try {
+ c.getEncoding();
+ fail("Expected " + IllegalCharsetNameException.class);
+ } catch (IllegalCharsetNameException badName) {
+ assertEquals("utf-8logoutputencoding=gbk", badName.getMessage());
+ }
+ }
+
+ @Test
+ public void testParse_unsupportedEncoding() throws Exception {
+ ByteArrayOutputStream b = new ByteArrayOutputStream();
+ b.write("tree 9788669ad918b6fcce64af8882fc9a81cb6aba67\n".getBytes(UTF_8));
+ b.write("author au <a at example.com> 1218123387 +0700\n".getBytes(UTF_8));
+ b.write("committer co <c at example.com> 1218123390 -0500\n".getBytes(UTF_8));
+ b.write("encoding it_IT.UTF8\n".getBytes(UTF_8));
+ b.write("\n".getBytes(UTF_8));
+ b.write("message\n".getBytes(UTF_8));
+
+ RevCommit c = new RevCommit(
+ id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
+ c.parseCanonical(new RevWalk(db), b.toByteArray());
+ assertEquals("it_IT.UTF8", c.getEncodingName());
+ assertEquals("message\n", c.getFullMessage());
+ assertEquals("message", c.getShortMessage());
+ assertTrue(c.getFooterLines().isEmpty());
+ assertEquals("au", c.getAuthorIdent().getName());
+
+ try {
+ c.getEncoding();
+ fail("Expected " + UnsupportedCharsetException.class);
+ } catch (UnsupportedCharsetException badName) {
+ assertEquals("it_IT.UTF8", badName.getMessage());
+ }
+ }
+
+ @Test
public void testParse_NoMessage() throws Exception {
final String msg = "";
final RevCommit c = create(msg);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevTagParseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevTagParseTest.java
index 614f49b..82505ca 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevTagParseTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevTagParseTest.java
@@ -43,6 +43,7 @@
package org.eclipse.jgit.revwalk;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -362,6 +363,44 @@ public class RevTagParseTest extends RepositoryTestCase {
}
@Test
+ public void testParse_illegalEncoding() throws Exception {
+ ByteArrayOutputStream b = new ByteArrayOutputStream();
+ b.write("object 9788669ad918b6fcce64af8882fc9a81cb6aba67\n".getBytes(UTF_8));
+ b.write("type tree\n".getBytes(UTF_8));
+ b.write("tag v1.0\n".getBytes(UTF_8));
+ b.write("tagger t <t at example.com> 1218123387 +0700\n".getBytes(UTF_8));
+ b.write("encoding utf-8logoutputencoding=gbk\n".getBytes(UTF_8));
+ b.write("\n".getBytes(UTF_8));
+ b.write("message\n".getBytes(UTF_8));
+
+ RevTag t = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
+ t.parseCanonical(new RevWalk(db), b.toByteArray());
+
+ assertEquals("t", t.getTaggerIdent().getName());
+ assertEquals("message", t.getShortMessage());
+ assertEquals("message\n", t.getFullMessage());
+ }
+
+ @Test
+ public void testParse_unsupportedEncoding() throws Exception {
+ ByteArrayOutputStream b = new ByteArrayOutputStream();
+ b.write("object 9788669ad918b6fcce64af8882fc9a81cb6aba67\n".getBytes(UTF_8));
+ b.write("type tree\n".getBytes(UTF_8));
+ b.write("tag v1.0\n".getBytes(UTF_8));
+ b.write("tagger t <t at example.com> 1218123387 +0700\n".getBytes(UTF_8));
+ b.write("encoding it_IT.UTF8\n".getBytes(UTF_8));
+ b.write("\n".getBytes(UTF_8));
+ b.write("message\n".getBytes(UTF_8));
+
+ RevTag t = new RevTag(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67"));
+ t.parseCanonical(new RevWalk(db), b.toByteArray());
+
+ assertEquals("t", t.getTaggerIdent().getName());
+ assertEquals("message", t.getShortMessage());
+ assertEquals("message\n", t.getFullMessage());
+ }
+
+ @Test
public void testParse_NoMessage() throws Exception {
final String msg = "";
final RevTag c = create(msg);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFilterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFilterTest.java
index 643ba26..8ab9728 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFilterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkFilterTest.java
@@ -250,14 +250,14 @@ public class RevWalkFilterTest extends RevWalkTestCase {
final RevCommit b = commit(a);
tick(100);
- Date since = getClock();
+ Date since = getDate();
final RevCommit c1 = commit(b);
tick(100);
final RevCommit c2 = commit(b);
tick(100);
- Date until = getClock();
+ Date until = getDate();
final RevCommit d = commit(c1, c2);
tick(100);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java
index 881deb1..30586ee 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkTestCase.java
@@ -70,8 +70,8 @@ public abstract class RevWalkTestCase extends RepositoryTestCase {
return new RevWalk(db);
}
- protected Date getClock() {
- return util.getClock();
+ protected Date getDate() {
+ return util.getDate();
}
protected void tick(final int secDelta) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkUtilsReachableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkUtilsReachableTest.java
index 855bb80..10bea0a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkUtilsReachableTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkUtilsReachableTest.java
@@ -103,12 +103,9 @@ public class RevWalkUtilsReachableTest extends RevWalkTestCase {
RevCommit a = commit();
Ref branchA = branch("a", a);
- RevWalk walk = new RevWalk(db);
- try {
+ try (RevWalk walk = new RevWalk(db)) {
RevCommit parsedCommit = walk.parseCommit(a.getId());
assertContains(parsedCommit, asList(branchA));
- } finally {
- walk.release();
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleInitTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleInitTest.java
index 22e55fe..2b46498 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleInitTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleInitTest.java
@@ -246,9 +246,8 @@ public class SubmoduleInitTest extends RepositoryTestCase {
if (File.separatorChar == '\\')
base = base.replace('\\', '/');
FileBasedConfig config = db.getConfig();
- config.setString(ConfigConstants.CONFIG_REMOTE_SECTION,
- Constants.DEFAULT_REMOTE_NAME, ConfigConstants.CONFIG_KEY_URL,
- null);
+ config.unset(ConfigConstants.CONFIG_REMOTE_SECTION,
+ Constants.DEFAULT_REMOTE_NAME, ConfigConstants.CONFIG_KEY_URL);
config.save();
SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java
index f7acaa7..72b4611 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java
@@ -65,6 +65,7 @@ import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.NoWorkTreeException;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
@@ -101,6 +102,13 @@ public class SubmoduleWalkTest extends RepositoryTestCase {
}
@Test
+ public void bareRepositoryWithNoSubmodules() throws IOException {
+ FileRepository bareRepo = createBareRepository();
+ boolean result = SubmoduleWalk.containsGitModulesFile(bareRepo);
+ assertFalse(result);
+ }
+
+ @Test
public void repositoryWithRootLevelSubmodule() throws IOException,
ConfigInvalidException, NoWorkTreeException, GitAPIException {
final ObjectId id = ObjectId
diff --git a/org.eclipse.jgit.java7.test/src/org/eclipse/jgit/symlinks/SymlinksTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/symlinks/SymlinksTest.java
similarity index 100%
rename from org.eclipse.jgit.java7.test/src/org/eclipse/jgit/symlinks/SymlinksTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/symlinks/SymlinksTest.java
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java
new file mode 100644
index 0000000..c1e078d
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
+import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
+import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class AtomicPushTest {
+ private URIish uri;
+ private TestProtocol<Object> testProtocol;
+ private Object ctx = new Object();
+ private InMemoryRepository server;
+ private InMemoryRepository client;
+ private ObjectId obj1;
+ private ObjectId obj2;
+
+ @Before
+ public void setUp() throws Exception {
+ server = newRepo("server");
+ client = newRepo("client");
+ testProtocol = new TestProtocol<>(
+ null,
+ new ReceivePackFactory<Object>() {
+ @Override
+ public ReceivePack create(Object req, Repository db)
+ throws ServiceNotEnabledException,
+ ServiceNotAuthorizedException {
+ return new ReceivePack(db);
+ }
+ });
+ uri = testProtocol.register(ctx, server);
+
+ try (ObjectInserter ins = client.newObjectInserter()) {
+ obj1 = ins.insert(Constants.OBJ_BLOB, Constants.encode("test"));
+ obj2 = ins.insert(Constants.OBJ_BLOB, Constants.encode("file"));
+ ins.flush();
+ }
+ }
+
+ @After
+ public void tearDown() {
+ Transport.unregister(testProtocol);
+ }
+
+ private static InMemoryRepository newRepo(String name) {
+ return new InMemoryRepository(new DfsRepositoryDescription(name));
+ }
+
+ @Test
+ public void pushNonAtomic() throws Exception {
+ PushResult r;
+ server.setPerformsAtomicTransactions(false);
+ try (Transport tn = testProtocol.open(uri, client, "server")) {
+ tn.setPushAtomic(false);
+ r = tn.push(NullProgressMonitor.INSTANCE, commands());
+ }
+
+ RemoteRefUpdate one = r.getRemoteUpdate("refs/heads/one");
+ RemoteRefUpdate two = r.getRemoteUpdate("refs/heads/two");
+ assertSame(RemoteRefUpdate.Status.OK, one.getStatus());
+ assertSame(
+ RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED,
+ two.getStatus());
+ }
+
+ @Test
+ public void pushAtomicClientGivesUpEarly() throws Exception {
+ PushResult r;
+ try (Transport tn = testProtocol.open(uri, client, "server")) {
+ tn.setPushAtomic(true);
+ r = tn.push(NullProgressMonitor.INSTANCE, commands());
+ }
+
+ RemoteRefUpdate one = r.getRemoteUpdate("refs/heads/one");
+ RemoteRefUpdate two = r.getRemoteUpdate("refs/heads/two");
+ assertSame(
+ RemoteRefUpdate.Status.REJECTED_OTHER_REASON,
+ one.getStatus());
+ assertSame(
+ RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED,
+ two.getStatus());
+ assertEquals(JGitText.get().transactionAborted, one.getMessage());
+ }
+
+ @Test
+ public void pushAtomicDisabled() throws Exception {
+ List<RemoteRefUpdate> cmds = new ArrayList<>();
+ cmds.add(new RemoteRefUpdate(
+ null, null,
+ obj1, "refs/heads/one",
+ true /* force update */,
+ null /* no local tracking ref */,
+ ObjectId.zeroId()));
+ cmds.add(new RemoteRefUpdate(
+ null, null,
+ obj2, "refs/heads/two",
+ true /* force update */,
+ null /* no local tracking ref */,
+ ObjectId.zeroId()));
+
+ server.setPerformsAtomicTransactions(false);
+ try (Transport tn = testProtocol.open(uri, client, "server")) {
+ tn.setPushAtomic(true);
+ tn.push(NullProgressMonitor.INSTANCE, cmds);
+ fail("did not throw TransportException");
+ } catch (TransportException e) {
+ assertEquals(
+ uri + ": " + JGitText.get().atomicPushNotSupported,
+ e.getMessage());
+ }
+ }
+
+ private List<RemoteRefUpdate> commands() throws IOException {
+ List<RemoteRefUpdate> cmds = new ArrayList<>();
+ cmds.add(new RemoteRefUpdate(
+ null, null,
+ obj1, "refs/heads/one",
+ true /* force update */,
+ null /* no local tracking ref */,
+ ObjectId.zeroId()));
+ cmds.add(new RemoteRefUpdate(
+ null, null,
+ obj2, "refs/heads/two",
+ true /* force update */,
+ null /* no local tracking ref */,
+ obj1));
+ return cmds;
+ }
+}
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BaseReceivePackTest.java
similarity index 56%
copy from org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ConfigTest.java
copy to org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BaseReceivePackTest.java
index aefdff1..7578c6e 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BaseReceivePackTest.java
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2012, Matthias Sohn <matthias.sohn at sap.com>
- * and other copyright owners as documented in the project's IP log.
+ * Copyright (C) 2015, Google Inc.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
@@ -40,49 +39,47 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.pgm;
-import static org.junit.Assert.assertEquals;
+package org.eclipse.jgit.transport;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.lib.CLIRepositoryTestCase;
-import org.eclipse.jgit.util.FS;
-import org.eclipse.jgit.util.SystemReader;
-import org.junit.Before;
+import org.eclipse.jgit.errors.PackProtocolException;
+import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
-public class ConfigTest extends CLIRepositoryTestCase {
- @Override
- @Before
- public void setUp() throws Exception {
- super.setUp();
- new Git(db).commit().setMessage("initial commit").call();
- }
-
+/** Tests for base receive-pack utilities. */
+public class BaseReceivePackTest {
@Test
- public void testListConfig() throws Exception {
- boolean isWindows = SystemReader.getInstance().getProperty("os.name")
- .startsWith("Windows");
- boolean isMac = SystemReader.getInstance().getProperty("os.name")
- .equals("Mac OS X");
+ public void parseCommand() throws Exception {
+ String o = "0000000000000000000000000000000000000000";
+ String n = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
+ String r = "refs/heads/master";
+ ReceiveCommand cmd = BaseReceivePack.parseCommand(o + " " + n + " " + r);
+ assertEquals(ObjectId.zeroId(), cmd.getOldId());
+ assertEquals("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
+ cmd.getNewId().name());
+ assertEquals("refs/heads/master", cmd.getRefName());
- String[] output = execute("git config --list");
- List<String> expect = new ArrayList<String>();
- expect.add("core.filemode=" + !isWindows);
- expect.add("core.logallrefupdates=true");
- if (isMac)
- expect.add("core.precomposeunicode=true");
- expect.add("core.repositoryformatversion=0");
- if (!FS.DETECTED.supportsSymlinks())
- expect.add("core.symlinks=false");
- expect.add(""); // ends with LF (last line empty)
- assertEquals("expected default configuration",
- Arrays.asList(expect.toArray()).toString(),
- Arrays.asList(output).toString());
+ assertParseCommandFails(null);
+ assertParseCommandFails("");
+ assertParseCommandFails(o.substring(35) + " " + n.substring(35)
+ + " " + r + "\n");
+ assertParseCommandFails(o + " " + n + " " + r + "\n");
+ assertParseCommandFails(o + " " + n + " " + "refs^foo");
+ assertParseCommandFails(o + " " + n.substring(10) + " " + r);
+ assertParseCommandFails(o.substring(10) + " " + n + " " + r);
+ assertParseCommandFails("X" + o.substring(1) + " " + n + " " + r);
+ assertParseCommandFails(o + " " + "X" + n.substring(1) + " " + r);
}
+ private void assertParseCommandFails(String input) {
+ try {
+ BaseReceivePack.parseCommand(input);
+ fail();
+ } catch (PackProtocolException e) {
+ // Expected.
+ }
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java
index 24cee0a..a83a993 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/BundleWriterTest.java
@@ -126,25 +126,39 @@ public class BundleWriterTest extends SampleDataRepositoryTestCase {
assertNull(newRepo.resolve("refs/heads/a"));
// Next an incremental bundle
- bundle = makeBundle("refs/heads/cc", db.resolve("c").name(),
- new RevWalk(db).parseCommit(db.resolve("a").toObjectId()));
- fetchResult = fetchFromBundle(newRepo, bundle);
- advertisedRef = fetchResult.getAdvertisedRef("refs/heads/cc");
- assertEquals(db.resolve("c").name(), advertisedRef.getObjectId().name());
- assertEquals(db.resolve("c").name(), newRepo.resolve("refs/heads/cc")
- .name());
- assertNull(newRepo.resolve("refs/heads/c"));
- assertNull(newRepo.resolve("refs/heads/a")); // still unknown
+ try (RevWalk rw = new RevWalk(db)) {
+ bundle = makeBundle("refs/heads/cc", db.resolve("c").name(),
+ rw.parseCommit(db.resolve("a").toObjectId()));
+ fetchResult = fetchFromBundle(newRepo, bundle);
+ advertisedRef = fetchResult.getAdvertisedRef("refs/heads/cc");
+ assertEquals(db.resolve("c").name(), advertisedRef.getObjectId().name());
+ assertEquals(db.resolve("c").name(), newRepo.resolve("refs/heads/cc")
+ .name());
+ assertNull(newRepo.resolve("refs/heads/c"));
+ assertNull(newRepo.resolve("refs/heads/a")); // still unknown
+
+ try {
+ // Check that we actually needed the first bundle
+ Repository newRepo2 = createBareRepository();
+ fetchResult = fetchFromBundle(newRepo2, bundle);
+ fail("We should not be able to fetch from bundle with prerequisites that are not fulfilled");
+ } catch (MissingBundlePrerequisiteException e) {
+ assertTrue(e.getMessage()
+ .indexOf(db.resolve("refs/heads/a").name()) >= 0);
+ }
+ }
+ }
+ @Test
+ public void testAbortWrite() throws Exception {
+ boolean caught = false;
try {
- // Check that we actually needed the first bundle
- Repository newRepo2 = createBareRepository();
- fetchResult = fetchFromBundle(newRepo2, bundle);
- fail("We should not be able to fetch from bundle with prerequisites that are not fulfilled");
- } catch (MissingBundlePrerequisiteException e) {
- assertTrue(e.getMessage()
- .indexOf(db.resolve("refs/heads/a").name()) >= 0);
+ makeBundleWithCallback(
+ "refs/heads/aa", db.resolve("a").name(), null, false);
+ } catch (WriteAbortedException e) {
+ caught = true;
}
+ assertTrue(caught);
}
private static FetchResult fetchFromBundle(final Repository newRepo,
@@ -154,16 +168,26 @@ public class BundleWriterTest extends SampleDataRepositoryTestCase {
final ByteArrayInputStream in = new ByteArrayInputStream(bundle);
final RefSpec rs = new RefSpec("refs/heads/*:refs/heads/*");
final Set<RefSpec> refs = Collections.singleton(rs);
- return new TransportBundleStream(newRepo, uri, in).fetch(
- NullProgressMonitor.INSTANCE, refs);
+ try (TransportBundleStream transport = new TransportBundleStream(
+ newRepo, uri, in)) {
+ return transport.fetch(NullProgressMonitor.INSTANCE, refs);
+ }
}
private byte[] makeBundle(final String name,
final String anObjectToInclude, final RevCommit assume)
throws FileNotFoundException, IOException {
+ return makeBundleWithCallback(name, anObjectToInclude, assume, true);
+ }
+
+ private byte[] makeBundleWithCallback(final String name,
+ final String anObjectToInclude, final RevCommit assume,
+ boolean value)
+ throws FileNotFoundException, IOException {
final BundleWriter bw;
bw = new BundleWriter(db);
+ bw.setObjectCountCallback(new NaiveObjectCountCallback(value));
bw.include(name, ObjectId.fromString(anObjectToInclude));
if (assume != null)
bw.assume(assume);
@@ -172,4 +196,19 @@ public class BundleWriterTest extends SampleDataRepositoryTestCase {
return out.toByteArray();
}
+ private static class NaiveObjectCountCallback
+ implements ObjectCountCallback {
+ private final boolean value;
+
+ NaiveObjectCountCallback(boolean value) {
+ this.value = value;
+ }
+
+ @Override
+ public void setObjectCount(long unused) throws WriteAbortedException {
+ if (!value)
+ throw new WriteAbortedException();
+ }
+ }
+
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HMACSHA1NonceGeneratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HMACSHA1NonceGeneratorTest.java
new file mode 100644
index 0000000..1e79b7a
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/HMACSHA1NonceGeneratorTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.PushCertificate.NonceStatus;
+import org.junit.Before;
+import org.junit.Test;
+
+/** Test for HMAC SHA-1 certificate verifier. */
+public class HMACSHA1NonceGeneratorTest {
+ private static final long TS = 1433954361;
+
+ private HMACSHA1NonceGenerator gen;
+ private Repository db;
+
+ @Before
+ public void setUp() {
+ gen = new HMACSHA1NonceGenerator("sekret");
+ db = new InMemoryRepository(new DfsRepositoryDescription("db"));
+ }
+
+ @Test
+ public void missing() throws Exception {
+ assertEquals(NonceStatus.MISSING, gen.verify("", "1234", db, false, 0));
+ }
+
+ @Test
+ public void unsolicited() throws Exception {
+ assertEquals(NonceStatus.UNSOLICITED, gen.verify("1234", "", db, false, 0));
+ }
+
+ @Test
+ public void invalidFormat() throws Exception {
+ String sent = gen.createNonce(db, TS);
+ int idx = sent.indexOf('-');
+ String sig = sent.substring(idx, sent.length() - idx);
+ assertEquals(NonceStatus.BAD,
+ gen.verify(Long.toString(TS), sent, db, true, 100));
+ assertEquals(NonceStatus.BAD, gen.verify(sig, sent, db, true, 100));
+ assertEquals(NonceStatus.BAD, gen.verify("xxx-" + sig, sent, db, true, 100));
+ assertEquals(NonceStatus.BAD, gen.verify(sent, "xxx-" + sig, db, true, 100));
+ }
+
+ @Test
+ public void slop() throws Exception {
+ String sent = gen.createNonce(db, TS - 10);
+ String received = gen.createNonce(db, TS);
+ assertEquals(NonceStatus.BAD,
+ gen.verify(received, sent, db, false, 0));
+ assertEquals(NonceStatus.BAD,
+ gen.verify(received, sent, db, false, 11));
+ assertEquals(NonceStatus.SLOP,
+ gen.verify(received, sent, db, true, 0));
+ assertEquals(NonceStatus.SLOP,
+ gen.verify(received, sent, db, true, 9));
+ assertEquals(NonceStatus.OK,
+ gen.verify(received, sent, db, true, 10));
+ assertEquals(NonceStatus.OK,
+ gen.verify(received, sent, db, true, 11));
+ }
+
+ @Test
+ public void ok() throws Exception {
+ String sent = gen.createNonce(db, TS);
+ assertEquals(NonceStatus.OK, gen.verify(sent, sent, db, false, 0));
+ }
+
+ @Test
+ public void signedByDifferentKey() throws Exception {
+ HMACSHA1NonceGenerator other = new HMACSHA1NonceGenerator("other");
+ String sent = gen.createNonce(db, TS);
+ String received = other.createNonce(db, TS);
+ assertNotEquals(received, sent);
+ assertEquals(NonceStatus.BAD,
+ gen.verify(received, sent, db, false, 0));
+ }
+
+ @Test
+ public void signedByDifferentKeyWithSlop() throws Exception {
+ HMACSHA1NonceGenerator other = new HMACSHA1NonceGenerator("other");
+ String sent = gen.createNonce(db, TS - 10);
+ String received = other.createNonce(db, TS);
+ assertEquals(NonceStatus.BAD, gen.verify(received, sent, db, true, 100));
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java
index 6859dd5..c829be9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PackParserTest.java
@@ -486,8 +486,9 @@ public class PackParserTest extends RepositoryTestCase {
@After
public void release() {
- if (inserter != null)
- inserter.release();
+ if (inserter != null) {
+ inserter.close();
+ }
}
private PackParser index(InputStream in) throws IOException {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateIdentTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateIdentTest.java
new file mode 100644
index 0000000..68aff72
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateIdentTest.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import static org.eclipse.jgit.transport.PushCertificateIdent.parse;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Date;
+import java.util.TimeZone;
+
+import org.eclipse.jgit.lib.PersonIdent;
+import org.junit.Test;
+
+public class PushCertificateIdentTest {
+ @Test
+ public void parseValid() throws Exception {
+ String raw = "A U. Thor <a_u_thor at example.com> 1218123387 +0700";
+ PushCertificateIdent ident = parse(raw);
+ assertEquals(raw, ident.getRaw());
+ assertEquals("A U. Thor <a_u_thor at example.com>", ident.getUserId());
+ assertEquals("A U. Thor", ident.getName());
+ assertEquals("a_u_thor at example.com", ident.getEmailAddress());
+ assertEquals(1218123387000L, ident.getWhen().getTime());
+ assertEquals(TimeZone.getTimeZone("GMT+0700"), ident.getTimeZone());
+ assertEquals(7 * 60, ident.getTimeZoneOffset());
+ }
+
+ @Test
+ public void trimName() throws Exception {
+ String name = "A U. Thor";
+ String email = "a_u_thor at example.com";
+ String rest = "<a_u_thor at example.com> 1218123387 +0700";
+
+ checkNameEmail(name, email, name + rest);
+ checkNameEmail(name, email, " " + name + rest);
+ checkNameEmail(name, email, " " + name + rest);
+ checkNameEmail(name, email, name + " " + rest);
+ checkNameEmail(name, email, name + " " + rest);
+ checkNameEmail(name, email, " " + name + " " + rest);
+ }
+
+ @Test
+ public void noEmail() throws Exception {
+ String name = "A U. Thor";
+ String rest = " 1218123387 +0700";
+
+ checkNameEmail(name, null, name + rest);
+ checkNameEmail(name, null, " " + name + rest);
+ checkNameEmail(name, null, " " + name + rest);
+ checkNameEmail(name, null, name + " " + rest);
+ checkNameEmail(name, null, name + " " + rest);
+ checkNameEmail(name, null, " " + name + " " + rest);
+ }
+
+ @Test
+ public void exoticUserId() throws Exception {
+ String rest = " 218123387 +0700";
+ assertEquals("", parse(rest).getUserId());
+
+ String id = "foo\n\0bar\uabcd\n ";
+ assertEquals(id, parse(id + rest).getUserId());
+ }
+
+ @Test
+ public void fuzzyCasesMatchPersonIdent() throws Exception {
+ // See RawParseUtils_ParsePersonIdentTest#testParsePersonIdent_fuzzyCases()
+ Date when = new Date(1234567890000l);
+ TimeZone tz = TimeZone.getTimeZone("GMT-7");
+
+ assertMatchesPersonIdent(
+ "A U Thor <author at example.com>, C O. Miter <comiter at example.com> 1234567890 -0700",
+ new PersonIdent("A U Thor", "author at example.com", when, tz),
+ "A U Thor <author at example.com>, C O. Miter <comiter at example.com>");
+ assertMatchesPersonIdent(
+ "A U Thor <author at example.com> and others 1234567890 -0700",
+ new PersonIdent("A U Thor", "author at example.com", when, tz),
+ "A U Thor <author at example.com> and others");
+ }
+
+ @Test
+ public void incompleteCasesMatchPersonIdent() throws Exception {
+ // See RawParseUtils_ParsePersonIdentTest#testParsePersonIdent_incompleteCases()
+ Date when = new Date(1234567890000l);
+ TimeZone tz = TimeZone.getTimeZone("GMT-7");
+
+ assertMatchesPersonIdent(
+ "Me <> 1234567890 -0700",
+ new PersonIdent("Me", "", when, tz),
+ "Me <>");
+ assertMatchesPersonIdent(
+ " <me at example.com> 1234567890 -0700",
+ new PersonIdent("", "me at example.com", when, tz),
+ " <me at example.com>");
+ assertMatchesPersonIdent(
+ " <> 1234567890 -0700",
+ new PersonIdent("", "", when, tz),
+ " <>");
+ assertMatchesPersonIdent(
+ "<>",
+ new PersonIdent("", "", 0, 0),
+ "<>");
+ assertMatchesPersonIdent(
+ " <>",
+ new PersonIdent("", "", 0, 0),
+ " <>");
+ assertMatchesPersonIdent(
+ "<me at example.com>",
+ new PersonIdent("", "me at example.com", 0, 0),
+ "<me at example.com>");
+ assertMatchesPersonIdent(
+ " <me at example.com>",
+ new PersonIdent("", "me at example.com", 0, 0),
+ " <me at example.com>");
+ assertMatchesPersonIdent(
+ "Me <>",
+ new PersonIdent("Me", "", 0, 0),
+ "Me <>");
+ assertMatchesPersonIdent(
+ "Me <me at example.com>",
+ new PersonIdent("Me", "me at example.com", 0, 0),
+ "Me <me at example.com>");
+ assertMatchesPersonIdent(
+ "Me <me at example.com> 1234567890",
+ new PersonIdent("Me", "me at example.com", 0, 0),
+ "Me <me at example.com>");
+ assertMatchesPersonIdent(
+ "Me <me at example.com> 1234567890 ",
+ new PersonIdent("Me", "me at example.com", 0, 0),
+ "Me <me at example.com>");
+ }
+
+ private static void assertMatchesPersonIdent(String raw,
+ PersonIdent expectedPersonIdent, String expectedUserId) {
+ PushCertificateIdent certIdent = PushCertificateIdent.parse(raw);
+ assertNotNull(raw);
+ assertEquals(raw, certIdent.getRaw());
+ assertEquals(expectedPersonIdent.getName(), certIdent.getName());
+ assertEquals(expectedPersonIdent.getEmailAddress(),
+ certIdent.getEmailAddress());
+ assertEquals(expectedPersonIdent.getWhen(), certIdent.getWhen());
+ assertEquals(expectedPersonIdent.getTimeZoneOffset(),
+ certIdent.getTimeZoneOffset());
+ assertEquals(expectedUserId, certIdent.getUserId());
+ }
+
+ private static void checkNameEmail(String expectedName, String expectedEmail,
+ String raw) {
+ PushCertificateIdent ident = parse(raw);
+ assertNotNull(ident);
+ assertEquals(raw, ident.getRaw());
+ assertEquals(expectedName, ident.getName());
+ assertEquals(expectedEmail, ident.getEmailAddress());
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java
new file mode 100644
index 0000000..0647167
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateParserTest.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+
+import org.eclipse.jgit.errors.PackProtocolException;
+import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.PushCertificate.NonceStatus;
+import org.junit.Before;
+import org.junit.Test;
+
+/** Test for push certificate parsing. */
+public class PushCertificateParserTest {
+ // Example push certificate generated by C git 2.2.0.
+ private static final String INPUT = "001ccertificate version 0.1\n"
+ + "0041pusher Dave Borowitz <dborowitz at google.com> 1433954361 -0700\n"
+ + "0024pushee git://localhost/repo.git\n"
+ + "002anonce 1433954361-bde756572d665bba81d8\n"
+ + "0005\n"
+ + "00680000000000000000000000000000000000000000"
+ + " 6c2b981a177396fb47345b7df3e4d3f854c6bea7"
+ + " refs/heads/master\n"
+ + "0022-----BEGIN PGP SIGNATURE-----\n"
+ + "0016Version: GnuPG v1\n"
+ + "0005\n"
+ + "0045iQEcBAABAgAGBQJVeGg5AAoJEPfTicJkUdPkUggH/RKAeI9/i/LduuiqrL/SSdIa\n"
+ + "00459tYaSqJKLbXz63M/AW4Sp+4u+dVCQvnAt/a35CVEnpZz6hN4Kn/tiswOWVJf4CO7\n"
+ + "0045htNubGs5ZMwvD6sLYqKAnrM3WxV/2TbbjzjZW6Jkidz3jz/WRT4SmjGYiEO7aA+V\n"
+ + "00454ZdIS9f7sW5VsHHYlNThCA7vH8Uu48bUovFXyQlPTX0pToSgrWV3JnTxDNxfn3iG\n"
+ + "0045IL0zTY/qwVCdXgFownLcs6J050xrrBWIKqfcWr3u4D2aCLyR0v+S/KArr7ulZygY\n"
+ + "0045+SOklImn8TAZiNxhWtA6ens66IiammUkZYFv7SSzoPLFZT4dC84SmGPWgf94NoQ=\n"
+ + "000a=XFeC\n"
+ + "0020-----END PGP SIGNATURE-----\n"
+ + "0012push-cert-end\n";
+
+ // Same push certificate, with all trailing newlines stripped.
+ // (Note that the canonical signed payload is the same, so the same signature
+ // is still valid.)
+ private static final String INPUT_NO_NEWLINES = "001bcertificate version 0.1"
+ + "0040pusher Dave Borowitz <dborowitz at google.com> 1433954361 -0700"
+ + "0023pushee git://localhost/repo.git"
+ + "0029nonce 1433954361-bde756572d665bba81d8"
+ + "0004"
+ + "00670000000000000000000000000000000000000000"
+ + " 6c2b981a177396fb47345b7df3e4d3f854c6bea7"
+ + " refs/heads/master"
+ + "0021-----BEGIN PGP SIGNATURE-----"
+ + "0015Version: GnuPG v1"
+ + "0004"
+ + "0044iQEcBAABAgAGBQJVeGg5AAoJEPfTicJkUdPkUggH/RKAeI9/i/LduuiqrL/SSdIa"
+ + "00449tYaSqJKLbXz63M/AW4Sp+4u+dVCQvnAt/a35CVEnpZz6hN4Kn/tiswOWVJf4CO7"
+ + "0044htNubGs5ZMwvD6sLYqKAnrM3WxV/2TbbjzjZW6Jkidz3jz/WRT4SmjGYiEO7aA+V"
+ + "00444ZdIS9f7sW5VsHHYlNThCA7vH8Uu48bUovFXyQlPTX0pToSgrWV3JnTxDNxfn3iG"
+ + "0044IL0zTY/qwVCdXgFownLcs6J050xrrBWIKqfcWr3u4D2aCLyR0v+S/KArr7ulZygY"
+ + "0044+SOklImn8TAZiNxhWtA6ens66IiammUkZYFv7SSzoPLFZT4dC84SmGPWgf94NoQ="
+ + "0009=XFeC"
+ + "001f-----END PGP SIGNATURE-----"
+ + "0011push-cert-end";
+
+ private Repository db;
+
+ @Before
+ public void setUp() {
+ db = new InMemoryRepository(new DfsRepositoryDescription("repo"));
+ }
+
+ private static SignedPushConfig newEnabledConfig() {
+ Config cfg = new Config();
+ cfg.setString("receive", null, "certnonceseed", "sekret");
+ return SignedPushConfig.KEY.parse(cfg);
+ }
+
+ private static SignedPushConfig newDisabledConfig() {
+ return SignedPushConfig.KEY.parse(new Config());
+ }
+
+ @Test
+ public void noCert() throws Exception {
+ PushCertificateParser parser =
+ new PushCertificateParser(db, newEnabledConfig());
+ assertTrue(parser.enabled());
+ assertNull(parser.build());
+
+ ObjectId oldId = ObjectId.zeroId();
+ ObjectId newId =
+ ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
+ String line = oldId.name() + " " + newId.name() + " refs/heads/master";
+ ReceiveCommand cmd = BaseReceivePack.parseCommand(line);
+
+ parser.addCommand(cmd);
+ parser.addCommand(line);
+ assertNull(parser.build());
+ }
+
+ @Test
+ public void disabled() throws Exception {
+ PacketLineIn pckIn = newPacketLineIn(INPUT);
+ PushCertificateParser parser =
+ new PushCertificateParser(db, newDisabledConfig());
+ assertFalse(parser.enabled());
+ assertNull(parser.build());
+
+ parser.receiveHeader(pckIn, false);
+ parser.addCommand(pckIn.readString());
+ assertEquals(PushCertificateParser.BEGIN_SIGNATURE, pckIn.readString());
+ parser.receiveSignature(pckIn);
+ assertNull(parser.build());
+ }
+
+ @Test
+ public void disabledParserStillRequiresCorrectSyntax() throws Exception {
+ PacketLineIn pckIn = newPacketLineIn("001ccertificate version XYZ\n");
+ PushCertificateParser parser =
+ new PushCertificateParser(db, newDisabledConfig());
+ assertFalse(parser.enabled());
+ try {
+ parser.receiveHeader(pckIn, false);
+ fail("Expected PackProtocolException");
+ } catch (PackProtocolException e) {
+ assertEquals(
+ "Push certificate has missing or invalid value for certificate"
+ + " version: XYZ",
+ e.getMessage());
+ }
+ assertNull(parser.build());
+ }
+
+ @Test
+ public void parseCertFromPktLine() throws Exception {
+ PacketLineIn pckIn = newPacketLineIn(INPUT);
+ PushCertificateParser parser =
+ new PushCertificateParser(db, newEnabledConfig());
+ parser.receiveHeader(pckIn, false);
+ parser.addCommand(pckIn.readString());
+ assertEquals(PushCertificateParser.BEGIN_SIGNATURE, pckIn.readString());
+ parser.receiveSignature(pckIn);
+
+ PushCertificate cert = parser.build();
+ assertEquals("0.1", cert.getVersion());
+ assertEquals("Dave Borowitz", cert.getPusherIdent().getName());
+ assertEquals("dborowitz at google.com",
+ cert.getPusherIdent().getEmailAddress());
+ assertEquals(1433954361000L, cert.getPusherIdent().getWhen().getTime());
+ assertEquals(-7 * 60, cert.getPusherIdent().getTimeZoneOffset());
+ assertEquals("git://localhost/repo.git", cert.getPushee());
+ assertEquals("1433954361-bde756572d665bba81d8", cert.getNonce());
+
+ assertNotEquals(cert.getNonce(), parser.getAdvertiseNonce());
+ assertEquals(PushCertificate.NonceStatus.BAD, cert.getNonceStatus());
+
+ assertEquals(1, cert.getCommands().size());
+ ReceiveCommand cmd = cert.getCommands().get(0);
+ assertEquals("refs/heads/master", cmd.getRefName());
+ assertEquals(ObjectId.zeroId(), cmd.getOldId());
+ assertEquals("6c2b981a177396fb47345b7df3e4d3f854c6bea7",
+ cmd.getNewId().name());
+
+ assertEquals(concatPacketLines(INPUT, 0, 6), cert.toText());
+ assertEquals(concatPacketLines(INPUT, 0, 17), cert.toTextWithSignature());
+
+ String signature = concatPacketLines(INPUT, 6, 17);
+ assertTrue(signature.startsWith(PushCertificateParser.BEGIN_SIGNATURE));
+ assertTrue(signature.endsWith(PushCertificateParser.END_SIGNATURE + "\n"));
+ assertEquals(signature, cert.getSignature());
+ }
+
+ @Test
+ public void parseCertFromPktLineNoNewlines() throws Exception {
+ PacketLineIn pckIn = newPacketLineIn(INPUT_NO_NEWLINES);
+ PushCertificateParser parser =
+ new PushCertificateParser(db, newEnabledConfig());
+ parser.receiveHeader(pckIn, false);
+ parser.addCommand(pckIn.readString());
+ assertEquals(PushCertificateParser.BEGIN_SIGNATURE, pckIn.readString());
+ parser.receiveSignature(pckIn);
+
+ PushCertificate cert = parser.build();
+ assertEquals("0.1", cert.getVersion());
+ assertEquals("Dave Borowitz", cert.getPusherIdent().getName());
+ assertEquals("dborowitz at google.com",
+ cert.getPusherIdent().getEmailAddress());
+ assertEquals(1433954361000L, cert.getPusherIdent().getWhen().getTime());
+ assertEquals(-7 * 60, cert.getPusherIdent().getTimeZoneOffset());
+ assertEquals("git://localhost/repo.git", cert.getPushee());
+ assertEquals("1433954361-bde756572d665bba81d8", cert.getNonce());
+
+ assertNotEquals(cert.getNonce(), parser.getAdvertiseNonce());
+ assertEquals(PushCertificate.NonceStatus.BAD, cert.getNonceStatus());
+
+ assertEquals(1, cert.getCommands().size());
+ ReceiveCommand cmd = cert.getCommands().get(0);
+ assertEquals("refs/heads/master", cmd.getRefName());
+ assertEquals(ObjectId.zeroId(), cmd.getOldId());
+ assertEquals("6c2b981a177396fb47345b7df3e4d3f854c6bea7",
+ cmd.getNewId().name());
+
+ // Canonical signed payload has reinserted newlines.
+ assertEquals(concatPacketLines(INPUT, 0, 6), cert.toText());
+
+ String signature = concatPacketLines(INPUT, 6, 17);
+ assertTrue(signature.startsWith(PushCertificateParser.BEGIN_SIGNATURE));
+ assertTrue(signature.endsWith(PushCertificateParser.END_SIGNATURE + "\n"));
+ assertEquals(signature, cert.getSignature());
+ }
+
+ @Test
+ public void testConcatPacketLines() throws Exception {
+ String input = "000bline 1\n000bline 2\n000bline 3\n";
+ assertEquals("line 1\n", concatPacketLines(input, 0, 1));
+ assertEquals("line 1\nline 2\n", concatPacketLines(input, 0, 2));
+ assertEquals("line 2\nline 3\n", concatPacketLines(input, 1, 3));
+ assertEquals("line 2\nline 3\n", concatPacketLines(input, 1, 4));
+ }
+
+ @Test
+ public void testConcatPacketLinesInsertsNewlines() throws Exception {
+ String input = "000bline 1\n000aline 2000bline 3\n";
+ assertEquals("line 1\n", concatPacketLines(input, 0, 1));
+ assertEquals("line 1\nline 2\n", concatPacketLines(input, 0, 2));
+ assertEquals("line 2\nline 3\n", concatPacketLines(input, 1, 3));
+ assertEquals("line 2\nline 3\n", concatPacketLines(input, 1, 4));
+ }
+
+ @Test
+ public void testParseReader() throws Exception {
+ Reader reader = new StringReader(concatPacketLines(INPUT, 0, 18));
+ PushCertificate streamCert = PushCertificateParser.fromReader(reader);
+
+ PacketLineIn pckIn = newPacketLineIn(INPUT);
+ PushCertificateParser pckParser =
+ new PushCertificateParser(db, newEnabledConfig());
+ pckParser.receiveHeader(pckIn, false);
+ pckParser.addCommand(pckIn.readString());
+ assertEquals(PushCertificateParser.BEGIN_SIGNATURE, pckIn.readString());
+ pckParser.receiveSignature(pckIn);
+ PushCertificate pckCert = pckParser.build();
+
+ // Nonce status is unsolicited since this was not parsed in the context of
+ // the wire protocol; as a result, certs are not actually equal.
+ assertEquals(NonceStatus.UNSOLICITED, streamCert.getNonceStatus());
+
+ assertEquals(pckCert.getVersion(), streamCert.getVersion());
+ assertEquals(pckCert.getPusherIdent().getName(),
+ streamCert.getPusherIdent().getName());
+ assertEquals(pckCert.getPusherIdent().getEmailAddress(),
+ streamCert.getPusherIdent().getEmailAddress());
+ assertEquals(pckCert.getPusherIdent().getWhen().getTime(),
+ streamCert.getPusherIdent().getWhen().getTime());
+ assertEquals(pckCert.getPusherIdent().getTimeZoneOffset(),
+ streamCert.getPusherIdent().getTimeZoneOffset());
+ assertEquals(pckCert.getPushee(), streamCert.getPushee());
+ assertEquals(pckCert.getNonce(), streamCert.getNonce());
+ assertEquals(pckCert.getSignature(), streamCert.getSignature());
+ assertEquals(pckCert.toText(), streamCert.toText());
+
+ assertEquals(pckCert.getCommands().size(), streamCert.getCommands().size());
+ ReceiveCommand pckCmd = pckCert.getCommands().get(0);
+ ReceiveCommand streamCmd = streamCert.getCommands().get(0);
+ assertEquals(pckCmd.getRefName(), streamCmd.getRefName());
+ assertEquals(pckCmd.getOldId(), streamCmd.getOldId());
+ assertEquals(pckCmd.getNewId().name(), streamCmd.getNewId().name());
+ }
+
+ @Test
+ public void testParseString() throws Exception {
+ String str = concatPacketLines(INPUT, 0, 18);
+ assertEquals(
+ PushCertificateParser.fromReader(new StringReader(str)),
+ PushCertificateParser.fromString(str));
+ }
+
+ @Test
+ public void testParseMultipleFromStream() throws Exception {
+ String input = concatPacketLines(INPUT, 0, 17);
+ assertFalse(input.contains(PushCertificateParser.END_CERT));
+ input += input;
+ Reader reader = new InputStreamReader(
+ new ByteArrayInputStream(Constants.encode(input)));
+
+ assertNotNull(PushCertificateParser.fromReader(reader));
+ assertNotNull(PushCertificateParser.fromReader(reader));
+ assertEquals(-1, reader.read());
+ assertNull(PushCertificateParser.fromReader(reader));
+ }
+
+ @Test
+ public void testMissingPusheeField() throws Exception {
+ // Omit pushee line from existing cert. (This means the signature would not
+ // match, but we're not verifying it here.)
+ String input = INPUT.replace("0024pushee git://localhost/repo.git\n", "");
+ assertFalse(input.contains(PushCertificateParser.PUSHEE));
+
+ PacketLineIn pckIn = newPacketLineIn(input);
+ PushCertificateParser parser =
+ new PushCertificateParser(db, newEnabledConfig());
+ parser.receiveHeader(pckIn, false);
+ parser.addCommand(pckIn.readString());
+ assertEquals(PushCertificateParser.BEGIN_SIGNATURE, pckIn.readString());
+ parser.receiveSignature(pckIn);
+
+ PushCertificate cert = parser.build();
+ assertEquals("0.1", cert.getVersion());
+ assertNull(cert.getPushee());
+ assertFalse(cert.toText().contains(PushCertificateParser.PUSHEE));
+ }
+
+ private static String concatPacketLines(String input, int begin, int end)
+ throws IOException {
+ StringBuilder result = new StringBuilder();
+ int i = 0;
+ PacketLineIn pckIn = newPacketLineIn(input);
+ while (i < end) {
+ String line;
+ try {
+ line = pckIn.readString();
+ } catch (EOFException e) {
+ break;
+ }
+ if (++i > begin) {
+ result.append(line).append('\n');
+ }
+ }
+ return result.toString();
+ }
+
+ private static PacketLineIn newPacketLineIn(String input) {
+ return new PacketLineIn(new ByteArrayInputStream(Constants.encode(input)));
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java
new file mode 100644
index 0000000..68e0129
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushCertificateStoreTest.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import static org.eclipse.jgit.lib.ObjectId.zeroId;
+import static org.eclipse.jgit.lib.RefUpdate.Result.FAST_FORWARD;
+import static org.eclipse.jgit.lib.RefUpdate.Result.LOCK_FAILURE;
+import static org.eclipse.jgit.lib.RefUpdate.Result.NEW;
+import static org.eclipse.jgit.lib.RefUpdate.Result.NO_CHANGE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PushCertificateStoreTest {
+ private static final ObjectId ID1 =
+ ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
+
+ private static final ObjectId ID2 =
+ ObjectId.fromString("badc0ffebadc0ffebadc0ffebadc0ffebadc0ffe");
+
+ private static PushCertificate newCert(String... updateLines) {
+ StringBuilder cert = new StringBuilder(
+ "certificate version 0.1\n"
+ + "pusher Dave Borowitz <dborowitz at google.com> 1433954361 -0700\n"
+ + "pushee git://localhost/repo.git\n"
+ + "nonce 1433954361-bde756572d665bba81d8\n"
+ + "\n");
+ for (String updateLine : updateLines) {
+ cert.append(updateLine).append('\n');
+ }
+ cert.append(
+ "-----BEGIN PGP SIGNATURE-----\n"
+ + "DUMMY/SIGNATURE\n"
+ + "-----END PGP SIGNATURE-----\n");
+ try {
+ return PushCertificateParser.fromReader(new InputStreamReader(
+ new ByteArrayInputStream(Constants.encode(cert.toString()))));
+ } catch (IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ private static String command(ObjectId oldId, ObjectId newId, String ref) {
+ return oldId.name() + " " + newId.name() + " " + ref;
+ }
+
+ private AtomicInteger ts = new AtomicInteger(1433954361);
+ private InMemoryRepository repo;
+ private PushCertificateStore store;
+
+ @Before
+ public void setUp() throws Exception {
+ repo = new InMemoryRepository(new DfsRepositoryDescription("repo"));
+ store = newStore();
+ }
+
+ @Test
+ public void missingRef() throws Exception {
+ assertCerts("refs/heads/master");
+ }
+
+ @Test
+ public void saveNoChange() throws Exception {
+ assertEquals(NO_CHANGE, store.save());
+ }
+
+ @Test
+ public void saveOneCertOnOneRef() throws Exception {
+ PersonIdent ident = newIdent();
+ PushCertificate addMaster = newCert(
+ command(zeroId(), ID1, "refs/heads/master"));
+ store.put(addMaster, ident);
+ assertEquals(NEW, store.save());
+ assertCerts("refs/heads/master", addMaster);
+ assertCerts("refs/heads/branch");
+
+ try (RevWalk rw = new RevWalk(repo)) {
+ RevCommit c = rw.parseCommit(repo.resolve(PushCertificateStore.REF_NAME));
+ rw.parseBody(c);
+ assertEquals("Store push certificate for refs/heads/master\n",
+ c.getFullMessage());
+ assertEquals(ident, c.getAuthorIdent());
+ assertEquals(ident, c.getCommitterIdent());
+ }
+ }
+
+ @Test
+ public void saveTwoCertsOnSameRefInTwoUpdates() throws Exception {
+ PushCertificate addMaster = newCert(
+ command(zeroId(), ID1, "refs/heads/master"));
+ store.put(addMaster, newIdent());
+ assertEquals(NEW, store.save());
+ PushCertificate updateMaster = newCert(
+ command(ID1, ID2, "refs/heads/master"));
+ store.put(updateMaster, newIdent());
+ assertEquals(FAST_FORWARD, store.save());
+ assertCerts("refs/heads/master", updateMaster, addMaster);
+ }
+
+ @Test
+ public void saveTwoCertsOnSameRefInOneUpdate() throws Exception {
+ PersonIdent ident1 = newIdent();
+ PersonIdent ident2 = newIdent();
+ PushCertificate updateMaster = newCert(
+ command(ID1, ID2, "refs/heads/master"));
+ store.put(updateMaster, ident2);
+ PushCertificate addMaster = newCert(
+ command(zeroId(), ID1, "refs/heads/master"));
+ store.put(addMaster, ident1);
+ assertEquals(NEW, store.save());
+ assertCerts("refs/heads/master", updateMaster, addMaster);
+ }
+
+ @Test
+ public void saveTwoCertsOnDifferentRefsInOneUpdate() throws Exception {
+ PersonIdent ident1 = newIdent();
+ PersonIdent ident3 = newIdent();
+ PushCertificate addBranch = newCert(
+ command(zeroId(), ID1, "refs/heads/branch"));
+ store.put(addBranch, ident3);
+ PushCertificate addMaster = newCert(
+ command(zeroId(), ID1, "refs/heads/master"));
+ store.put(addMaster, ident1);
+ assertEquals(NEW, store.save());
+ assertCerts("refs/heads/master", addMaster);
+ assertCerts("refs/heads/branch", addBranch);
+ }
+
+ @Test
+ public void saveTwoCertsOnDifferentRefsInTwoUpdates() throws Exception {
+ PushCertificate addMaster = newCert(
+ command(zeroId(), ID1, "refs/heads/master"));
+ store.put(addMaster, newIdent());
+ assertEquals(NEW, store.save());
+ PushCertificate addBranch = newCert(
+ command(zeroId(), ID1, "refs/heads/branch"));
+ store.put(addBranch, newIdent());
+ assertEquals(FAST_FORWARD, store.save());
+ assertCerts("refs/heads/master", addMaster);
+ assertCerts("refs/heads/branch", addBranch);
+ }
+
+ @Test
+ public void saveOneCertOnMultipleRefs() throws Exception {
+ PersonIdent ident = newIdent();
+ PushCertificate addMasterAndBranch = newCert(
+ command(zeroId(), ID1, "refs/heads/branch"),
+ command(zeroId(), ID2, "refs/heads/master"));
+ store.put(addMasterAndBranch, ident);
+ assertEquals(NEW, store.save());
+ assertCerts("refs/heads/master", addMasterAndBranch);
+ assertCerts("refs/heads/branch", addMasterAndBranch);
+
+ try (RevWalk rw = new RevWalk(repo)) {
+ RevCommit c = rw.parseCommit(repo.resolve(PushCertificateStore.REF_NAME));
+ rw.parseBody(c);
+ assertEquals("Store push certificate for 2 refs\n", c.getFullMessage());
+ assertEquals(ident, c.getAuthorIdent());
+ assertEquals(ident, c.getCommitterIdent());
+ }
+ }
+
+ @Test
+ public void changeRefFileToDirectory() throws Exception {
+ PushCertificate deleteRefsHeads = newCert(
+ command(ID1, zeroId(), "refs/heads"));
+ store.put(deleteRefsHeads, newIdent());
+ PushCertificate addMaster = newCert(
+ command(zeroId(), ID1, "refs/heads/master"));
+ store.put(addMaster, newIdent());
+ assertEquals(NEW, store.save());
+ assertCerts("refs/heads", deleteRefsHeads);
+ assertCerts("refs/heads/master", addMaster);
+ }
+
+ @Test
+ public void getBeforeSaveDoesNotIncludePending() throws Exception {
+ PushCertificate addMaster = newCert(
+ command(zeroId(), ID1, "refs/heads/master"));
+ store.put(addMaster, newIdent());
+ assertEquals(NEW, store.save());
+
+ PushCertificate updateMaster = newCert(
+ command(ID1, ID2, "refs/heads/master"));
+ store.put(updateMaster, newIdent());
+
+ assertCerts("refs/heads/master", addMaster);
+ assertEquals(FAST_FORWARD, store.save());
+ assertCerts("refs/heads/master", updateMaster, addMaster);
+ }
+
+ @Test
+ public void lockFailure() throws Exception {
+ PushCertificateStore store1 = store;
+ PushCertificateStore store2 = newStore();
+ store2.get("refs/heads/master");
+
+ PushCertificate addMaster = newCert(
+ command(zeroId(), ID1, "refs/heads/master"));
+ store1.put(addMaster, newIdent());
+ assertEquals(NEW, store1.save());
+
+ PushCertificate addBranch = newCert(
+ command(zeroId(), ID2, "refs/heads/branch"));
+ store2.put(addBranch, newIdent());
+
+ assertEquals(LOCK_FAILURE, store2.save());
+ // Reread ref after lock failure.
+ assertCerts(store2, "refs/heads/master", addMaster);
+ assertCerts(store2, "refs/heads/branch");
+
+ assertEquals(FAST_FORWARD, store2.save());
+ assertCerts(store2, "refs/heads/master", addMaster);
+ assertCerts(store2, "refs/heads/branch", addBranch);
+ }
+
+ @Test
+ public void saveInBatch() throws Exception {
+ BatchRefUpdate batch = repo.getRefDatabase().newBatchUpdate();
+ assertFalse(store.save(batch));
+ assertEquals(0, batch.getCommands().size());
+ PushCertificate addMaster = newCert(
+ command(zeroId(), ID1, "refs/heads/master"));
+ store.put(addMaster, newIdent());
+ assertTrue(store.save(batch));
+
+ List<ReceiveCommand> commands = batch.getCommands();
+ assertEquals(1, commands.size());
+ ReceiveCommand cmd = commands.get(0);
+ assertEquals("refs/meta/push-certs", cmd.getRefName());
+ assertEquals(ReceiveCommand.Result.NOT_ATTEMPTED, cmd.getResult());
+
+ try (RevWalk rw = new RevWalk(repo)) {
+ batch.execute(rw, NullProgressMonitor.INSTANCE);
+ assertEquals(ReceiveCommand.Result.OK, cmd.getResult());
+ }
+ }
+
+ @Test
+ public void putMatchingWithNoMatchingRefs() throws Exception {
+ PushCertificate addMaster = newCert(
+ command(zeroId(), ID1, "refs/heads/master"),
+ command(zeroId(), ID2, "refs/heads/branch"));
+ store.put(addMaster, newIdent(), Collections.<ReceiveCommand> emptyList());
+ assertEquals(NO_CHANGE, store.save());
+ }
+
+ @Test
+ public void putMatchingWithNoMatchingRefsInBatchOnEmptyRef()
+ throws Exception {
+ PushCertificate addMaster = newCert(
+ command(zeroId(), ID1, "refs/heads/master"),
+ command(zeroId(), ID2, "refs/heads/branch"));
+ store.put(addMaster, newIdent(), Collections.<ReceiveCommand> emptyList());
+ BatchRefUpdate batch = repo.getRefDatabase().newBatchUpdate();
+ assertFalse(store.save(batch));
+ assertEquals(0, batch.getCommands().size());
+ }
+
+ @Test
+ public void putMatchingWithNoMatchingRefsInBatchOnNonEmptyRef()
+ throws Exception {
+ PushCertificate addMaster = newCert(
+ command(zeroId(), ID1, "refs/heads/master"));
+ store.put(addMaster, newIdent());
+ assertEquals(NEW, store.save());
+
+ PushCertificate addBranch = newCert(
+ command(zeroId(), ID2, "refs/heads/branch"));
+ store.put(addBranch, newIdent(), Collections.<ReceiveCommand> emptyList());
+ BatchRefUpdate batch = repo.getRefDatabase().newBatchUpdate();
+ assertFalse(store.save(batch));
+ assertEquals(0, batch.getCommands().size());
+ }
+
+ @Test
+ public void putMatchingWithSomeMatchingRefs() throws Exception {
+ PushCertificate addMasterAndBranch = newCert(
+ command(zeroId(), ID1, "refs/heads/master"),
+ command(zeroId(), ID2, "refs/heads/branch"));
+ store.put(addMasterAndBranch, newIdent(),
+ Collections.singleton(addMasterAndBranch.getCommands().get(0)));
+ assertEquals(NEW, store.save());
+ assertCerts("refs/heads/master", addMasterAndBranch);
+ assertCerts("refs/heads/branch");
+ }
+
+ private PersonIdent newIdent() {
+ return new PersonIdent(
+ "A U. Thor", "author at example.com", ts.getAndIncrement(), 0);
+ }
+
+ private PushCertificateStore newStore() {
+ return new PushCertificateStore(repo);
+ }
+
+ private void assertCerts(String refName, PushCertificate... expected)
+ throws Exception {
+ assertCerts(store, refName, expected);
+ assertCerts(newStore(), refName, expected);
+ }
+
+ private static void assertCerts(PushCertificateStore store, String refName,
+ PushCertificate... expected) throws Exception {
+ List<PushCertificate> ex = Arrays.asList(expected);
+ PushCertificate first = !ex.isEmpty() ? ex.get(0) : null;
+ assertEquals(first, store.get(refName));
+ assertEquals(ex, toList(store.getAll(refName)));
+ }
+
+ private static <T> List<T> toList(Iterable<T> it) {
+ List<T> list = new ArrayList<>();
+ for (T t : it) {
+ list.add(t);
+ }
+ return list;
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushConnectionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushConnectionTest.java
new file mode 100644
index 0000000..a3b4134
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushConnectionTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import static org.eclipse.jgit.transport.RemoteRefUpdate.Status.REJECTED_OTHER_REASON;
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
+import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
+import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PushConnectionTest {
+ private URIish uri;
+ private TestProtocol<Object> testProtocol;
+ private Object ctx = new Object();
+ private InMemoryRepository server;
+ private InMemoryRepository client;
+ private ObjectId obj1;
+ private ObjectId obj2;
+ private ObjectId obj3;
+ private String refName = "refs/tags/blob";
+
+ @Before
+ public void setUp() throws Exception {
+ server = newRepo("server");
+ client = newRepo("client");
+ testProtocol = new TestProtocol<>(
+ null,
+ new ReceivePackFactory<Object>() {
+ @Override
+ public ReceivePack create(Object req, Repository db)
+ throws ServiceNotEnabledException,
+ ServiceNotAuthorizedException {
+ return new ReceivePack(db);
+ }
+ });
+ uri = testProtocol.register(ctx, server);
+
+ try (ObjectInserter ins = server.newObjectInserter()) {
+ obj1 = ins.insert(Constants.OBJ_BLOB, Constants.encode("test"));
+ obj3 = ins.insert(Constants.OBJ_BLOB, Constants.encode("not"));
+ ins.flush();
+
+ RefUpdate u = server.updateRef(refName);
+ u.setNewObjectId(obj1);
+ assertEquals(RefUpdate.Result.NEW, u.update());
+ }
+
+ try (ObjectInserter ins = client.newObjectInserter()) {
+ obj2 = ins.insert(Constants.OBJ_BLOB, Constants.encode("file"));
+ ins.flush();
+ }
+ }
+
+ @After
+ public void tearDown() {
+ Transport.unregister(testProtocol);
+ }
+
+ private static InMemoryRepository newRepo(String name) {
+ return new InMemoryRepository(new DfsRepositoryDescription(name));
+ }
+
+ @Test
+ public void testWrongOldIdDoesNotReplace() throws IOException {
+ RemoteRefUpdate rru = new RemoteRefUpdate(null, null, obj2, refName,
+ false, null, obj3);
+
+ Map<String, RemoteRefUpdate> updates = new HashMap<>();
+ updates.put(rru.getRemoteName(), rru);
+
+ Transport tn = testProtocol.open(uri, client, "server");
+ try {
+ PushConnection connection = tn.openPush();
+ try {
+ connection.push(NullProgressMonitor.INSTANCE, updates);
+ } finally {
+ connection.close();
+ }
+ } finally {
+ tn.close();
+ }
+
+ assertEquals(REJECTED_OTHER_REASON, rru.getStatus());
+ assertEquals("invalid old id sent", rru.getMessage());
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java
index 2bd9077..94bc383 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ReceivePackAdvertiseRefsHookTest.java
@@ -116,12 +116,9 @@ public class ReceivePackAdvertiseRefsHookTest extends LocalDiskRepositoryTestCas
// Clone from dst into src
//
- Transport t = Transport.open(src, uriOf(dst));
- try {
+ try (Transport t = Transport.open(src, uriOf(dst))) {
t.fetch(PM, Collections.singleton(new RefSpec("+refs/*:refs/*")));
assertEquals(B, src.resolve(R_MASTER));
- } finally {
- t.close();
}
// Now put private stuff into dst.
@@ -144,7 +141,8 @@ public class ReceivePackAdvertiseRefsHookTest extends LocalDiskRepositoryTestCas
@Test
public void testFilterHidesPrivate() throws Exception {
Map<String, Ref> refs;
- TransportLocal t = new TransportLocal(src, uriOf(dst), dst.getDirectory()) {
+ try (TransportLocal t = new TransportLocal(src, uriOf(dst),
+ dst.getDirectory()) {
@Override
ReceivePack createReceivePack(final Repository db) {
db.close();
@@ -154,16 +152,10 @@ public class ReceivePackAdvertiseRefsHookTest extends LocalDiskRepositoryTestCas
rp.setAdvertiseRefsHook(new HidePrivateHook());
return rp;
}
- };
- try {
- PushConnection c = t.openPush();
- try {
+ }) {
+ try (PushConnection c = t.openPush()) {
refs = c.getRefsMap();
- } finally {
- c.close();
}
- } finally {
- t.close();
}
assertNotNull(refs);
@@ -543,8 +535,9 @@ public class ReceivePackAdvertiseRefsHookTest extends LocalDiskRepositoryTestCas
@After
public void release() {
- if (inserter != null)
- inserter.release();
+ if (inserter != null) {
+ inserter.close();
+ }
}
private void openPack(TemporaryBuffer.Heap buf) throws IOException {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java
index 3f5fcbb..4f83350 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/RefSpecTest.java
@@ -341,6 +341,41 @@ public class RefSpecTest {
}
@Test
+ public void testWildcardAfterText1() {
+ RefSpec a = new RefSpec("refs/heads/*/for-linus:refs/remotes/mine/*-blah");
+ assertTrue(a.isWildcard());
+ assertTrue(a.matchDestination("refs/remotes/mine/x-blah"));
+ assertTrue(a.matchDestination("refs/remotes/mine/foo-blah"));
+ assertTrue(a.matchDestination("refs/remotes/mine/foo/x-blah"));
+ assertFalse(a.matchDestination("refs/remotes/origin/foo/x-blah"));
+
+ RefSpec b = a.expandFromSource("refs/heads/foo/for-linus");
+ assertEquals("refs/remotes/mine/foo-blah", b.getDestination());
+ RefSpec c = a.expandFromDestination("refs/remotes/mine/foo-blah");
+ assertEquals("refs/heads/foo/for-linus", c.getSource());
+ }
+
+ @Test
+ public void testWildcardAfterText2() {
+ RefSpec a = new RefSpec("refs/heads*/for-linus:refs/remotes/mine/*");
+ assertTrue(a.isWildcard());
+ assertTrue(a.matchSource("refs/headsx/for-linus"));
+ assertTrue(a.matchSource("refs/headsfoo/for-linus"));
+ assertTrue(a.matchSource("refs/headsx/foo/for-linus"));
+ assertFalse(a.matchSource("refs/headx/for-linus"));
+
+ RefSpec b = a.expandFromSource("refs/headsx/for-linus");
+ assertEquals("refs/remotes/mine/x", b.getDestination());
+ RefSpec c = a.expandFromDestination("refs/remotes/mine/x");
+ assertEquals("refs/headsx/for-linus", c.getSource());
+
+ RefSpec d = a.expandFromSource("refs/headsx/foo/for-linus");
+ assertEquals("refs/remotes/mine/x/foo", d.getDestination());
+ RefSpec e = a.expandFromDestination("refs/remotes/mine/x/foo");
+ assertEquals("refs/headsx/foo/for-linus", e.getSource());
+ }
+
+ @Test
public void testWildcardMirror() {
RefSpec a = new RefSpec("*:*");
assertTrue(a.isWildcard());
@@ -404,21 +439,6 @@ public class RefSpecTest {
}
@Test(expected = IllegalArgumentException.class)
- public void invalidWhenWildcardAfterText() {
- assertNotNull(new RefSpec("refs/heads/wrong*:refs/heads/right/*"));
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void invalidWhenWildcardBeforeText() {
- assertNotNull(new RefSpec("*wrong:right/*"));
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void invalidWhenWildcardBeforeTextAtEnd() {
- assertNotNull(new RefSpec("refs/heads/*wrong:right/*"));
- }
-
- @Test(expected = IllegalArgumentException.class)
public void invalidSourceDoubleSlashes() {
assertNotNull(new RefSpec("refs/heads//wrong"));
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SideBandOutputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SideBandOutputStreamTest.java
index e9ae190..d2c3a0b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SideBandOutputStreamTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SideBandOutputStreamTest.java
@@ -195,24 +195,31 @@ public class SideBandOutputStreamTest {
assertEquals(1, flushCnt[0]);
}
+ private void createSideBandOutputStream(int chan, int sz, OutputStream os)
+ throws Exception {
+ try (SideBandOutputStream s = new SideBandOutputStream(chan, sz, os)) {
+ // Unused
+ }
+ }
+
@Test
- public void testConstructor_RejectsBadChannel() {
+ public void testConstructor_RejectsBadChannel() throws Exception {
try {
- new SideBandOutputStream(-1, MAX_BUF, rawOut);
+ createSideBandOutputStream(-1, MAX_BUF, rawOut);
fail("Accepted -1 channel number");
} catch (IllegalArgumentException e) {
assertEquals("channel -1 must be in range [1, 255]", e.getMessage());
}
try {
- new SideBandOutputStream(0, MAX_BUF, rawOut);
+ createSideBandOutputStream(0, MAX_BUF, rawOut);
fail("Accepted 0 channel number");
} catch (IllegalArgumentException e) {
assertEquals("channel 0 must be in range [1, 255]", e.getMessage());
}
try {
- new SideBandOutputStream(256, MAX_BUF, rawOut);
+ createSideBandOutputStream(256, MAX_BUF, rawOut);
fail("Accepted 256 channel number");
} catch (IllegalArgumentException e) {
assertEquals("channel 256 must be in range [1, 255]", e
@@ -221,30 +228,30 @@ public class SideBandOutputStreamTest {
}
@Test
- public void testConstructor_RejectsBadBufferSize() {
+ public void testConstructor_RejectsBadBufferSize() throws Exception {
try {
- new SideBandOutputStream(CH_DATA, -1, rawOut);
+ createSideBandOutputStream(CH_DATA, -1, rawOut);
fail("Accepted -1 for buffer size");
} catch (IllegalArgumentException e) {
assertEquals("packet size -1 must be >= 5", e.getMessage());
}
try {
- new SideBandOutputStream(CH_DATA, 0, rawOut);
+ createSideBandOutputStream(CH_DATA, 0, rawOut);
fail("Accepted 0 for buffer size");
} catch (IllegalArgumentException e) {
assertEquals("packet size 0 must be >= 5", e.getMessage());
}
try {
- new SideBandOutputStream(CH_DATA, 1, rawOut);
+ createSideBandOutputStream(CH_DATA, 1, rawOut);
fail("Accepted 1 for buffer size");
} catch (IllegalArgumentException e) {
assertEquals("packet size 1 must be >= 5", e.getMessage());
}
try {
- new SideBandOutputStream(CH_DATA, Integer.MAX_VALUE, rawOut);
+ createSideBandOutputStream(CH_DATA, Integer.MAX_VALUE, rawOut);
fail("Accepted " + Integer.MAX_VALUE + " for buffer size");
} catch (IllegalArgumentException e) {
assertEquals(MessageFormat.format(
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java
new file mode 100644
index 0000000..31b6418
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.InvalidRemoteException;
+import org.eclipse.jgit.api.errors.TransportException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
+import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
+import org.eclipse.jgit.transport.resolver.UploadPackFactory;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestProtocolTest {
+ private static final RefSpec HEADS = new RefSpec("+refs/heads/*:refs/heads/*");
+
+ private static class User {
+ private final String name;
+
+ private User(String name) {
+ this.name = name;
+ }
+ }
+
+ private static class DefaultUpload implements UploadPackFactory<User> {
+ @Override
+ public UploadPack create(User req, Repository db) {
+ return new UploadPack(db);
+ }
+ }
+
+ private static class DefaultReceive implements ReceivePackFactory<User> {
+ @Override
+ public ReceivePack create(User req, Repository db) {
+ return new ReceivePack(db);
+ }
+ }
+
+ private List<TransportProtocol> protos;
+ private TestRepository<InMemoryRepository> local;
+ private TestRepository<InMemoryRepository> remote;
+
+ @Before
+ public void setUp() throws Exception {
+ protos = new ArrayList<TransportProtocol>();
+ local = new TestRepository<InMemoryRepository>(
+ new InMemoryRepository(new DfsRepositoryDescription("local")));
+ remote = new TestRepository<InMemoryRepository>(
+ new InMemoryRepository(new DfsRepositoryDescription("remote")));
+ }
+
+ @After
+ public void tearDown() {
+ for (TransportProtocol proto : protos) {
+ Transport.unregister(proto);
+ }
+ }
+
+ @Test
+ public void testFetch() throws Exception {
+ ObjectId master = remote.branch("master").commit().create();
+
+ TestProtocol<User> proto = registerDefault();
+ URIish uri = proto.register(new User("user"), remote.getRepository());
+
+ try (Git git = new Git(local.getRepository())) {
+ git.fetch()
+ .setRemote(uri.toString())
+ .setRefSpecs(HEADS)
+ .call();
+ assertEquals(master,
+ local.getRepository().exactRef("refs/heads/master").getObjectId());
+ }
+ }
+
+ @Test
+ public void testPush() throws Exception {
+ ObjectId master = local.branch("master").commit().create();
+
+ TestProtocol<User> proto = registerDefault();
+ URIish uri = proto.register(new User("user"), remote.getRepository());
+
+ try (Git git = new Git(local.getRepository())) {
+ git.push()
+ .setRemote(uri.toString())
+ .setRefSpecs(HEADS)
+ .call();
+ assertEquals(master,
+ remote.getRepository().exactRef("refs/heads/master").getObjectId());
+ }
+ }
+
+ @Test
+ public void testUploadPackFactory() throws Exception {
+ ObjectId master = remote.branch("master").commit().create();
+
+ final AtomicInteger rejected = new AtomicInteger();
+ TestProtocol<User> proto = registerProto(new UploadPackFactory<User>() {
+ @Override
+ public UploadPack create(User req, Repository db)
+ throws ServiceNotAuthorizedException {
+ if (!"user2".equals(req.name)) {
+ rejected.incrementAndGet();
+ throw new ServiceNotAuthorizedException();
+ }
+ return new UploadPack(db);
+ }
+ }, new DefaultReceive());
+
+ // Same repository, different users.
+ URIish user1Uri = proto.register(new User("user1"), remote.getRepository());
+ URIish user2Uri = proto.register(new User("user2"), remote.getRepository());
+
+ try (Git git = new Git(local.getRepository())) {
+ try {
+ git.fetch()
+ .setRemote(user1Uri.toString())
+ .setRefSpecs(HEADS)
+ .call();
+ } catch (InvalidRemoteException expected) {
+ // Expected.
+ }
+ assertEquals(1, rejected.get());
+ assertNull(local.getRepository().exactRef("refs/heads/master"));
+
+ git.fetch()
+ .setRemote(user2Uri.toString())
+ .setRefSpecs(HEADS)
+ .call();
+ assertEquals(1, rejected.get());
+ assertEquals(master,
+ local.getRepository().exactRef("refs/heads/master").getObjectId());
+ }
+ }
+
+ @Test
+ public void testReceivePackFactory() throws Exception {
+ ObjectId master = local.branch("master").commit().create();
+
+ final AtomicInteger rejected = new AtomicInteger();
+ TestProtocol<User> proto = registerProto(new DefaultUpload(),
+ new ReceivePackFactory<User>() {
+ @Override
+ public ReceivePack create(User req, Repository db)
+ throws ServiceNotAuthorizedException {
+ if (!"user2".equals(req.name)) {
+ rejected.incrementAndGet();
+ throw new ServiceNotAuthorizedException();
+ }
+ return new ReceivePack(db);
+ }
+ });
+
+ // Same repository, different users.
+ URIish user1Uri = proto.register(new User("user1"), remote.getRepository());
+ URIish user2Uri = proto.register(new User("user2"), remote.getRepository());
+
+ try (Git git = new Git(local.getRepository())) {
+ try {
+ git.push()
+ .setRemote(user1Uri.toString())
+ .setRefSpecs(HEADS)
+ .call();
+ } catch (TransportException expected) {
+ assertTrue(expected.getMessage().contains(
+ JGitText.get().pushNotPermitted));
+ }
+ assertEquals(1, rejected.get());
+ assertNull(remote.getRepository().exactRef("refs/heads/master"));
+
+ git.push()
+ .setRemote(user2Uri.toString())
+ .setRefSpecs(HEADS)
+ .call();
+ assertEquals(1, rejected.get());
+ assertEquals(master,
+ remote.getRepository().exactRef("refs/heads/master").getObjectId());
+ }
+ }
+
+ private TestProtocol<User> registerDefault() {
+ return registerProto(new DefaultUpload(), new DefaultReceive());
+ }
+
+ private TestProtocol<User> registerProto(UploadPackFactory<User> upf,
+ ReceivePackFactory<User> rpf) {
+ TestProtocol<User> proto = new TestProtocol<User>(upf, rpf);
+ protos.add(proto);
+ Transport.register(proto);
+ return proto;
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java
index 55e1e44..5519f61 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java
@@ -61,13 +61,10 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class TransportTest extends SampleDataRepositoryTestCase {
- private Transport transport;
-
private RemoteConfig remoteConfig;
@Override
@@ -77,17 +74,6 @@ public class TransportTest extends SampleDataRepositoryTestCase {
final Config config = db.getConfig();
remoteConfig = new RemoteConfig(config, "test");
remoteConfig.addURI(new URIish("http://everyones.loves.git/u/2"));
- transport = null;
- }
-
- @Override
- @After
- public void tearDown() throws Exception {
- if (transport != null) {
- transport.close();
- transport = null;
- }
- super.tearDown();
}
/**
@@ -99,10 +85,11 @@ public class TransportTest extends SampleDataRepositoryTestCase {
@Test
public void testFindRemoteRefUpdatesNoWildcardNoTracking()
throws IOException {
- transport = Transport.open(db, remoteConfig);
- final Collection<RemoteRefUpdate> result = transport
- .findRemoteRefUpdatesFor(Collections.nCopies(1, new RefSpec(
- "refs/heads/master:refs/heads/x")));
+ Collection<RemoteRefUpdate> result;
+ try (Transport transport = Transport.open(db, remoteConfig)) {
+ result = transport.findRemoteRefUpdatesFor(Collections.nCopies(1,
+ new RefSpec("refs/heads/master:refs/heads/x")));
+ }
assertEquals(1, result.size());
final RemoteRefUpdate rru = result.iterator().next();
@@ -122,10 +109,11 @@ public class TransportTest extends SampleDataRepositoryTestCase {
@Test
public void testFindRemoteRefUpdatesNoWildcardNoDestination()
throws IOException {
- transport = Transport.open(db, remoteConfig);
- final Collection<RemoteRefUpdate> result = transport
- .findRemoteRefUpdatesFor(Collections.nCopies(1, new RefSpec(
- "+refs/heads/master")));
+ Collection<RemoteRefUpdate> result;
+ try (Transport transport = Transport.open(db, remoteConfig)) {
+ result = transport.findRemoteRefUpdatesFor(
+ Collections.nCopies(1, new RefSpec("+refs/heads/master")));
+ }
assertEquals(1, result.size());
final RemoteRefUpdate rru = result.iterator().next();
@@ -143,10 +131,11 @@ public class TransportTest extends SampleDataRepositoryTestCase {
*/
@Test
public void testFindRemoteRefUpdatesWildcardNoTracking() throws IOException {
- transport = Transport.open(db, remoteConfig);
- final Collection<RemoteRefUpdate> result = transport
- .findRemoteRefUpdatesFor(Collections.nCopies(1, new RefSpec(
- "+refs/heads/*:refs/heads/test/*")));
+ Collection<RemoteRefUpdate> result;
+ try (Transport transport = Transport.open(db, remoteConfig)) {
+ result = transport.findRemoteRefUpdatesFor(Collections.nCopies(1,
+ new RefSpec("+refs/heads/*:refs/heads/test/*")));
+ }
assertEquals(12, result.size());
boolean foundA = false;
@@ -171,12 +160,14 @@ public class TransportTest extends SampleDataRepositoryTestCase {
*/
@Test
public void testFindRemoteRefUpdatesTwoRefSpecs() throws IOException {
- transport = Transport.open(db, remoteConfig);
final RefSpec specA = new RefSpec("+refs/heads/a:refs/heads/b");
final RefSpec specC = new RefSpec("+refs/heads/c:refs/heads/d");
final Collection<RefSpec> specs = Arrays.asList(specA, specC);
- final Collection<RemoteRefUpdate> result = transport
- .findRemoteRefUpdatesFor(specs);
+
+ Collection<RemoteRefUpdate> result;
+ try (Transport transport = Transport.open(db, remoteConfig)) {
+ result = transport.findRemoteRefUpdatesFor(specs);
+ }
assertEquals(2, result.size());
boolean foundA = false;
@@ -202,10 +193,12 @@ public class TransportTest extends SampleDataRepositoryTestCase {
public void testFindRemoteRefUpdatesTrackingRef() throws IOException {
remoteConfig.addFetchRefSpec(new RefSpec(
"refs/heads/*:refs/remotes/test/*"));
- transport = Transport.open(db, remoteConfig);
- final Collection<RemoteRefUpdate> result = transport
- .findRemoteRefUpdatesFor(Collections.nCopies(1, new RefSpec(
- "+refs/heads/a:refs/heads/a")));
+
+ Collection<RemoteRefUpdate> result;
+ try (Transport transport = Transport.open(db, remoteConfig)) {
+ result = transport.findRemoteRefUpdatesFor(Collections.nCopies(1,
+ new RefSpec("+refs/heads/a:refs/heads/a")));
+ }
assertEquals(1, result.size());
final TrackingRefUpdate tru = result.iterator().next()
@@ -225,20 +218,18 @@ public class TransportTest extends SampleDataRepositoryTestCase {
config.addURI(new URIish("../" + otherDir));
// Should not throw NoRemoteRepositoryException
- transport = Transport.open(db, config);
+ Transport.open(db, config).close();
}
@Test
public void testLocalTransportFetchWithoutLocalRepository()
throws Exception {
URIish uri = new URIish("file://" + db.getWorkTree().getAbsolutePath());
- transport = Transport.open(uri);
- FetchConnection fetchConnection = transport.openFetch();
- try {
- Ref head = fetchConnection.getRef(Constants.HEAD);
- assertNotNull(head);
- } finally {
- fetchConnection.close();
+ try (Transport transport = Transport.open(uri)) {
+ try (FetchConnection fetchConnection = transport.openFetch()) {
+ Ref head = fetchConnection.getRef(Constants.HEAD);
+ assertNotNull(head);
+ }
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java
index 8c7c992..e55d373 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java
@@ -3,6 +3,7 @@
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg at dewire.com>
* Copyright (C) 2008, Shawn O. Pearce <spearce at spearce.org>
* Copyright (C) 2013, Robin Stocker <robin at nibor.org>
+ * Copyright (C) 2015, Patrick Steinhardt <ps at pks.im>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -50,7 +51,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import java.io.File;
import java.io.IOException;
@@ -63,24 +63,16 @@ public class URIishTest {
private static final String GIT_SCHEME = "git://";
- @Test
+ @SuppressWarnings("unused")
+ @Test(expected = URISyntaxException.class)
public void shouldRaiseErrorOnEmptyURI() throws Exception {
- try {
- new URIish("");
- fail("expecting an exception");
- } catch (URISyntaxException e) {
- // expected
- }
+ new URIish("");
}
- @Test
+ @SuppressWarnings("unused")
+ @Test(expected = URISyntaxException.class)
public void shouldRaiseErrorOnNullURI() throws Exception {
- try {
- new URIish((String) null);
- fail("expecting an exception");
- } catch (URISyntaxException e) {
- // expected
- }
+ new URIish((String) null);
}
@Test
@@ -379,6 +371,56 @@ public class URIishTest {
}
@Test
+ public void testSshProtoHostOnly() throws Exception {
+ final String str = "ssh://example.com/";
+ URIish u = new URIish(str);
+ assertEquals("ssh", u.getScheme());
+ assertTrue(u.isRemote());
+ assertEquals("/", u.getRawPath());
+ assertEquals("/", u.getPath());
+ assertEquals("example.com", u.getHost());
+ assertEquals(-1, u.getPort());
+ assertEquals("ssh://example.com/", u.toString());
+ assertEquals("ssh://example.com/", u.toASCIIString());
+ assertEquals("example.com", u.getHumanishName());
+ assertEquals(u, new URIish(str));
+ }
+
+ @Test
+ public void testSshProtoHostWithAuthentication() throws Exception {
+ final String str = "ssh://user:secret@pass@example.com/";
+ URIish u = new URIish(str);
+ assertEquals("ssh", u.getScheme());
+ assertTrue(u.isRemote());
+ assertEquals("/", u.getRawPath());
+ assertEquals("/", u.getPath());
+ assertEquals("example.com", u.getHost());
+ assertEquals(-1, u.getPort());
+ assertEquals("ssh://user@example.com/", u.toString());
+ assertEquals("ssh://user@example.com/", u.toASCIIString());
+ assertEquals("example.com", u.getHumanishName());
+ assertEquals("user", u.getUser());
+ assertEquals("secret at pass", u.getPass());
+ assertEquals(u, new URIish(str));
+ }
+
+ @Test
+ public void testSshProtoHostWithPort() throws Exception {
+ final String str = "ssh://example.com:2222/";
+ URIish u = new URIish(str);
+ assertEquals("ssh", u.getScheme());
+ assertTrue(u.isRemote());
+ assertEquals("/", u.getRawPath());
+ assertEquals("/", u.getPath());
+ assertEquals("example.com", u.getHost());
+ assertEquals(2222, u.getPort());
+ assertEquals("ssh://example.com:2222/", u.toString());
+ assertEquals("ssh://example.com:2222/", u.toASCIIString());
+ assertEquals("example.com", u.getHumanishName());
+ assertEquals(u, new URIish(str));
+ }
+
+ @Test
public void testSshProtoWithUserAndPort() throws Exception {
final String str = "ssh://user@example.com:33/some/p ath";
URIish u = new URIish(str);
@@ -418,6 +460,48 @@ public class URIishTest {
}
@Test
+ public void testSshProtoWithEmailUserAndPort() throws Exception {
+ final String str = "ssh://user.name@email.com@example.com:33/some/p ath";
+ URIish u = new URIish(str);
+ assertEquals("ssh", u.getScheme());
+ assertTrue(u.isRemote());
+ assertEquals("/some/p ath", u.getRawPath());
+ assertEquals("/some/p ath", u.getPath());
+ assertEquals("example.com", u.getHost());
+ assertEquals("user.name at email.com", u.getUser());
+ assertNull(u.getPass());
+ assertEquals(33, u.getPort());
+ assertEquals("ssh://user.name%40email.com@example.com:33/some/p ath",
+ u.toPrivateString());
+ assertEquals("ssh://user.name%40email.com@example.com:33/some/p%20ath",
+ u.toPrivateASCIIString());
+ assertEquals(u.setPass(null).toPrivateString(), u.toString());
+ assertEquals(u.setPass(null).toPrivateASCIIString(), u.toASCIIString());
+ assertEquals(u, new URIish(str));
+ }
+
+ @Test
+ public void testSshProtoWithEmailUserPassAndPort() throws Exception {
+ final String str = "ssh://user.name@email.com:pass@wor:d@example.com:33/some/p ath";
+ URIish u = new URIish(str);
+ assertEquals("ssh", u.getScheme());
+ assertTrue(u.isRemote());
+ assertEquals("/some/p ath", u.getRawPath());
+ assertEquals("/some/p ath", u.getPath());
+ assertEquals("example.com", u.getHost());
+ assertEquals("user.name at email.com", u.getUser());
+ assertEquals("pass at wor:d", u.getPass());
+ assertEquals(33, u.getPort());
+ assertEquals("ssh://user.name%40email.com:pass%40wor%3ad@example.com:33/some/p ath",
+ u.toPrivateString());
+ assertEquals("ssh://user.name%40email.com:pass%40wor%3ad@example.com:33/some/p%20ath",
+ u.toPrivateASCIIString());
+ assertEquals(u.setPass(null).toPrivateString(), u.toString());
+ assertEquals(u.setPass(null).toPrivateASCIIString(), u.toASCIIString());
+ assertEquals(u, new URIish(str));
+ }
+
+ @Test
public void testSshProtoWithADUserPassAndPort() throws Exception {
final String str = "ssh://DOMAIN\\user:pass@example.com:33/some/p ath";
URIish u = new URIish(str);
@@ -541,34 +625,19 @@ public class URIishTest {
assertEquals(u, new URIish(str));
}
- @Test
+ @Test(expected = IllegalArgumentException.class)
public void testGetNullHumanishName() {
- try {
- new URIish().getHumanishName();
- fail("path must be not null");
- } catch (IllegalArgumentException e) {
- // expected
- }
+ new URIish().getHumanishName();
}
- @Test
+ @Test(expected = IllegalArgumentException.class)
public void testGetEmptyHumanishName() throws URISyntaxException {
- try {
- new URIish(GIT_SCHEME).getHumanishName();
- fail("empty path is useless");
- } catch (IllegalArgumentException e) {
- // expected
- }
+ new URIish(GIT_SCHEME).getHumanishName();
}
- @Test
+ @Test(expected = IllegalArgumentException.class)
public void testGetAbsEmptyHumanishName() {
- try {
- new URIish().getHumanishName();
- fail("empty path is useless");
- } catch (IllegalArgumentException e) {
- // expected
- }
+ new URIish().getHumanishName();
}
@Test
@@ -623,6 +692,13 @@ public class URIishTest {
}
@Test
+ public void testGetEmptyHumanishNameWithAuthorityOnly() throws IllegalArgumentException,
+ URISyntaxException {
+ String humanishName = new URIish(GIT_SCHEME + "abc").getHumanishName();
+ assertEquals("abc", humanishName);
+ }
+
+ @Test
public void testGetValidSlashHumanishName()
throws IllegalArgumentException, URISyntaxException {
String humanishName = new URIish(GIT_SCHEME + "host/abc/")
@@ -870,4 +946,19 @@ public class URIishTest {
}
}
}
+
+ @Test
+ public void testStringConstructor() throws Exception {
+ String str = "http://example.com/";
+ URIish u = new URIish(str);
+ assertEquals("example.com", u.getHost());
+ assertEquals("/", u.getPath());
+ assertEquals(str, u.toString());
+
+ str = "http://example.com";
+ u = new URIish(str);
+ assertEquals("example.com", u.getHost());
+ assertEquals("", u.getPath());
+ assertEquals(str, u.toString());
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java
new file mode 100644
index 0000000..ac2bfd1
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java
@@ -0,0 +1,1318 @@
+/*
+ * Copyright (C) 2015, Andrei Pozolotin.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.UTF_8;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.cryptoCipherListPBE;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.cryptoCipherListTrans;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.folderDelete;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.permitLongTests;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.policySetup;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.product;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.proxySetup;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.publicAddress;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.reportPolicy;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.securityProviderName;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.textWrite;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.transferStream;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.verifyFileContent;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.net.SocketTimeoutException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.UnknownHostException;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.security.GeneralSecurityException;
+import java.security.Provider;
+import java.security.Security;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.UUID;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKeyFactory;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
+import org.eclipse.jgit.util.FileUtils;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.junit.runners.Suite;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Amazon S3 encryption pipeline test.
+ *
+ * See {@link AmazonS3} {@link WalkEncryption}
+ *
+ * Note: CI server must provide amazon credentials (access key, secret key,
+ * bucket name) via one of methods available in {@link Names}.
+ *
+ * Note: long running tests are activated by Maven profile "test.long". There is
+ * also a separate Eclipse m2e launcher for that. See 'pom.xml' and
+ * 'WalkEncryptionTest.launch'.
+ */
+ at RunWith(Suite.class)
+ at Suite.SuiteClasses({ //
+ WalkEncryptionTest.Required.class, //
+ WalkEncryptionTest.MinimalSet.class, //
+ WalkEncryptionTest.TestablePBE.class, //
+ WalkEncryptionTest.TestableTransformation.class, //
+})
+public class WalkEncryptionTest {
+
+ /**
+ * Logger setup: ${project_loc}/tst-rsrc/log4j.properties
+ */
+ static final Logger logger = LoggerFactory.getLogger(WalkEncryptionTest.class);
+
+ /**
+ * Property names used in test session.
+ */
+ interface Names {
+
+ // Names of discovered test properties.
+
+ String TEST_BUCKET = "test.bucket";
+
+ // Names of test environment variables for CI.
+
+ String ENV_ACCESS_KEY = "JGIT_S3_ACCESS_KEY";
+
+ String ENV_SECRET_KEY = "JGIT_S3_SECRET_KEY";
+
+ String ENV_BUCKET_NAME = "JGIT_S3_BUCKET_NAME";
+
+ // Name of test environment variable file path for CI.
+
+ String ENV_CONFIG_FILE = "JGIT_S3_CONFIG_FILE";
+
+ // Names of test system properties for CI.
+
+ String SYS_ACCESS_KEY = "jgit.s3.access.key";
+
+ String SYS_SECRET_KEY = "jgit.s3.secret.key";
+
+ String SYS_BUCKET_NAME = "jgit.s3.bucket.name";
+
+ // Name of test system property file path for CI.
+ String SYS_CONFIG_FILE = "jgit.s3.config.file";
+
+ // Hard coded name of test properties file for CI.
+ // File format follows AmazonS3.Keys:
+ // #
+ // # Required entries:
+ // #
+ // accesskey = your-amazon-access-key # default AmazonS3.Keys
+ // secretkey = your-amazon-secret-key # default AmazonS3.Keys
+ // test.bucket = your-bucket-for-testing # custom name, for this test
+ String CONFIG_FILE = "jgit-s3-config.properties";
+
+ // Test properties file in [user home] of CI.
+ String HOME_CONFIG_FILE = System.getProperty("user.home")
+ + File.separator + CONFIG_FILE;
+
+ // Test properties file in [project work directory] of CI.
+ String WORK_CONFIG_FILE = System.getProperty("user.dir")
+ + File.separator + CONFIG_FILE;
+
+ // Test properties file in [project test source directory] of CI.
+ String TEST_CONFIG_FILE = System.getProperty("user.dir")
+ + File.separator + "tst-rsrc" + File.separator + CONFIG_FILE;
+
+ }
+
+ /**
+ * Find test properties from various sources in order of priority.
+ */
+ static class Props implements WalkEncryptionTest.Names, AmazonS3.Keys {
+
+ static boolean haveEnvVar(String name) {
+ return System.getenv(name) != null;
+ }
+
+ static boolean haveEnvVarFile(String name) {
+ return haveEnvVar(name) && new File(name).exists();
+ }
+
+ static boolean haveSysProp(String name) {
+ return System.getProperty(name) != null;
+ }
+
+ static boolean haveSysPropFile(String name) {
+ return haveSysProp(name) && new File(name).exists();
+ }
+
+ static void loadEnvVar(String source, String target, Properties props) {
+ props.put(target, System.getenv(source));
+ }
+
+ static void loadSysProp(String source, String target,
+ Properties props) {
+ props.put(target, System.getProperty(source));
+ }
+
+ static boolean haveProp(String name, Properties props) {
+ return props.containsKey(name);
+ }
+
+ static boolean checkTestProps(Properties props) {
+ return haveProp(ACCESS_KEY, props) && haveProp(SECRET_KEY, props)
+ && haveProp(TEST_BUCKET, props);
+ }
+
+ static Properties fromEnvVars() {
+ if (haveEnvVar(ENV_ACCESS_KEY) && haveEnvVar(ENV_SECRET_KEY)
+ && haveEnvVar(ENV_BUCKET_NAME)) {
+ Properties props = new Properties();
+ loadEnvVar(ENV_ACCESS_KEY, ACCESS_KEY, props);
+ loadEnvVar(ENV_SECRET_KEY, SECRET_KEY, props);
+ loadEnvVar(ENV_BUCKET_NAME, TEST_BUCKET, props);
+ return props;
+ } else {
+ return null;
+ }
+ }
+
+ static Properties fromEnvFile() throws Exception {
+ if (haveEnvVarFile(ENV_CONFIG_FILE)) {
+ Properties props = new Properties();
+ props.load(new FileInputStream(ENV_CONFIG_FILE));
+ if (checkTestProps(props)) {
+ return props;
+ } else {
+ throw new Error("Environment config file is incomplete.");
+ }
+ } else {
+ return null;
+ }
+ }
+
+ static Properties fromSysProps() {
+ if (haveSysProp(SYS_ACCESS_KEY) && haveSysProp(SYS_SECRET_KEY)
+ && haveSysProp(SYS_BUCKET_NAME)) {
+ Properties props = new Properties();
+ loadSysProp(SYS_ACCESS_KEY, ACCESS_KEY, props);
+ loadSysProp(SYS_SECRET_KEY, SECRET_KEY, props);
+ loadSysProp(SYS_BUCKET_NAME, TEST_BUCKET, props);
+ return props;
+ } else {
+ return null;
+ }
+ }
+
+ static Properties fromSysFile() throws Exception {
+ if (haveSysPropFile(SYS_CONFIG_FILE)) {
+ Properties props = new Properties();
+ props.load(new FileInputStream(SYS_CONFIG_FILE));
+ if (checkTestProps(props)) {
+ return props;
+ } else {
+ throw new Error("System props config file is incomplete.");
+ }
+ } else {
+ return null;
+ }
+ }
+
+ static Properties fromConfigFile(String path) throws Exception {
+ File file = new File(path);
+ if (file.exists()) {
+ Properties props = new Properties();
+ props.load(new FileInputStream(file));
+ if (checkTestProps(props)) {
+ return props;
+ } else {
+ throw new Error("Props config file is incomplete: " + path);
+ }
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Find test properties from various sources in order of priority.
+ *
+ * @return result
+ * @throws Exception
+ */
+ static Properties discover() throws Exception {
+ Properties props;
+ if ((props = fromEnvVars()) != null) {
+ logger.debug(
+ "Using test properties from environment variables.");
+ return props;
+ }
+ if ((props = fromEnvFile()) != null) {
+ logger.debug(
+ "Using test properties from environment variable config file.");
+ return props;
+ }
+ if ((props = fromSysProps()) != null) {
+ logger.debug("Using test properties from system properties.");
+ return props;
+ }
+ if ((props = fromSysFile()) != null) {
+ logger.debug(
+ "Using test properties from system property config file.");
+ return props;
+ }
+ if ((props = fromConfigFile(HOME_CONFIG_FILE)) != null) {
+ logger.debug(
+ "Using test properties from hard coded ${user.home} file.");
+ return props;
+ }
+ if ((props = fromConfigFile(WORK_CONFIG_FILE)) != null) {
+ logger.debug(
+ "Using test properties from hard coded ${user.dir} file.");
+ return props;
+ }
+ if ((props = fromConfigFile(TEST_CONFIG_FILE)) != null) {
+ logger.debug(
+ "Using test properties from hard coded ${project.source} file.");
+ return props;
+ }
+ throw new Error("Can not load test properties form any source.");
+ }
+
+ }
+
+ /**
+ * Collection of test utility methods.
+ */
+ static class Util {
+
+ static final Charset UTF_8 = Charset.forName("UTF-8");
+
+ /**
+ * Read UTF-8 encoded text file into string.
+ *
+ * @param file
+ * @return result
+ * @throws Exception
+ */
+ static String textRead(File file) throws Exception {
+ return new String(Files.readAllBytes(file.toPath()), UTF_8);
+ }
+
+ /**
+ * Write string into UTF-8 encoded file.
+ *
+ * @param file
+ * @param text
+ * @throws Exception
+ */
+ static void textWrite(File file, String text) throws Exception {
+ Files.write(file.toPath(), text.getBytes(UTF_8));
+ }
+
+ static void verifyFileContent(File fileOne, File fileTwo)
+ throws Exception {
+ assertTrue(fileOne.length() > 0);
+ assertTrue(fileTwo.length() > 0);
+ String textOne = textRead(fileOne);
+ String textTwo = textRead(fileTwo);
+ assertEquals(textOne, textTwo);
+ }
+
+ /**
+ * Create local folder.
+ *
+ * @param folder
+ * @throws Exception
+ */
+ static void folderCreate(String folder) throws Exception {
+ File path = new File(folder);
+ assertTrue(path.mkdirs());
+ }
+
+ /**
+ * Delete local folder.
+ *
+ * @param folder
+ * @throws Exception
+ */
+ static void folderDelete(String folder) throws Exception {
+ File path = new File(folder);
+ FileUtils.delete(path,
+ FileUtils.RECURSIVE | FileUtils.SKIP_MISSING);
+ }
+
+ /**
+ * Discover public address of CI server.
+ *
+ * @return result
+ * @throws Exception
+ */
+ static String publicAddress() throws Exception {
+ try {
+ String service = "http://checkip.amazonaws.com";
+ URL url = new URL(service);
+ URLConnection c = url.openConnection();
+ c.setConnectTimeout(500);
+ c.setReadTimeout(500);
+ BufferedReader reader = new BufferedReader(
+ new InputStreamReader(c.getInputStream()));
+ try {
+ return reader.readLine();
+ } finally {
+ reader.close();
+ }
+ } catch (UnknownHostException | SocketTimeoutException e) {
+ return "Can't reach http://checkip.amazonaws.com to"
+ + " determine public address";
+ }
+ }
+
+ /**
+ * Discover Password-Based Encryption (PBE) engines providing both
+ * [SecretKeyFactory] and [AlgorithmParameters].
+ *
+ * @return result
+ */
+ // https://www.bouncycastle.org/specifications.html
+ // https://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html
+ static List<String> cryptoCipherListPBE() {
+ return cryptoCipherList(WalkEncryption.Vals.REGEX_PBE);
+ }
+
+ // TODO returns inconsistent list.
+ static List<String> cryptoCipherListTrans() {
+ return cryptoCipherList(WalkEncryption.Vals.REGEX_TRANS);
+ }
+
+ static String securityProviderName(String algorithm) throws Exception {
+ return SecretKeyFactory.getInstance(algorithm).getProvider()
+ .getName();
+ }
+
+ static List<String> cryptoCipherList(String regex) {
+ Set<String> source = Security.getAlgorithms("Cipher");
+ Set<String> target = new TreeSet<String>();
+ for (String algo : source) {
+ algo = algo.toUpperCase();
+ if (algo.matches(regex)) {
+ target.add(algo);
+ }
+ }
+ return new ArrayList<String>(target);
+ }
+
+ /**
+ * Stream copy.
+ *
+ * @param from
+ * @param into
+ * @return count
+ * @throws IOException
+ */
+ static long transferStream(InputStream from, OutputStream into)
+ throws IOException {
+ byte[] array = new byte[1 * 1024];
+ long total = 0;
+ while (true) {
+ int count = from.read(array);
+ if (count == -1) {
+ break;
+ }
+ into.write(array, 0, count);
+ total += count;
+ }
+ return total;
+ }
+
+ /**
+ * Setup proxy during CI build.
+ *
+ * @throws Exception
+ */
+ // https://wiki.eclipse.org/Hudson#Accessing_the_Internet_using_Proxy
+ // http://docs.oracle.com/javase/7/docs/api/java/net/doc-files/net-properties.html
+ static void proxySetup() throws Exception {
+ String keyNoProxy = "no_proxy";
+ String keyHttpProxy = "http_proxy";
+ String keyHttpsProxy = "https_proxy";
+
+ String no_proxy = System.getProperty(keyNoProxy,
+ System.getenv(keyNoProxy));
+ if (no_proxy != null) {
+ System.setProperty("http.nonProxyHosts", no_proxy);
+ logger.info("Proxy NOT: " + no_proxy);
+ }
+
+ String http_proxy = System.getProperty(keyHttpProxy,
+ System.getenv(keyHttpProxy));
+ if (http_proxy != null) {
+ URL url = new URL(http_proxy);
+ System.setProperty("http.proxyHost", url.getHost());
+ System.setProperty("http.proxyPort", "" + url.getPort());
+ logger.info("Proxy HTTP: " + http_proxy);
+ }
+
+ String https_proxy = System.getProperty(keyHttpsProxy,
+ System.getenv(keyHttpsProxy));
+ if (https_proxy != null) {
+ URL url = new URL(https_proxy);
+ System.setProperty("https.proxyHost", url.getHost());
+ System.setProperty("https.proxyPort", "" + url.getPort());
+ logger.info("Proxy HTTPS: " + https_proxy);
+ }
+
+ if (no_proxy == null && http_proxy == null && https_proxy == null) {
+ logger.info("Proxy not used.");
+ }
+
+ }
+
+ /**
+ * Permit long tests on CI or with manual activation.
+ *
+ * @return result
+ */
+ static boolean permitLongTests() {
+ return isBuildCI() || isProfileActive();
+ }
+
+ /**
+ * Using Maven profile activation, see pom.xml
+ *
+ * @return result
+ */
+ static boolean isProfileActive() {
+ return Boolean.parseBoolean(System.getProperty("jgit.test.long"));
+ }
+
+ /**
+ * Detect if build is running on CI.
+ *
+ * @return result
+ */
+ static boolean isBuildCI() {
+ return System.getenv("HUDSON_HOME") != null;
+ }
+
+ /**
+ * Setup JCE security policy restrictions. Can remove restrictions when
+ * restrictions are present, but can not impose them when restrictions
+ * are missing.
+ *
+ * @param restrictedOn
+ */
+ // http://www.docjar.com/html/api/javax/crypto/JceSecurity.java.html
+ static void policySetup(boolean restrictedOn) {
+ try {
+ java.lang.reflect.Field isRestricted = Class
+ .forName("javax.crypto.JceSecurity")
+ .getDeclaredField("isRestricted");
+ isRestricted.setAccessible(true);
+ isRestricted.set(null, new Boolean(restrictedOn));
+ } catch (Throwable e) {
+ logger.info(
+ "Could not setup JCE security policy restrictions.");
+ }
+ }
+
+ static void reportPolicy() {
+ try {
+ java.lang.reflect.Field isRestricted = Class
+ .forName("javax.crypto.JceSecurity")
+ .getDeclaredField("isRestricted");
+ isRestricted.setAccessible(true);
+ logger.info("JCE security policy restricted="
+ + isRestricted.get(null));
+ } catch (Throwable e) {
+ logger.info(
+ "Could not report JCE security policy restrictions.");
+ }
+ }
+
+ static List<Object[]> product(List<String> one, List<String> two) {
+ List<Object[]> result = new ArrayList<Object[]>();
+ for (String s1 : one) {
+ for (String s2 : two) {
+ result.add(new Object[] { s1, s2 });
+ }
+ }
+ return result;
+ }
+
+ }
+
+ /**
+ * Common base for encryption tests.
+ */
+ @FixMethodOrder(MethodSorters.NAME_ASCENDING)
+ public abstract static class Base extends SampleDataRepositoryTestCase {
+
+ /**
+ * S3 URI user used by JGIT to discover connection configuration file.
+ */
+ static final String JGIT_USER = "tester-" + System.currentTimeMillis();
+
+ /**
+ * S3 content encoding password used for this test session.
+ */
+ static final String JGIT_PASS = "secret-" + System.currentTimeMillis();
+
+ /**
+ * S3 repository configuration file expected by {@link AmazonS3}.
+ */
+ static final String JGIT_CONF_FILE = System.getProperty("user.home")
+ + "/" + JGIT_USER;
+
+ /**
+ * Name representing remote or local JGIT repository.
+ */
+ static final String JGIT_REPO_DIR = JGIT_USER + ".jgit";
+
+ /**
+ * Local JGIT repository for this test session.
+ */
+ static final String JGIT_LOCAL_DIR = System.getProperty("user.dir")
+ + "/target/" + JGIT_REPO_DIR;
+
+ /**
+ * Remote JGIT repository for this test session.
+ */
+ static final String JGIT_REMOTE_DIR = JGIT_REPO_DIR;
+
+ /**
+ * Generate JGIT S3 connection configuration file.
+ *
+ * @param algorithm
+ * @throws Exception
+ */
+ static void configCreate(String algorithm) throws Exception {
+ Properties props = Props.discover();
+ props.put(AmazonS3.Keys.PASSWORD, JGIT_PASS);
+ props.put(AmazonS3.Keys.CRYPTO_ALG, algorithm);
+ PrintWriter writer = new PrintWriter(JGIT_CONF_FILE);
+ props.store(writer, "JGIT S3 connection configuration file.");
+ writer.close();
+ }
+
+ /**
+ * Generate JGIT S3 connection configuration file.
+ *
+ * @param source
+ * @throws Exception
+ */
+ static void configCreate(Properties source) throws Exception {
+ Properties target = Props.discover();
+ target.putAll(source);
+ PrintWriter writer = new PrintWriter(JGIT_CONF_FILE);
+ target.store(writer, "JGIT S3 connection configuration file.");
+ writer.close();
+ }
+
+ /**
+ * Remove JGIT connection configuration file.
+ *
+ * @throws Exception
+ */
+ static void configDelete() throws Exception {
+ File path = new File(JGIT_CONF_FILE);
+ FileUtils.delete(path, FileUtils.SKIP_MISSING);
+ }
+
+ /**
+ * Generate remote URI for the test session.
+ *
+ * @return result
+ * @throws Exception
+ */
+ static String amazonURI() throws Exception {
+ Properties props = Props.discover();
+ String bucket = props.getProperty(Names.TEST_BUCKET);
+ assertNotNull(bucket);
+ return TransportAmazonS3.S3_SCHEME + "://" + JGIT_USER + "@"
+ + bucket + "/" + JGIT_REPO_DIR;
+ }
+
+ /**
+ * Create S3 repository folder.
+ *
+ * @throws Exception
+ */
+ static void remoteCreate() throws Exception {
+ Properties props = Props.discover();
+ props.remove(AmazonS3.Keys.PASSWORD); // Disable encryption.
+ String bucket = props.getProperty(Names.TEST_BUCKET);
+ AmazonS3 s3 = new AmazonS3(props);
+ String path = JGIT_REMOTE_DIR + "/";
+ s3.put(bucket, path, new byte[0]);
+ logger.debug("remote create: " + JGIT_REMOTE_DIR);
+ }
+
+ /**
+ * Delete S3 repository folder.
+ *
+ * @throws Exception
+ */
+ static void remoteDelete() throws Exception {
+ Properties props = Props.discover();
+ props.remove(AmazonS3.Keys.PASSWORD); // Disable encryption.
+ String bucket = props.getProperty(Names.TEST_BUCKET);
+ AmazonS3 s3 = new AmazonS3(props);
+ List<String> list = s3.list(bucket, JGIT_REMOTE_DIR);
+ for (String path : list) {
+ path = JGIT_REMOTE_DIR + "/" + path;
+ s3.delete(bucket, path);
+ }
+ logger.debug("remote delete: " + JGIT_REMOTE_DIR);
+ }
+
+ /**
+ * Verify if we can create/delete remote file.
+ *
+ * @throws Exception
+ */
+ static void remoteVerify() throws Exception {
+ Properties props = Props.discover();
+ String bucket = props.getProperty(Names.TEST_BUCKET);
+ AmazonS3 s3 = new AmazonS3(props);
+ String file = JGIT_USER + "-" + UUID.randomUUID().toString();
+ String path = JGIT_REMOTE_DIR + "/" + file;
+ s3.put(bucket, path, file.getBytes(UTF_8));
+ s3.delete(bucket, path);
+ }
+
+ /**
+ * Verify if any security provider published the algorithm.
+ *
+ * @param algorithm
+ * @return result
+ */
+ static boolean isAlgorithmPresent(String algorithm) {
+ Set<String> cipherSet = Security.getAlgorithms("Cipher");
+ for (String source : cipherSet) {
+ // Standard names are not case-sensitive.
+ // http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html
+ String target = algorithm.toUpperCase();
+ if (source.equalsIgnoreCase(target)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static boolean isAlgorithmPresent(Properties props) {
+ String profile = props.getProperty(AmazonS3.Keys.CRYPTO_ALG);
+ String version = props.getProperty(AmazonS3.Keys.CRYPTO_VER,
+ WalkEncryption.Vals.DEFAULT_VERS);
+ String crytoAlgo;
+ String keyAlgo;
+ switch (version) {
+ case WalkEncryption.Vals.DEFAULT_VERS:
+ case WalkEncryption.JGitV1.VERSION:
+ crytoAlgo = profile;
+ keyAlgo = profile;
+ break;
+ case WalkEncryption.JGitV2.VERSION:
+ crytoAlgo = props
+ .getProperty(profile + WalkEncryption.Keys.X_ALGO);
+ keyAlgo = props
+ .getProperty(profile + WalkEncryption.Keys.X_KEY_ALGO);
+ break;
+ default:
+ return false;
+ }
+ try {
+ Cipher.getInstance(crytoAlgo);
+ SecretKeyFactory.getInstance(keyAlgo);
+ return true;
+ } catch (Throwable e) {
+ return false;
+ }
+ }
+
+ /**
+ * Verify if JRE security policy allows the algorithm.
+ *
+ * @param algorithm
+ * @return result
+ */
+ static boolean isAlgorithmAllowed(String algorithm) {
+ try {
+ WalkEncryption crypto = new WalkEncryption.JetS3tV2(
+ algorithm, JGIT_PASS);
+ verifyCrypto(crypto);
+ return true;
+ } catch (IOException e) {
+ return false; // Encryption failure.
+ } catch (GeneralSecurityException e) {
+ throw new Error(e); // Construction failure.
+ }
+ }
+
+ static boolean isAlgorithmAllowed(Properties props) {
+ try {
+ WalkEncryption.instance(props);
+ return true;
+ } catch (GeneralSecurityException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Verify round trip encryption.
+ *
+ * @param crypto
+ * @throws IOException
+ */
+ static void verifyCrypto(WalkEncryption crypto) throws IOException {
+ String charset = "UTF-8";
+ String sourceText = "secret-message Свобода 老子";
+ String targetText;
+ byte[] cipherText;
+ {
+ byte[] origin = sourceText.getBytes(charset);
+ ByteArrayOutputStream target = new ByteArrayOutputStream();
+ OutputStream source = crypto.encrypt(target);
+ source.write(origin);
+ source.flush();
+ source.close();
+ cipherText = target.toByteArray();
+ }
+ {
+ InputStream source = new ByteArrayInputStream(cipherText);
+ InputStream target = crypto.decrypt(source);
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ transferStream(target, result);
+ targetText = result.toString(charset);
+ }
+ assertEquals(sourceText, targetText);
+ }
+
+ /**
+ * Algorithm is testable when it is present and allowed by policy.
+ *
+ * @param algorithm
+ * @return result
+ */
+ static boolean isAlgorithmTestable(String algorithm) {
+ return isAlgorithmPresent(algorithm)
+ && isAlgorithmAllowed(algorithm);
+ }
+
+ static boolean isAlgorithmTestable(Properties props) {
+ return isAlgorithmPresent(props) && isAlgorithmAllowed(props);
+ }
+
+ /**
+ * Log algorithm, provider, testability.
+ *
+ * @param algorithm
+ * @throws Exception
+ */
+ static void reportAlgorithmStatus(String algorithm) throws Exception {
+ final boolean present = isAlgorithmPresent(algorithm);
+ final boolean allowed = present && isAlgorithmAllowed(algorithm);
+ final String provider = present ? securityProviderName(algorithm)
+ : "N/A";
+ String status = "Algorithm: " + algorithm + " @ " + provider + "; "
+ + "present/allowed : " + present + "/" + allowed;
+ if (allowed) {
+ logger.info("Testing " + status);
+ } else {
+ logger.warn("Missing " + status);
+ }
+ }
+
+ static void reportAlgorithmStatus(Properties props) throws Exception {
+ final boolean present = isAlgorithmPresent(props);
+ final boolean allowed = present && isAlgorithmAllowed(props);
+
+ String profile = props.getProperty(AmazonS3.Keys.CRYPTO_ALG);
+ String version = props.getProperty(AmazonS3.Keys.CRYPTO_VER);
+
+ StringBuilder status = new StringBuilder();
+ status.append(" Version: " + version);
+ status.append(" Profile: " + profile);
+ status.append(" Present: " + present);
+ status.append(" Allowed: " + allowed);
+
+ if (allowed) {
+ logger.info("Testing " + status);
+ } else {
+ logger.warn("Missing " + status);
+ }
+ }
+
+ /**
+ * Verify if we can perform remote tests.
+ *
+ * @return result
+ */
+ static boolean isTestConfigPresent() {
+ try {
+ Props.discover();
+ return true;
+ } catch (Throwable e) {
+ return false;
+ }
+ }
+
+ static void reportTestConfigPresent() {
+ if (isTestConfigPresent()) {
+ logger.info("Amazon S3 test configuration is present.");
+ } else {
+ logger.error(
+ "Amazon S3 test configuration is missing, tests will not run.");
+ }
+ }
+
+ /**
+ * Log public address of CI.
+ *
+ * @throws Exception
+ */
+ static void reportPublicAddress() throws Exception {
+ logger.info("Public address: " + publicAddress());
+ }
+
+ /**
+ * BouncyCastle provider class.
+ *
+ * Needs extra dependency, see pom.xml
+ */
+ // http://search.maven.org/#artifactdetails%7Corg.bouncycastle%7Cbcprov-jdk15on%7C1.52%7Cjar
+ static final String PROVIDER_BC = "org.bouncycastle.jce.provider.BouncyCastleProvider";
+
+ /**
+ * Load BouncyCastle provider if present.
+ */
+ static void loadBouncyCastle() {
+ try {
+ Class<?> provider = Class.forName(PROVIDER_BC);
+ Provider instance = (Provider) provider
+ .getConstructor(new Class[] {})
+ .newInstance(new Object[] {});
+ Security.addProvider(instance);
+ logger.info("Loaded " + PROVIDER_BC);
+ } catch (Throwable e) {
+ logger.warn("Failed to load " + PROVIDER_BC);
+ }
+ }
+
+ static void reportLongTests() {
+ if (permitLongTests()) {
+ logger.info("Long running tests are enabled.");
+ } else {
+ logger.warn("Long running tests are disabled.");
+ }
+ }
+
+ /**
+ * Non-PBE algorithm, for error check.
+ */
+ static final String ALGO_ERROR = "PBKDF2WithHmacSHA1";
+
+ /**
+ * Default JetS3t algorithm present in most JRE.
+ */
+ static final String ALGO_JETS3T = "PBEWithMD5AndDES";
+
+ /**
+ * Minimal strength AES based algorithm present in most JRE.
+ */
+ static final String ALGO_MINIMAL_AES = "PBEWithHmacSHA1AndAES_128";
+
+ /**
+ * Selected non-AES algorithm present in BouncyCastle provider.
+ */
+ static final String ALGO_BOUNCY_CASTLE_CBC = "PBEWithSHAAndTwofish-CBC";
+
+ //////////////////////////////////////////////////
+
+ @BeforeClass
+ public static void initialize() throws Exception {
+ Transport.register(TransportAmazonS3.PROTO_S3);
+ proxySetup();
+ reportPolicy();
+ reportLongTests();
+ reportPublicAddress();
+ reportTestConfigPresent();
+ loadBouncyCastle();
+ if (isTestConfigPresent()) {
+ remoteCreate();
+ }
+ }
+
+ @AfterClass
+ public static void terminate() throws Exception {
+ configDelete();
+ folderDelete(JGIT_LOCAL_DIR);
+ if (isTestConfigPresent()) {
+ remoteDelete();
+ }
+ }
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @After
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ * Optional encrypted amazon remote JGIT life cycle test.
+ *
+ * @param props
+ * @throws Exception
+ */
+ void cryptoTestIfCan(Properties props) throws Exception {
+ reportAlgorithmStatus(props);
+ assumeTrue(isTestConfigPresent());
+ assumeTrue(isAlgorithmTestable(props));
+ cryptoTest(props);
+ }
+
+ /**
+ * Required encrypted amazon remote JGIT life cycle test.
+ *
+ * @param props
+ * @throws Exception
+ */
+ void cryptoTest(Properties props) throws Exception {
+
+ remoteDelete();
+ configCreate(props);
+ folderDelete(JGIT_LOCAL_DIR);
+
+ String uri = amazonURI();
+
+ // Local repositories.
+ File dirOne = db.getWorkTree(); // Provided by setup.
+ File dirTwo = new File(JGIT_LOCAL_DIR);
+
+ // Local verification files.
+ String nameStatic = "master.txt"; // Provided by setup.
+ String nameDynamic = JGIT_USER + "-" + UUID.randomUUID().toString();
+
+ String remote = "remote";
+ RefSpec specs = new RefSpec("refs/heads/master:refs/heads/master");
+
+ { // Push into remote from local one.
+
+ StoredConfig config = db.getConfig();
+ RemoteConfig remoteConfig = new RemoteConfig(config, remote);
+ remoteConfig.addURI(new URIish(uri));
+ remoteConfig.update(config);
+ config.save();
+
+ Git git = Git.open(dirOne);
+ git.checkout().setName("master").call();
+ git.push().setRemote(remote).setRefSpecs(specs).call();
+ git.close();
+
+ File fileStatic = new File(dirOne, nameStatic);
+ assertTrue("Provided by setup", fileStatic.exists());
+
+ }
+
+ { // Clone from remote into local two.
+
+ File fileStatic = new File(dirTwo, nameStatic);
+ assertFalse("Not Provided by setup", fileStatic.exists());
+
+ Git git = Git.cloneRepository().setURI(uri).setDirectory(dirTwo)
+ .call();
+ git.close();
+
+ assertTrue("Provided by clone", fileStatic.exists());
+ }
+
+ { // Verify static file content.
+ File fileOne = new File(dirOne, nameStatic);
+ File fileTwo = new File(dirTwo, nameStatic);
+ verifyFileContent(fileOne, fileTwo);
+ }
+
+ { // Verify new file commit and push from local one.
+
+ File fileDynamic = new File(dirOne, nameDynamic);
+ assertFalse("Not Provided by setup", fileDynamic.exists());
+ FileUtils.createNewFile(fileDynamic);
+ textWrite(fileDynamic, nameDynamic);
+ assertTrue("Provided by create", fileDynamic.exists());
+ assertTrue("Need content to encrypt", fileDynamic.length() > 0);
+
+ Git git = Git.open(dirOne);
+ git.add().addFilepattern(nameDynamic).call();
+ git.commit().setMessage(nameDynamic).call();
+ git.push().setRemote(remote).setRefSpecs(specs).call();
+ git.close();
+
+ }
+
+ { // Verify new file pull from remote into local two.
+
+ File fileDynamic = new File(dirTwo, nameDynamic);
+ assertFalse("Not Provided by setup", fileDynamic.exists());
+
+ Git git = Git.open(dirTwo);
+ git.pull().call();
+ git.close();
+
+ assertTrue("Provided by pull", fileDynamic.exists());
+ }
+
+ { // Verify dynamic file content.
+ File fileOne = new File(dirOne, nameDynamic);
+ File fileTwo = new File(dirTwo, nameDynamic);
+ verifyFileContent(fileOne, fileTwo);
+ }
+
+ }
+
+ }
+
+ /**
+ * Verify prerequisites.
+ */
+ @FixMethodOrder(MethodSorters.NAME_ASCENDING)
+ public static class Required extends Base {
+
+ @Test
+ public void test_A1_ValidURI() throws Exception {
+ assumeTrue(isTestConfigPresent());
+ URIish uri = new URIish(amazonURI());
+ assertTrue("uri=" + uri, TransportAmazonS3.PROTO_S3.canHandle(uri));
+ }
+
+ @Test(expected = Exception.class)
+ public void test_A2_CryptoError() throws Exception {
+ assumeTrue(isTestConfigPresent());
+ Properties props = new Properties();
+ props.put(AmazonS3.Keys.CRYPTO_ALG, ALGO_ERROR);
+ props.put(AmazonS3.Keys.PASSWORD, JGIT_PASS);
+ cryptoTest(props);
+ }
+
+ }
+
+ /**
+ * Test minimal set of algorithms.
+ */
+ @FixMethodOrder(MethodSorters.NAME_ASCENDING)
+ public static class MinimalSet extends Base {
+
+ @Test
+ public void test_V0_Java7_JET() throws Exception {
+ assumeTrue(isTestConfigPresent());
+ Properties props = new Properties();
+ props.put(AmazonS3.Keys.CRYPTO_ALG, ALGO_JETS3T);
+ // Do not set version.
+ props.put(AmazonS3.Keys.PASSWORD, JGIT_PASS);
+ cryptoTestIfCan(props);
+ }
+
+ @Test
+ public void test_V1_Java7_GIT() throws Exception {
+ assumeTrue(isTestConfigPresent());
+ Properties props = new Properties();
+ props.put(AmazonS3.Keys.CRYPTO_ALG, ALGO_JETS3T);
+ props.put(AmazonS3.Keys.CRYPTO_VER, "1");
+ props.put(AmazonS3.Keys.PASSWORD, JGIT_PASS);
+ cryptoTestIfCan(props);
+ }
+
+ @Test
+ public void test_V2_Java7_AES() throws Exception {
+ assumeTrue(isTestConfigPresent());
+ // String profile = "default";
+ String profile = "AES/CBC/PKCS5Padding+PBKDF2WithHmacSHA1";
+ Properties props = new Properties();
+ props.put(AmazonS3.Keys.CRYPTO_ALG, profile);
+ props.put(AmazonS3.Keys.CRYPTO_VER, "2");
+ props.put(AmazonS3.Keys.PASSWORD, JGIT_PASS);
+ props.put(profile + WalkEncryption.Keys.X_ALGO, "AES/CBC/PKCS5Padding");
+ props.put(profile + WalkEncryption.Keys.X_KEY_ALGO, "PBKDF2WithHmacSHA1");
+ props.put(profile + WalkEncryption.Keys.X_KEY_SIZE, "128");
+ props.put(profile + WalkEncryption.Keys.X_KEY_ITER, "10000");
+ props.put(profile + WalkEncryption.Keys.X_KEY_SALT, "e2 55 89 67 8e 8d e8 4c");
+ cryptoTestIfCan(props);
+ }
+
+ @Test
+ public void test_V2_Java8_PBE_AES() throws Exception {
+ assumeTrue(isTestConfigPresent());
+ String profile = "PBEWithHmacSHA512AndAES_256";
+ Properties props = new Properties();
+ props.put(AmazonS3.Keys.CRYPTO_ALG, profile);
+ props.put(AmazonS3.Keys.CRYPTO_VER, "2");
+ props.put(AmazonS3.Keys.PASSWORD, JGIT_PASS);
+ props.put(profile + WalkEncryption.Keys.X_ALGO, "PBEWithHmacSHA512AndAES_256");
+ props.put(profile + WalkEncryption.Keys.X_KEY_ALGO, "PBEWithHmacSHA512AndAES_256");
+ props.put(profile + WalkEncryption.Keys.X_KEY_SIZE, "256");
+ props.put(profile + WalkEncryption.Keys.X_KEY_ITER, "10000");
+ props.put(profile + WalkEncryption.Keys.X_KEY_SALT, "e2 55 89 67 8e 8d e8 4c");
+ policySetup(false);
+ cryptoTestIfCan(props);
+ }
+
+ }
+
+ /**
+ * Test all present and allowed PBE algorithms.
+ */
+ // https://github.com/junit-team/junit/wiki/Parameterized-tests
+ @RunWith(Parameterized.class)
+ @FixMethodOrder(MethodSorters.NAME_ASCENDING)
+ public static class TestablePBE extends Base {
+
+ @Parameters(name = "Profile: {0} Version: {1}")
+ public static Collection<Object[]> argsList() {
+ List<String> algorithmList = new ArrayList<String>();
+ algorithmList.addAll(cryptoCipherListPBE());
+
+ List<String> versionList = new ArrayList<String>();
+ versionList.add("0");
+ versionList.add("1");
+
+ return product(algorithmList, versionList);
+ }
+
+ final String profile;
+
+ final String version;
+
+ final String password = JGIT_PASS;
+
+ public TestablePBE(String profile, String version) {
+ this.profile = profile;
+ this.version = version;
+ }
+
+ @Test
+ public void testCrypto() throws Exception {
+ assumeTrue(permitLongTests());
+ Properties props = new Properties();
+ props.put(AmazonS3.Keys.CRYPTO_ALG, profile);
+ props.put(AmazonS3.Keys.CRYPTO_VER, version);
+ props.put(AmazonS3.Keys.PASSWORD, password);
+ cryptoTestIfCan(props);
+ }
+
+ }
+
+ /**
+ * Test all present and allowed transformation algorithms.
+ */
+ // https://github.com/junit-team/junit/wiki/Parameterized-tests
+ @RunWith(Parameterized.class)
+ @FixMethodOrder(MethodSorters.NAME_ASCENDING)
+ public static class TestableTransformation extends Base {
+
+ @Parameters(name = "Profile: {0} Version: {1}")
+ public static Collection<Object[]> argsList() {
+ List<String> algorithmList = new ArrayList<String>();
+ algorithmList.addAll(cryptoCipherListTrans());
+
+ List<String> versionList = new ArrayList<String>();
+ versionList.add("1");
+
+ return product(algorithmList, versionList);
+ }
+
+ final String profile;
+
+ final String version;
+
+ final String password = JGIT_PASS;
+
+ public TestableTransformation(String profile, String version) {
+ this.profile = profile;
+ this.version = version;
+ }
+
+ @Test
+ public void testCrypto() throws Exception {
+ assumeTrue(permitLongTests());
+ Properties props = new Properties();
+ props.put(AmazonS3.Keys.CRYPTO_ALG, profile);
+ props.put(AmazonS3.Keys.CRYPTO_VER, version);
+ props.put(AmazonS3.Keys.PASSWORD, password);
+ cryptoTestIfCan(props);
+ }
+
+ }
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/CanonicalTreeParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/CanonicalTreeParserTest.java
index 52da69e..f5e97c2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/CanonicalTreeParserTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/CanonicalTreeParserTest.java
@@ -43,6 +43,8 @@
package org.eclipse.jgit.treewalk;
+import static org.eclipse.jgit.lib.FileMode.REGULAR_FILE;
+import static org.eclipse.jgit.lib.FileMode.SYMLINK;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
@@ -50,9 +52,11 @@ import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream;
+import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.TreeFormatter;
import org.eclipse.jgit.util.RawParseUtils;
import org.junit.Before;
import org.junit.Test;
@@ -369,4 +373,41 @@ public class CanonicalTreeParserTest {
assertEquals(name, RawParseUtils.decode(Constants.CHARSET, ctp.path,
ctp.pathOffset, ctp.pathLen));
}
+
+ @Test
+ public void testFindAttributesWhenFirst() throws CorruptObjectException {
+ TreeFormatter tree = new TreeFormatter();
+ tree.append(".gitattributes", REGULAR_FILE, hash_a);
+ ctp.reset(tree.toByteArray());
+
+ assertTrue(ctp.findFile(".gitattributes"));
+ assertEquals(REGULAR_FILE.getBits(), ctp.getEntryRawMode());
+ assertEquals(".gitattributes", ctp.getEntryPathString());
+ assertEquals(hash_a, ctp.getEntryObjectId());
+ }
+
+ @Test
+ public void testFindAttributesWhenSecond() throws CorruptObjectException {
+ TreeFormatter tree = new TreeFormatter();
+ tree.append(".config", SYMLINK, hash_a);
+ tree.append(".gitattributes", REGULAR_FILE, hash_foo);
+ ctp.reset(tree.toByteArray());
+
+ assertTrue(ctp.findFile(".gitattributes"));
+ assertEquals(REGULAR_FILE.getBits(), ctp.getEntryRawMode());
+ assertEquals(".gitattributes", ctp.getEntryPathString());
+ assertEquals(hash_foo, ctp.getEntryObjectId());
+ }
+
+ @Test
+ public void testFindAttributesWhenMissing() throws CorruptObjectException {
+ TreeFormatter tree = new TreeFormatter();
+ tree.append("src", REGULAR_FILE, hash_a);
+ tree.append("zoo", REGULAR_FILE, hash_foo);
+ ctp.reset(tree.toByteArray());
+
+ assertFalse(ctp.findFile(".gitattributes"));
+ assertEquals(11, ctp.idOffset()); // Did not walk the entire tree.
+ assertEquals("src", ctp.getEntryPathString());
+ }
}
diff --git a/org.eclipse.jgit.java7.test/src/org/eclipse/jgit/treewalk/FileTreeIteratorJava7Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorJava7Test.java
similarity index 59%
rename from org.eclipse.jgit.java7.test/src/org/eclipse/jgit/treewalk/FileTreeIteratorJava7Test.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorJava7Test.java
index 741396c..cd55cba 100644
--- a/org.eclipse.jgit.java7.test/src/org/eclipse/jgit/treewalk/FileTreeIteratorJava7Test.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorJava7Test.java
@@ -67,6 +67,7 @@ import org.junit.Test;
public class FileTreeIteratorJava7Test extends RepositoryTestCase {
@Test
public void testFileModeSymLinkIsNotATree() throws IOException {
+ org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
FS fs = db.getFS();
// mål = target in swedish, just to get som unicode in here
writeTrashFile("mål/data", "targetdata");
@@ -89,32 +90,33 @@ public class FileTreeIteratorJava7Test extends RepositoryTestCase {
DirCacheEditor dce = dc.editor();
final String UNNORMALIZED = "target/";
final byte[] UNNORMALIZED_BYTES = Constants.encode(UNNORMALIZED);
- ObjectInserter oi = db.newObjectInserter();
- final ObjectId linkid = oi.insert(Constants.OBJ_BLOB,
- UNNORMALIZED_BYTES, 0,
- UNNORMALIZED_BYTES.length);
- oi.release();
- dce.add(new DirCacheEditor.PathEdit("link") {
- @Override
- public void apply(DirCacheEntry ent) {
- ent.setFileMode(FileMode.SYMLINK);
- ent.setObjectId(linkid);
- ent.setLength(UNNORMALIZED_BYTES.length);
- }
- });
- assertTrue(dce.commit());
- new Git(db).commit().setMessage("Adding link").call();
- new Git(db).reset().setMode(ResetType.HARD).call();
- DirCacheIterator dci = new DirCacheIterator(db.readDirCache());
- FileTreeIterator fti = new FileTreeIterator(db);
+ try (ObjectInserter oi = db.newObjectInserter()) {
+ final ObjectId linkid = oi.insert(Constants.OBJ_BLOB,
+ UNNORMALIZED_BYTES, 0, UNNORMALIZED_BYTES.length);
+ dce.add(new DirCacheEditor.PathEdit("link") {
+ @Override
+ public void apply(DirCacheEntry ent) {
+ ent.setFileMode(FileMode.SYMLINK);
+ ent.setObjectId(linkid);
+ ent.setLength(UNNORMALIZED_BYTES.length);
+ }
+ });
+ assertTrue(dce.commit());
+ }
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("Adding link").call();
+ git.reset().setMode(ResetType.HARD).call();
+ DirCacheIterator dci = new DirCacheIterator(db.readDirCache());
+ FileTreeIterator fti = new FileTreeIterator(db);
- // self-check
- assertEquals("link", fti.getEntryPathString());
- assertEquals("link", dci.getEntryPathString());
+ // self-check
+ assertEquals("link", fti.getEntryPathString());
+ assertEquals("link", dci.getEntryPathString());
- // test
- assertFalse(fti.isModified(dci.getDirCacheEntry(), true,
- db.newObjectReader()));
+ // test
+ assertFalse(fti.isModified(dci.getDirCacheEntry(), true,
+ db.newObjectReader()));
+ }
}
/**
@@ -129,31 +131,33 @@ public class FileTreeIteratorJava7Test extends RepositoryTestCase {
DirCacheEditor dce = dc.editor();
final String NORMALIZED = "target";
final byte[] NORMALIZED_BYTES = Constants.encode(NORMALIZED);
- ObjectInserter oi = db.newObjectInserter();
- final ObjectId linkid = oi.insert(Constants.OBJ_BLOB, NORMALIZED_BYTES,
- 0, NORMALIZED_BYTES.length);
- oi.release();
- dce.add(new DirCacheEditor.PathEdit("link") {
- @Override
- public void apply(DirCacheEntry ent) {
- ent.setFileMode(FileMode.SYMLINK);
- ent.setObjectId(linkid);
- ent.setLength(NORMALIZED_BYTES.length);
- }
- });
- assertTrue(dce.commit());
- new Git(db).commit().setMessage("Adding link").call();
- new Git(db).reset().setMode(ResetType.HARD).call();
- DirCacheIterator dci = new DirCacheIterator(db.readDirCache());
- FileTreeIterator fti = new FileTreeIterator(db);
+ try (ObjectInserter oi = db.newObjectInserter()) {
+ final ObjectId linkid = oi.insert(Constants.OBJ_BLOB,
+ NORMALIZED_BYTES, 0, NORMALIZED_BYTES.length);
+ dce.add(new DirCacheEditor.PathEdit("link") {
+ @Override
+ public void apply(DirCacheEntry ent) {
+ ent.setFileMode(FileMode.SYMLINK);
+ ent.setObjectId(linkid);
+ ent.setLength(NORMALIZED_BYTES.length);
+ }
+ });
+ assertTrue(dce.commit());
+ }
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("Adding link").call();
+ git.reset().setMode(ResetType.HARD).call();
+ DirCacheIterator dci = new DirCacheIterator(db.readDirCache());
+ FileTreeIterator fti = new FileTreeIterator(db);
- // self-check
- assertEquals("link", fti.getEntryPathString());
- assertEquals("link", dci.getEntryPathString());
+ // self-check
+ assertEquals("link", fti.getEntryPathString());
+ assertEquals("link", dci.getEntryPathString());
- // test
- assertFalse(fti.isModified(dci.getDirCacheEntry(), true,
- db.newObjectReader()));
+ // test
+ assertFalse(fti.isModified(dci.getDirCacheEntry(), true,
+ db.newObjectReader()));
+ }
}
/**
@@ -164,37 +168,40 @@ public class FileTreeIteratorJava7Test extends RepositoryTestCase {
*/
@Test
public void testSymlinkActuallyModified() throws Exception {
+ org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
final String NORMALIZED = "target";
final byte[] NORMALIZED_BYTES = Constants.encode(NORMALIZED);
- ObjectInserter oi = db.newObjectInserter();
- final ObjectId linkid = oi.insert(Constants.OBJ_BLOB, NORMALIZED_BYTES,
- 0, NORMALIZED_BYTES.length);
- oi.release();
- DirCache dc = db.lockDirCache();
- DirCacheEditor dce = dc.editor();
- dce.add(new DirCacheEditor.PathEdit("link") {
- @Override
- public void apply(DirCacheEntry ent) {
- ent.setFileMode(FileMode.SYMLINK);
- ent.setObjectId(linkid);
- ent.setLength(NORMALIZED_BYTES.length);
- }
- });
- assertTrue(dce.commit());
- new Git(db).commit().setMessage("Adding link").call();
- new Git(db).reset().setMode(ResetType.HARD).call();
+ try (ObjectInserter oi = db.newObjectInserter()) {
+ final ObjectId linkid = oi.insert(Constants.OBJ_BLOB,
+ NORMALIZED_BYTES, 0, NORMALIZED_BYTES.length);
+ DirCache dc = db.lockDirCache();
+ DirCacheEditor dce = dc.editor();
+ dce.add(new DirCacheEditor.PathEdit("link") {
+ @Override
+ public void apply(DirCacheEntry ent) {
+ ent.setFileMode(FileMode.SYMLINK);
+ ent.setObjectId(linkid);
+ ent.setLength(NORMALIZED_BYTES.length);
+ }
+ });
+ assertTrue(dce.commit());
+ }
+ try (Git git = new Git(db)) {
+ git.commit().setMessage("Adding link").call();
+ git.reset().setMode(ResetType.HARD).call();
- FileUtils.delete(new File(trash, "link"), FileUtils.NONE);
- FS.DETECTED.createSymLink(new File(trash, "link"), "newtarget");
- DirCacheIterator dci = new DirCacheIterator(db.readDirCache());
- FileTreeIterator fti = new FileTreeIterator(db);
+ FileUtils.delete(new File(trash, "link"), FileUtils.NONE);
+ FS.DETECTED.createSymLink(new File(trash, "link"), "newtarget");
+ DirCacheIterator dci = new DirCacheIterator(db.readDirCache());
+ FileTreeIterator fti = new FileTreeIterator(db);
- // self-check
- assertEquals("link", fti.getEntryPathString());
- assertEquals("link", dci.getEntryPathString());
+ // self-check
+ assertEquals("link", fti.getEntryPathString());
+ assertEquals("link", dci.getEntryPathString());
- // test
- assertTrue(fti.isModified(dci.getDirCacheEntry(), true,
- db.newObjectReader()));
+ // test
+ assertTrue(fti.isModified(dci.getDirCacheEntry(), true,
+ db.newObjectReader()));
+ }
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
index 7964578..767e13d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java
@@ -274,9 +274,9 @@ public class FileTreeIteratorTest extends RepositoryTestCase {
ObjectId fromRaw = ObjectId.fromRaw(fti.idBuffer(), fti.idOffset());
assertEquals("6b584e8ece562ebffc15d38808cd6b98fc3d97ea",
fromRaw.getName());
- ObjectReader objectReader = db.newObjectReader();
- assertFalse(fti.isModified(dce, false, objectReader));
- objectReader.release();
+ try (ObjectReader objectReader = db.newObjectReader()) {
+ assertFalse(fti.isModified(dce, false, objectReader));
+ }
}
@Test
@@ -291,15 +291,15 @@ public class FileTreeIteratorTest extends RepositoryTestCase {
// Modify previously committed DirCacheEntry and write it back to disk
DirCacheEntry dce = db.readDirCache().getEntry("symlink");
dce.setFileMode(FileMode.SYMLINK);
- ObjectReader objectReader = db.newObjectReader();
- DirCacheCheckout.checkoutEntry(db, dce, objectReader);
-
- FileTreeIterator fti = new FileTreeIterator(trash, db.getFS(), db
- .getConfig().get(WorkingTreeOptions.KEY));
- while (!fti.getEntryPathString().equals("symlink"))
- fti.next(1);
- assertFalse(fti.isModified(dce, false, objectReader));
- objectReader.release();
+ try (ObjectReader objectReader = db.newObjectReader()) {
+ DirCacheCheckout.checkoutEntry(db, dce, objectReader);
+
+ FileTreeIterator fti = new FileTreeIterator(trash, db.getFS(),
+ db.getConfig().get(WorkingTreeOptions.KEY));
+ while (!fti.getEntryPathString().equals("symlink"))
+ fti.next(1);
+ assertFalse(fti.isModified(dce, false, objectReader));
+ }
}
@Test
@@ -327,9 +327,9 @@ public class FileTreeIteratorTest extends RepositoryTestCase {
// If the rounding trick does not work we could skip the compareMetaData
// test and hope that we are usually testing the intended code path.
assertEquals(MetadataDiff.SMUDGED, fti.compareMetadata(dce));
- ObjectReader objectReader = db.newObjectReader();
- assertTrue(fti.isModified(dce, false, objectReader));
- objectReader.release();
+ try (ObjectReader objectReader = db.newObjectReader()) {
+ assertTrue(fti.isModified(dce, false, objectReader));
+ }
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/ForPathTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/ForPathTest.java
index 59c4a8d..eaee8bb 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/ForPathTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/ForPathTest.java
@@ -68,70 +68,69 @@ public class ForPathTest extends RepositoryTestCase {
public void testFindObjects() throws Exception {
final DirCache tree0 = DirCache.newInCore();
final DirCacheBuilder b0 = tree0.builder();
- ObjectReader or = db.newObjectReader();
- ObjectInserter oi = db.newObjectInserter();
-
- DirCacheEntry aDotB = createEntry("a.b", EXECUTABLE_FILE);
- b0.add(aDotB);
- DirCacheEntry aSlashB = createEntry("a/b", REGULAR_FILE);
- b0.add(aSlashB);
- DirCacheEntry aSlashCSlashD = createEntry("a/c/d", REGULAR_FILE);
- b0.add(aSlashCSlashD);
- DirCacheEntry aZeroB = createEntry("a0b", SYMLINK);
- b0.add(aZeroB);
- b0.finish();
- assertEquals(4, tree0.getEntryCount());
- ObjectId tree = tree0.writeTree(oi);
-
- // Find the directories that were implicitly created above.
- TreeWalk tw = new TreeWalk(or);
- tw.addTree(tree);
- ObjectId a = null;
- ObjectId aSlashC = null;
- while (tw.next()) {
- if (tw.getPathString().equals("a")) {
- a = tw.getObjectId(0);
- tw.enterSubtree();
- while (tw.next()) {
- if (tw.getPathString().equals("a/c")) {
- aSlashC = tw.getObjectId(0);
- break;
+ try (ObjectReader or = db.newObjectReader();
+ ObjectInserter oi = db.newObjectInserter()) {
+
+ DirCacheEntry aDotB = createEntry("a.b", EXECUTABLE_FILE);
+ b0.add(aDotB);
+ DirCacheEntry aSlashB = createEntry("a/b", REGULAR_FILE);
+ b0.add(aSlashB);
+ DirCacheEntry aSlashCSlashD = createEntry("a/c/d", REGULAR_FILE);
+ b0.add(aSlashCSlashD);
+ DirCacheEntry aZeroB = createEntry("a0b", SYMLINK);
+ b0.add(aZeroB);
+ b0.finish();
+ assertEquals(4, tree0.getEntryCount());
+ ObjectId tree = tree0.writeTree(oi);
+
+ // Find the directories that were implicitly created above.
+ TreeWalk tw = new TreeWalk(or);
+ tw.addTree(tree);
+ ObjectId a = null;
+ ObjectId aSlashC = null;
+ while (tw.next()) {
+ if (tw.getPathString().equals("a")) {
+ a = tw.getObjectId(0);
+ tw.enterSubtree();
+ while (tw.next()) {
+ if (tw.getPathString().equals("a/c")) {
+ aSlashC = tw.getObjectId(0);
+ break;
+ }
}
+ break;
}
- break;
}
- }
-
- assertEquals(a, TreeWalk.forPath(or, "a", tree).getObjectId(0));
- assertEquals(a, TreeWalk.forPath(or, "a/", tree).getObjectId(0));
- assertEquals(null, TreeWalk.forPath(or, "/a", tree));
- assertEquals(null, TreeWalk.forPath(or, "/a/", tree));
-
- assertEquals(aDotB.getObjectId(), TreeWalk.forPath(or, "a.b", tree)
- .getObjectId(0));
- assertEquals(null, TreeWalk.forPath(or, "/a.b", tree));
- assertEquals(null, TreeWalk.forPath(or, "/a.b/", tree));
- assertEquals(aDotB.getObjectId(), TreeWalk.forPath(or, "a.b/", tree)
- .getObjectId(0));
-
- assertEquals(aZeroB.getObjectId(), TreeWalk.forPath(or, "a0b", tree)
- .getObjectId(0));
- assertEquals(aSlashB.getObjectId(), TreeWalk.forPath(or, "a/b", tree)
- .getObjectId(0));
- assertEquals(aSlashB.getObjectId(), TreeWalk.forPath(or, "b", a)
- .getObjectId(0));
-
- assertEquals(aSlashC, TreeWalk.forPath(or, "a/c", tree).getObjectId(0));
- assertEquals(aSlashC, TreeWalk.forPath(or, "c", a).getObjectId(0));
-
- assertEquals(aSlashCSlashD.getObjectId(),
- TreeWalk.forPath(or, "a/c/d", tree).getObjectId(0));
- assertEquals(aSlashCSlashD.getObjectId(), TreeWalk
- .forPath(or, "c/d", a).getObjectId(0));
-
- or.release();
- oi.release();
+ assertEquals(a, TreeWalk.forPath(or, "a", tree).getObjectId(0));
+ assertEquals(a, TreeWalk.forPath(or, "a/", tree).getObjectId(0));
+ assertEquals(null, TreeWalk.forPath(or, "/a", tree));
+ assertEquals(null, TreeWalk.forPath(or, "/a/", tree));
+
+ assertEquals(aDotB.getObjectId(),
+ TreeWalk.forPath(or, "a.b", tree).getObjectId(0));
+ assertEquals(null, TreeWalk.forPath(or, "/a.b", tree));
+ assertEquals(null, TreeWalk.forPath(or, "/a.b/", tree));
+ assertEquals(aDotB.getObjectId(),
+ TreeWalk.forPath(or, "a.b/", tree).getObjectId(0));
+
+ assertEquals(aZeroB.getObjectId(),
+ TreeWalk.forPath(or, "a0b", tree).getObjectId(0));
+
+ assertEquals(aSlashB.getObjectId(),
+ TreeWalk.forPath(or, "a/b", tree).getObjectId(0));
+ assertEquals(aSlashB.getObjectId(),
+ TreeWalk.forPath(or, "b", a).getObjectId(0));
+
+ assertEquals(aSlashC,
+ TreeWalk.forPath(or, "a/c", tree).getObjectId(0));
+ assertEquals(aSlashC, TreeWalk.forPath(or, "c", a).getObjectId(0));
+
+ assertEquals(aSlashCSlashD.getObjectId(),
+ TreeWalk.forPath(or, "a/c/d", tree).getObjectId(0));
+ assertEquals(aSlashCSlashD.getObjectId(),
+ TreeWalk.forPath(or, "c/d", a).getObjectId(0));
+ }
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/TreeWalkBasicDiffTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/TreeWalkBasicDiffTest.java
index b52a727..c3ff7df 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/TreeWalkBasicDiffTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/TreeWalkBasicDiffTest.java
@@ -44,7 +44,6 @@
package org.eclipse.jgit.treewalk;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
-import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
import static org.eclipse.jgit.lib.Constants.encode;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -54,81 +53,80 @@ import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.Tree;
+import org.eclipse.jgit.lib.TreeFormatter;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
import org.junit.Test;
- at SuppressWarnings("deprecation")
public class TreeWalkBasicDiffTest extends RepositoryTestCase {
@Test
public void testMissingSubtree_DetectFileAdded_FileModified()
throws Exception {
- final ObjectInserter inserter = db.newObjectInserter();
- final ObjectId aFileId = inserter.insert(OBJ_BLOB, encode("a"));
- final ObjectId bFileId = inserter.insert(OBJ_BLOB, encode("b"));
- final ObjectId cFileId1 = inserter.insert(OBJ_BLOB, encode("c-1"));
- final ObjectId cFileId2 = inserter.insert(OBJ_BLOB, encode("c-2"));
+ final ObjectId oldTree, newTree, bFileId, cFileId1, cFileId2;
+ try (ObjectInserter inserter = db.newObjectInserter()) {
+ final ObjectId aFileId = inserter.insert(OBJ_BLOB, encode("a"));
+ bFileId = inserter.insert(OBJ_BLOB, encode("b"));
+ cFileId1 = inserter.insert(OBJ_BLOB, encode("c-1"));
+ cFileId2 = inserter.insert(OBJ_BLOB, encode("c-2"));
- // Create sub-a/empty, sub-c/empty = hello.
- final ObjectId oldTree;
- {
- final Tree root = new Tree(db);
+ // Create sub-a/empty, sub-c/empty = hello.
{
- final Tree subA = root.addTree("sub-a");
- subA.addFile("empty").setId(aFileId);
- subA.setId(inserter.insert(OBJ_TREE, subA.format()));
+ TreeFormatter root = new TreeFormatter();
+ {
+ TreeFormatter subA = new TreeFormatter();
+ subA.append("empty", FileMode.REGULAR_FILE, aFileId);
+ root.append("sub-a", FileMode.TREE, inserter.insert(subA));
+ }
+ {
+ TreeFormatter subC = new TreeFormatter();
+ subC.append("empty", FileMode.REGULAR_FILE, cFileId1);
+ root.append("sub-c", FileMode.TREE, inserter.insert(subC));
+ }
+ oldTree = inserter.insert(root);
}
- {
- final Tree subC = root.addTree("sub-c");
- subC.addFile("empty").setId(cFileId1);
- subC.setId(inserter.insert(OBJ_TREE, subC.format()));
- }
- oldTree = inserter.insert(OBJ_TREE, root.format());
- }
- // Create sub-a/empty, sub-b/empty, sub-c/empty.
- final ObjectId newTree;
- {
- final Tree root = new Tree(db);
- {
- final Tree subA = root.addTree("sub-a");
- subA.addFile("empty").setId(aFileId);
- subA.setId(inserter.insert(OBJ_TREE, subA.format()));
- }
- {
- final Tree subB = root.addTree("sub-b");
- subB.addFile("empty").setId(bFileId);
- subB.setId(inserter.insert(OBJ_TREE, subB.format()));
- }
+ // Create sub-a/empty, sub-b/empty, sub-c/empty.
{
- final Tree subC = root.addTree("sub-c");
- subC.addFile("empty").setId(cFileId2);
- subC.setId(inserter.insert(OBJ_TREE, subC.format()));
+ TreeFormatter root = new TreeFormatter();
+ {
+ TreeFormatter subA = new TreeFormatter();
+ subA.append("empty", FileMode.REGULAR_FILE, aFileId);
+ root.append("sub-a", FileMode.TREE, inserter.insert(subA));
+ }
+ {
+ TreeFormatter subB = new TreeFormatter();
+ subB.append("empty", FileMode.REGULAR_FILE, bFileId);
+ root.append("sub-b", FileMode.TREE, inserter.insert(subB));
+ }
+ {
+ TreeFormatter subC = new TreeFormatter();
+ subC.append("empty", FileMode.REGULAR_FILE, cFileId2);
+ root.append("sub-c", FileMode.TREE, inserter.insert(subC));
+ }
+ newTree = inserter.insert(root);
}
- newTree = inserter.insert(OBJ_TREE, root.format());
+ inserter.flush();
}
- inserter.flush();
- inserter.release();
- final TreeWalk tw = new TreeWalk(db);
- tw.reset(oldTree, newTree);
- tw.setRecursive(true);
- tw.setFilter(TreeFilter.ANY_DIFF);
+ try (TreeWalk tw = new TreeWalk(db)) {
+ tw.reset(oldTree, newTree);
+ tw.setRecursive(true);
+ tw.setFilter(TreeFilter.ANY_DIFF);
- assertTrue(tw.next());
- assertEquals("sub-b/empty", tw.getPathString());
- assertEquals(FileMode.MISSING, tw.getFileMode(0));
- assertEquals(FileMode.REGULAR_FILE, tw.getFileMode(1));
- assertEquals(ObjectId.zeroId(), tw.getObjectId(0));
- assertEquals(bFileId, tw.getObjectId(1));
+ assertTrue(tw.next());
+ assertEquals("sub-b/empty", tw.getPathString());
+ assertEquals(FileMode.MISSING, tw.getFileMode(0));
+ assertEquals(FileMode.REGULAR_FILE, tw.getFileMode(1));
+ assertEquals(ObjectId.zeroId(), tw.getObjectId(0));
+ assertEquals(bFileId, tw.getObjectId(1));
- assertTrue(tw.next());
- assertEquals("sub-c/empty", tw.getPathString());
- assertEquals(FileMode.REGULAR_FILE, tw.getFileMode(0));
- assertEquals(FileMode.REGULAR_FILE, tw.getFileMode(1));
- assertEquals(cFileId1, tw.getObjectId(0));
- assertEquals(cFileId2, tw.getObjectId(1));
+ assertTrue(tw.next());
+ assertEquals("sub-c/empty", tw.getPathString());
+ assertEquals(FileMode.REGULAR_FILE, tw.getFileMode(0));
+ assertEquals(FileMode.REGULAR_FILE, tw.getFileMode(1));
+ assertEquals(cFileId1, tw.getObjectId(0));
+ assertEquals(cFileId2, tw.getObjectId(1));
- assertFalse(tw.next());
+ assertFalse(tw.next());
+ }
}
}
diff --git a/org.eclipse.jgit.java7.test/src/org/eclipse/jgit/treewalk/TreeWalkJava7Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/TreeWalkJava7Test.java
similarity index 97%
rename from org.eclipse.jgit.java7.test/src/org/eclipse/jgit/treewalk/TreeWalkJava7Test.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/TreeWalkJava7Test.java
index bb1f2a6..1328b38 100644
--- a/org.eclipse.jgit.java7.test/src/org/eclipse/jgit/treewalk/TreeWalkJava7Test.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/TreeWalkJava7Test.java
@@ -55,6 +55,7 @@ import org.junit.Test;
public class TreeWalkJava7Test extends RepositoryTestCase {
@Test
public void testSymlinkToDirNotRecursingViaSymlink() throws Exception {
+ org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
FS fs = db.getFS();
assertTrue(fs.supportsSymlinks());
writeTrashFile("target/data", "targetdata");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java
index d0062e1..5edc192 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java
@@ -43,11 +43,18 @@
package org.eclipse.jgit.treewalk.filter;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheEditor;
@@ -58,6 +65,7 @@ import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.StopWalkException;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Sets;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.junit.Before;
import org.junit.Test;
@@ -66,6 +74,8 @@ public class PathFilterGroupTest {
private TreeFilter filter;
+ private Map<String, TreeFilter> singles;
+
@Before
public void setup() {
// @formatter:off
@@ -81,64 +91,75 @@ public class PathFilterGroupTest {
};
// @formatter:on
filter = PathFilterGroup.createFromStrings(paths);
+ singles = new HashMap<>();
+ for (String path : paths) {
+ singles.put(path, PathFilterGroup.createFromStrings(path));
+ }
}
@Test
public void testExact() throws MissingObjectException,
IncorrectObjectTypeException, IOException {
- assertTrue(filter.include(fakeWalk("a")));
- assertTrue(filter.include(fakeWalk("b/c")));
- assertTrue(filter.include(fakeWalk("c/d/e")));
- assertTrue(filter.include(fakeWalk("c/d/f")));
- assertTrue(filter.include(fakeWalk("d/e/f/g")));
- assertTrue(filter.include(fakeWalk("d/e/f/g.x")));
+ assertMatches(Sets.of("a"), fakeWalk("a"));
+ assertMatches(Sets.of("b/c"), fakeWalk("b/c"));
+ assertMatches(Sets.of("c/d/e"), fakeWalk("c/d/e"));
+ assertMatches(Sets.of("c/d/f"), fakeWalk("c/d/f"));
+ assertMatches(Sets.of("d/e/f/g"), fakeWalk("d/e/f/g"));
+ assertMatches(Sets.of("d/e/f/g.x"), fakeWalk("d/e/f/g.x"));
}
@Test
public void testNoMatchButClose() throws MissingObjectException,
IncorrectObjectTypeException, IOException {
- assertFalse(filter.include(fakeWalk("a+")));
- assertFalse(filter.include(fakeWalk("b+/c")));
- assertFalse(filter.include(fakeWalk("c+/d/e")));
- assertFalse(filter.include(fakeWalk("c+/d/f")));
- assertFalse(filter.include(fakeWalk("c/d.a")));
- assertFalse(filter.include(fakeWalk("d+/e/f/g")));
+ assertNoMatches(fakeWalk("a+"));
+ assertNoMatches(fakeWalk("b+/c"));
+ assertNoMatches(fakeWalk("c+/d/e"));
+ assertNoMatches(fakeWalk("c+/d/f"));
+ assertNoMatches(fakeWalk("c/d.a"));
+ assertNoMatches(fakeWalk("d+/e/f/g"));
}
@Test
public void testJustCommonPrefixIsNotMatch() throws MissingObjectException,
IncorrectObjectTypeException, IOException {
- assertFalse(filter.include(fakeWalk("b/a")));
- assertFalse(filter.include(fakeWalk("b/d")));
- assertFalse(filter.include(fakeWalk("c/d/a")));
- assertFalse(filter.include(fakeWalk("d/e/e")));
+ assertNoMatches(fakeWalk("b/a"));
+ assertNoMatches(fakeWalk("b/d"));
+ assertNoMatches(fakeWalk("c/d/a"));
+ assertNoMatches(fakeWalk("d/e/e"));
+ assertNoMatches(fakeWalk("d/e/f/g.y"));
}
@Test
public void testKeyIsPrefixOfFilter() throws MissingObjectException,
IncorrectObjectTypeException, IOException {
- assertTrue(filter.include(fakeWalk("b")));
- assertTrue(filter.include(fakeWalk("c/d")));
- assertTrue(filter.include(fakeWalk("c/d")));
- assertTrue(filter.include(fakeWalk("c")));
- assertTrue(filter.include(fakeWalk("d/e/f")));
- assertTrue(filter.include(fakeWalk("d/e")));
- assertTrue(filter.include(fakeWalk("d")));
+ assertMatches(Sets.of("b/c"), fakeWalkAtSubtree("b"));
+ assertMatches(Sets.of("c/d/e", "c/d/f"), fakeWalkAtSubtree("c/d"));
+ assertMatches(Sets.of("c/d/e", "c/d/f"), fakeWalkAtSubtree("c"));
+ assertMatches(Sets.of("d/e/f/g", "d/e/f/g.x"),
+ fakeWalkAtSubtree("d/e/f"));
+ assertMatches(Sets.of("d/e/f/g", "d/e/f/g.x"),
+ fakeWalkAtSubtree("d/e"));
+ assertMatches(Sets.of("d/e/f/g", "d/e/f/g.x"), fakeWalkAtSubtree("d"));
+
+ assertNoMatches(fakeWalk("b"));
+ assertNoMatches(fakeWalk("c/d"));
+ assertNoMatches(fakeWalk("c"));
+ assertNoMatches(fakeWalk("d/e/f"));
+ assertNoMatches(fakeWalk("d/e"));
+ assertNoMatches(fakeWalk("d"));
+
}
@Test
public void testFilterIsPrefixOfKey() throws MissingObjectException,
IncorrectObjectTypeException, IOException {
- assertTrue(filter.include(fakeWalk("a/b")));
- assertTrue(filter.include(fakeWalk("b/c/d")));
- assertTrue(filter.include(fakeWalk("c/d/e/f")));
- assertTrue(filter.include(fakeWalk("c/d/f/g")));
- assertTrue(filter.include(fakeWalk("d/e/f/g/h")));
- assertTrue(filter.include(fakeWalk("d/e/f/g/y")));
- assertTrue(filter.include(fakeWalk("d/e/f/g.x/h")));
- // listed before g/y, so can't StopWalk here, but it's not included
- // either
- assertFalse(filter.include(fakeWalk("d/e/f/g.y")));
+ assertMatches(Sets.of("a"), fakeWalk("a/b"));
+ assertMatches(Sets.of("b/c"), fakeWalk("b/c/d"));
+ assertMatches(Sets.of("c/d/e"), fakeWalk("c/d/e/f"));
+ assertMatches(Sets.of("c/d/f"), fakeWalk("c/d/f/g"));
+ assertMatches(Sets.of("d/e/f/g"), fakeWalk("d/e/f/g/h"));
+ assertMatches(Sets.of("d/e/f/g"), fakeWalk("d/e/f/g/y"));
+ assertMatches(Sets.of("d/e/f/g.x"), fakeWalk("d/e/f/g.x/h"));
}
@Test
@@ -182,6 +203,10 @@ public class PathFilterGroupTest {
// less obvious #2 due to git sorting order
filter.include(fakeWalk("d/e/f/g/h.txt"));
+ // listed before g/y, so can't StopWalk here
+ filter.include(fakeWalk("d/e/f/g.y"));
+ singles.get("d/e/f/g").include(fakeWalk("d/e/f/g.y"));
+
// non-ascii
try {
filter.include(fakeWalk("\u00C0"));
@@ -191,6 +216,44 @@ public class PathFilterGroupTest {
}
}
+ private void assertNoMatches(TreeWalk tw) throws MissingObjectException,
+ IncorrectObjectTypeException, IOException {
+ assertMatches(Sets.<String> of(), tw);
+ }
+
+ private void assertMatches(Set<String> expect, TreeWalk tw)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException {
+ List<String> actual = new ArrayList<>();
+ for (String path : singles.keySet()) {
+ if (includes(singles.get(path), tw)) {
+ actual.add(path);
+ }
+ }
+
+ String[] e = expect.toArray(new String[expect.size()]);
+ String[] a = actual.toArray(new String[actual.size()]);
+ Arrays.sort(e);
+ Arrays.sort(a);
+ assertArrayEquals(e, a);
+
+ if (expect.isEmpty()) {
+ assertFalse(includes(filter, tw));
+ } else {
+ assertTrue(includes(filter, tw));
+ }
+ }
+
+ private static boolean includes(TreeFilter f, TreeWalk tw)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException {
+ try {
+ return f.include(tw);
+ } catch (StopWalkException e) {
+ return false;
+ }
+ }
+
TreeWalk fakeWalk(final String path) throws IOException {
DirCache dc = DirCache.newInCore();
DirCacheEditor dce = dc.editor();
@@ -210,4 +273,25 @@ public class PathFilterGroupTest {
return ret;
}
+ TreeWalk fakeWalkAtSubtree(final String path) throws IOException {
+ DirCache dc = DirCache.newInCore();
+ DirCacheEditor dce = dc.editor();
+ dce.add(new DirCacheEditor.PathEdit(path + "/README") {
+ public void apply(DirCacheEntry ent) {
+ ent.setFileMode(FileMode.REGULAR_FILE);
+ }
+ });
+ dce.finish();
+
+ TreeWalk ret = new TreeWalk((ObjectReader) null);
+ ret.addTree(new DirCacheIterator(dc));
+ ret.next();
+ while (!path.equals(ret.getPathString())) {
+ if (ret.isSubtree()) {
+ ret.enterSubtree();
+ }
+ ret.next();
+ }
+ return ret;
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ChangeIdUtilTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ChangeIdUtilTest.java
index 7273cdb..aaeb79c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ChangeIdUtilTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/ChangeIdUtilTest.java
@@ -45,7 +45,6 @@ package org.eclipse.jgit.util;
import static org.junit.Assert.assertEquals;
-import java.io.IOException;
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.junit.MockSystemReader;
@@ -113,7 +112,7 @@ public class ChangeIdUtilTest {
}
@Test
- public void testId() throws IOException {
+ public void testId() {
String msg = "A\nMessage\n";
ObjectId id = ChangeIdUtil.computeChangeId(treeId, parentId, p, q, msg);
assertEquals("73f3751208ac92cbb76f9a26ac4a0d9d472e381b", ObjectId
diff --git a/org.eclipse.jgit.java7.test/src/org/eclipse/jgit/util/FSJava7Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSJava7Test.java
similarity index 78%
rename from org.eclipse.jgit.java7.test/src/org/eclipse/jgit/util/FSJava7Test.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSJava7Test.java
index 91555f3..53b6fec 100644
--- a/org.eclipse.jgit.java7.test/src/org/eclipse/jgit/util/FSJava7Test.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSJava7Test.java
@@ -46,15 +46,10 @@ package org.eclipse.jgit.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeNotNull;
import static org.junit.Assume.assumeTrue;
-import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
-import java.io.InputStreamReader;
-import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFilePermission;
@@ -62,6 +57,7 @@ import java.util.Set;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.junit.After;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
@@ -92,6 +88,7 @@ public class FSJava7Test {
*/
@Test
public void testSymlinkAttributes() throws IOException, InterruptedException {
+ Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
FS fs = FS.DETECTED;
File link = new File(trash, "ä");
File target = new File(trash, "å");
@@ -117,34 +114,21 @@ public class FSJava7Test {
assertFalse(fs.canExecute(link));
fs.setExecute(target, true);
assertFalse(fs.canExecute(link));
+ assumeTrue(fs.supportsExecute());
assertTrue(fs.canExecute(target));
}
@Test
public void testExecutableAttributes() throws Exception {
- FS fs = FS.DETECTED;
+ FS fs = FS.DETECTED.newInstance();
// If this assumption fails the test is halted and ignored.
- assumeTrue(fs instanceof FS_POSIX_Java7);
+ assumeTrue(fs instanceof FS_POSIX);
+ ((FS_POSIX) fs).setUmask(0022);
File f = new File(trash, "bla");
assertTrue(f.createNewFile());
assertFalse(fs.canExecute(f));
- String umask = readUmask();
- assumeNotNull(umask);
-
- char others = umask.charAt(umask.length() - 1);
-
- boolean badUmask;
- if (others != '0' && others != '2' && others != '4' && others != '6') {
- // umask is set in the way that "others" can not "execute" => git
- // CLI will not set "execute" attribute for "others", so we also
- // don't care
- badUmask = true;
- } else {
- badUmask = false;
- }
-
Set<PosixFilePermission> permissions = readPermissions(f);
assertTrue(!permissions.contains(PosixFilePermission.OTHERS_EXECUTE));
assertTrue(!permissions.contains(PosixFilePermission.GROUP_EXECUTE));
@@ -157,27 +141,21 @@ public class FSJava7Test {
permissions.contains(PosixFilePermission.OWNER_EXECUTE));
assertTrue("'group' execute permission not set",
permissions.contains(PosixFilePermission.GROUP_EXECUTE));
- if (badUmask) {
- assertFalse("'others' execute permission set",
- permissions.contains(PosixFilePermission.OTHERS_EXECUTE));
- System.err.println("WARNING: your system's umask: \"" + umask
- + "\" doesn't allow FSJava7Test to test if setting posix"
- + " permissions for \"others\" works properly");
- assumeFalse(badUmask);
- } else {
- assertTrue("'others' execute permission not set",
- permissions.contains(PosixFilePermission.OTHERS_EXECUTE));
- }
- }
+ assertTrue("'others' execute permission not set",
+ permissions.contains(PosixFilePermission.OTHERS_EXECUTE));
+
+ ((FS_POSIX) fs).setUmask(0033);
+ fs.setExecute(f, false);
+ assertFalse(fs.canExecute(f));
+ fs.setExecute(f, true);
- private String readUmask() throws Exception {
- Process p = Runtime.getRuntime().exec(
- new String[] { "sh", "-c", "umask" }, null, null);
- final BufferedReader lineRead = new BufferedReader(
- new InputStreamReader(p.getInputStream(), Charset
- .defaultCharset().name()));
- p.waitFor();
- return lineRead.readLine();
+ permissions = readPermissions(f);
+ assertTrue("'owner' execute permission not set",
+ permissions.contains(PosixFilePermission.OWNER_EXECUTE));
+ assertFalse("'group' execute permission set",
+ permissions.contains(PosixFilePermission.GROUP_EXECUTE));
+ assertFalse("'others' execute permission set",
+ permissions.contains(PosixFilePermission.OTHERS_EXECUTE));
}
private Set<PosixFilePermission> readPermissions(File f) throws IOException {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilTest.java
index 0d7d31b..1f78e02 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtilTest.java
@@ -54,6 +54,7 @@ import java.util.regex.Matcher;
import org.eclipse.jgit.junit.JGitTestUtil;
import org.junit.After;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
@@ -424,19 +425,28 @@ public class FileUtilTest {
@Test
public void testCreateSymlink() throws IOException {
FS fs = FS.DETECTED;
- try {
- fs.createSymLink(new File(trash, "x"), "y");
- } catch (IOException e) {
- if (fs.supportsSymlinks())
- fail("FS claims to support symlinks but attempt to create symlink failed");
- return;
- }
- assertTrue(fs.supportsSymlinks());
+ // show test as ignored if the FS doesn't support symlinks
+ Assume.assumeTrue(fs.supportsSymlinks());
+ fs.createSymLink(new File(trash, "x"), "y");
String target = fs.readSymLink(new File(trash, "x"));
assertEquals("y", target);
}
@Test
+ public void testCreateSymlinkOverrideExisting() throws IOException {
+ FS fs = FS.DETECTED;
+ // show test as ignored if the FS doesn't support symlinks
+ Assume.assumeTrue(fs.supportsSymlinks());
+ File file = new File(trash, "x");
+ fs.createSymLink(file, "y");
+ String target = fs.readSymLink(file);
+ assertEquals("y", target);
+ fs.createSymLink(file, "z");
+ target = fs.readSymLink(file);
+ assertEquals("z", target);
+ }
+
+ @Test
public void testRelativize_doc() {
// This is the javadoc example
String base = toOSPathString("c:\\Users\\jdoe\\eclipse\\git\\project");
diff --git a/org.eclipse.jgit.java7.test/src/org/eclipse/jgit/util/FileUtils7Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtils7Test.java
similarity index 87%
rename from org.eclipse.jgit.java7.test/src/org/eclipse/jgit/util/FileUtils7Test.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtils7Test.java
index 9dc5fac..cc1fdc2 100644
--- a/org.eclipse.jgit.java7.test/src/org/eclipse/jgit/util/FileUtils7Test.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtils7Test.java
@@ -48,6 +48,8 @@ import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
import org.junit.After;
import org.junit.Before;
@@ -71,6 +73,7 @@ public class FileUtils7Test {
@Test
public void testDeleteSymlinkToDirectoryDoesNotDeleteTarget()
throws IOException {
+ org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
FS fs = FS.DETECTED;
File dir = new File(trash, "dir");
File file = new File(dir, "file");
@@ -83,4 +86,14 @@ public class FileUtils7Test {
assertTrue(dir.exists());
assertTrue(file.exists());
}
+
+ @Test
+ public void testAtomicMove() throws IOException {
+ File src = new File(trash, "src");
+ Files.createFile(src.toPath());
+ File dst = new File(trash, "dst");
+ FileUtils.rename(src, dst, StandardCopyOption.ATOMIC_MOVE);
+ assertFalse(Files.exists(src.toPath()));
+ assertTrue(Files.exists(dst.toPath()));
+ }
}
diff --git a/org.eclipse.jgit.java7.test/src/org/eclipse/jgit/util/HookTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java
similarity index 60%
rename from org.eclipse.jgit.java7.test/src/org/eclipse/jgit/util/HookTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java
index 9655088..e07076e 100644
--- a/org.eclipse.jgit.java7.test/src/org/eclipse/jgit/util/HookTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java
@@ -52,9 +52,12 @@ import java.io.IOException;
import java.io.PrintStream;
import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.api.errors.RejectCommitException;
+import org.eclipse.jgit.api.errors.AbortedByHookException;
+import org.eclipse.jgit.hooks.CommitMsgHook;
+import org.eclipse.jgit.hooks.PreCommitHook;
import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Assume;
import org.junit.Test;
@@ -64,27 +67,85 @@ public class HookTest extends RepositoryTestCase {
public void testFindHook() throws Exception {
assumeSupportedPlatform();
- Hook h = Hook.PRE_COMMIT;
- assertNull("no hook should be installed", FS.DETECTED.findHook(db, h));
- File hookFile = writeHookFile(h.getName(),
+ assertNull("no hook should be installed",
+ FS.DETECTED.findHook(db, PreCommitHook.NAME));
+ File hookFile = writeHookFile(PreCommitHook.NAME,
"#!/bin/bash\necho \"test $1 $2\"");
- assertEquals("exected to find pre-commit hook", hookFile,
- FS.DETECTED.findHook(db, h));
+ assertEquals("expected to find pre-commit hook", hookFile,
+ FS.DETECTED.findHook(db, PreCommitHook.NAME));
+ }
+
+ @Test
+ public void testFailedCommitMsgHookBlocksCommit() throws Exception {
+ assumeSupportedPlatform();
+
+ writeHookFile(CommitMsgHook.NAME,
+ "#!/bin/sh\necho \"test\"\n\necho 1>&2 \"stderr\"\nexit 1");
+ Git git = Git.wrap(db);
+ String path = "a.txt";
+ writeTrashFile(path, "content");
+ git.add().addFilepattern(path).call();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try {
+ git.commit().setMessage("commit")
+ .setHookOutputStream(new PrintStream(out)).call();
+ fail("expected commit-msg hook to abort commit");
+ } catch (AbortedByHookException e) {
+ assertEquals("unexpected error message from commit-msg hook",
+ "Rejected by \"commit-msg\" hook.\nstderr\n",
+ e.getMessage());
+ assertEquals("unexpected output from commit-msg hook", "test\n",
+ out.toString());
+ }
+ }
+
+ @Test
+ public void testCommitMsgHookReceivesCorrectParameter() throws Exception {
+ assumeSupportedPlatform();
+
+ writeHookFile(CommitMsgHook.NAME,
+ "#!/bin/sh\necho $1\n\necho 1>&2 \"stderr\"\nexit 0");
+ Git git = Git.wrap(db);
+ String path = "a.txt";
+ writeTrashFile(path, "content");
+ git.add().addFilepattern(path).call();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ git.commit().setMessage("commit")
+ .setHookOutputStream(new PrintStream(out)).call();
+ assertEquals(".git/COMMIT_EDITMSG\n",
+ out.toString("UTF-8"));
+ }
+
+ @Test
+ public void testCommitMsgHookCanModifyCommitMessage() throws Exception {
+ assumeSupportedPlatform();
+
+ writeHookFile(CommitMsgHook.NAME,
+ "#!/bin/sh\necho \"new message\" > $1\nexit 0");
+ Git git = Git.wrap(db);
+ String path = "a.txt";
+ writeTrashFile(path, "content");
+ git.add().addFilepattern(path).call();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ RevCommit revCommit = git.commit().setMessage("commit")
+ .setHookOutputStream(new PrintStream(out)).call();
+ assertEquals("new message\n", revCommit.getFullMessage());
}
@Test
public void testRunHook() throws Exception {
assumeSupportedPlatform();
- Hook h = Hook.PRE_COMMIT;
- writeHookFile(
- h.getName(),
+ writeHookFile(PreCommitHook.NAME,
"#!/bin/sh\necho \"test $1 $2\"\nread INPUT\necho $INPUT\necho 1>&2 \"stderr\"");
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayOutputStream err = new ByteArrayOutputStream();
- ProcessResult res = FS.DETECTED.runIfPresent(db, h, new String[] {
+ ProcessResult res = FS.DETECTED.runHookIfPresent(db,
+ PreCommitHook.NAME,
+ new String[] {
"arg1", "arg2" },
new PrintStream(out), new PrintStream(err), "stdin");
+
assertEquals("unexpected hook output", "test arg1 arg2\nstdin\n",
out.toString("UTF-8"));
assertEquals("unexpected output on stderr stream", "stderr\n",
@@ -95,11 +156,10 @@ public class HookTest extends RepositoryTestCase {
}
@Test
- public void testPreCommitHook() throws Exception {
+ public void testFailedPreCommitHookBlockCommit() throws Exception {
assumeSupportedPlatform();
- Hook h = Hook.PRE_COMMIT;
- writeHookFile(h.getName(),
+ writeHookFile(PreCommitHook.NAME,
"#!/bin/sh\necho \"test\"\n\necho 1>&2 \"stderr\"\nexit 1");
Git git = Git.wrap(db);
String path = "a.txt";
@@ -110,14 +170,12 @@ public class HookTest extends RepositoryTestCase {
git.commit().setMessage("commit")
.setHookOutputStream(new PrintStream(out)).call();
fail("expected pre-commit hook to abort commit");
- } catch (RejectCommitException e) {
+ } catch (AbortedByHookException e) {
assertEquals("unexpected error message from pre-commit hook",
- "Commit rejected by \"pre-commit\" hook.\nstderr\n",
+ "Rejected by \"pre-commit\" hook.\nstderr\n",
e.getMessage());
assertEquals("unexpected output from pre-commit hook", "test\n",
out.toString());
- } catch (Throwable e) {
- fail("unexpected exception thrown by pre-commit hook: " + e);
}
}
@@ -131,6 +189,6 @@ public class HookTest extends RepositoryTestCase {
private void assumeSupportedPlatform() {
Assume.assumeTrue(FS.DETECTED instanceof FS_POSIX
- || FS.DETECTED instanceof FS_Win32_Java7Cygwin);
+ || FS.DETECTED instanceof FS_Win32_Cygwin);
}
}
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/BranchTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/IOReadLineTest.java
similarity index 53%
copy from org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/BranchTest.java
copy to org.eclipse.jgit.test/tst/org/eclipse/jgit/util/IOReadLineTest.java
index 4200cd0..928fb2e 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/BranchTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/IOReadLineTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012, 2014 IBM Corporation and others.
+ * Copyright (C) 2015, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -40,55 +40,77 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.pgm;
+
+package org.eclipse.jgit.util;
import static org.junit.Assert.assertEquals;
-import org.eclipse.jgit.api.Git;
-import org.eclipse.jgit.lib.CLIRepositoryTestCase;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Collection;
+
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+ at RunWith(Parameterized.class)
+public class IOReadLineTest {
+ @Parameter(0)
+ public boolean buffered;
-public class BranchTest extends CLIRepositoryTestCase {
- @Override
- @Before
- public void setUp() throws Exception {
- super.setUp();
- new Git(db).commit().setMessage("initial commit").call();
+ @Parameter(1)
+ public int sizeHint;
+
+ @SuppressWarnings("boxing")
+ @Parameters(name="buffered={0}, sizeHint={1}")
+ public static Collection<Object[]> getParameters() {
+ Boolean[] bv = {false, true};
+ Integer[] sv = {-1, 0, 1, 2, 3, 4, 64};
+ Collection<Object[]> params = new ArrayList<>(bv.length * sv.length);
+ for (boolean b : bv) {
+ for (Integer s : sv) {
+ params.add(new Object[]{b, s});
+ }
+ }
+ return params;
}
@Test
- public void testList() throws Exception {
- assertEquals("* master 6fd41be initial commit",
- execute("git branch -v")[0]);
+ public void testReadLine() throws Exception {
+ Reader r = newReader("foo\nbar\nbaz\n");
+ assertEquals("foo\n", readLine(r));
+ assertEquals("bar\n", readLine(r));
+ assertEquals("baz\n", readLine(r));
+ assertEquals("", readLine(r));
}
@Test
- public void testListDetached() throws Exception {
- RefUpdate updateRef = db.updateRef(Constants.HEAD, true);
- updateRef.setNewObjectId(db.resolve("6fd41be"));
- updateRef.update();
- assertEquals("* (no branch) 6fd41be initial commit",
- execute("git branch -v")[0]);
+ public void testReadLineNoTrailingNewline() throws Exception {
+ Reader r = newReader("foo\nbar\nbaz");
+ assertEquals("foo\n", readLine(r));
+ assertEquals("bar\n", readLine(r));
+ assertEquals("baz", readLine(r));
+ assertEquals("", readLine(r));
}
- @Test
- public void testListContains() throws Exception {
- new Git(db).branchCreate().setName("initial").call();
- RevCommit second = new Git(db).commit().setMessage("second commit")
- .call();
- assertArrayOfLinesEquals(new String[] { " initial", "* master", "" },
- execute("git branch --contains 6fd41be"));
- assertArrayOfLinesEquals(new String[] { "* master", "" },
- execute("git branch --contains " + second.name()));
+ private String readLine(Reader r) throws Exception {
+ return IO.readLine(r, sizeHint);
}
- @Test
- public void testExistingBranch() throws Exception {
- assertEquals("fatal: A branch named 'master' already exists.",
- execute("git branch master")[0]);
+ private Reader newReader(String in) {
+ Reader r = new InputStreamReader(
+ new ByteArrayInputStream(Constants.encode(in)));
+ if (buffered) {
+ r = new BufferedReader(r);
+ }
+ assertEquals(Boolean.valueOf(buffered),
+ Boolean.valueOf(r.markSupported()));
+ return r;
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/PathsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/PathsTest.java
new file mode 100644
index 0000000..7542ec8
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/PathsTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.util;
+
+import static org.eclipse.jgit.util.Paths.compare;
+import static org.eclipse.jgit.util.Paths.compareSameName;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.FileMode;
+import org.junit.Test;
+
+public class PathsTest {
+ @Test
+ public void testStripTrailingSeparator() {
+ assertNull(Paths.stripTrailingSeparator(null));
+ assertEquals("", Paths.stripTrailingSeparator(""));
+ assertEquals("a", Paths.stripTrailingSeparator("a"));
+ assertEquals("a/boo", Paths.stripTrailingSeparator("a/boo"));
+ assertEquals("a/boo", Paths.stripTrailingSeparator("a/boo/"));
+ assertEquals("a/boo", Paths.stripTrailingSeparator("a/boo//"));
+ assertEquals("a/boo", Paths.stripTrailingSeparator("a/boo///"));
+ }
+
+ @Test
+ public void testPathCompare() {
+ byte[] a = Constants.encode("afoo/bar.c");
+ byte[] b = Constants.encode("bfoo/bar.c");
+
+ assertEquals(0, compare(a, 1, a.length, 0, b, 1, b.length, 0));
+ assertEquals(-1, compare(a, 0, a.length, 0, b, 0, b.length, 0));
+ assertEquals(1, compare(b, 0, b.length, 0, a, 0, a.length, 0));
+
+ a = Constants.encode("a");
+ b = Constants.encode("aa");
+ assertEquals(-97, compare(a, 0, a.length, 0, b, 0, b.length, 0));
+ assertEquals(0, compare(a, 0, a.length, 0, b, 0, 1, 0));
+ assertEquals(0, compare(a, 0, a.length, 0, b, 1, 2, 0));
+ assertEquals(0, compareSameName(a, 0, a.length, b, 1, b.length, 0));
+ assertEquals(0, compareSameName(a, 0, a.length, b, 0, 1, 0));
+ assertEquals(-50, compareSameName(a, 0, a.length, b, 0, b.length, 0));
+ assertEquals(97, compareSameName(b, 0, b.length, a, 0, a.length, 0));
+
+ a = Constants.encode("a");
+ b = Constants.encode("a");
+ assertEquals(0, compare(
+ a, 0, a.length, FileMode.TREE.getBits(),
+ b, 0, b.length, FileMode.TREE.getBits()));
+ assertEquals(0, compare(
+ a, 0, a.length, FileMode.REGULAR_FILE.getBits(),
+ b, 0, b.length, FileMode.REGULAR_FILE.getBits()));
+ assertEquals(-47, compare(
+ a, 0, a.length, FileMode.REGULAR_FILE.getBits(),
+ b, 0, b.length, FileMode.TREE.getBits()));
+ assertEquals(47, compare(
+ a, 0, a.length, FileMode.TREE.getBits(),
+ b, 0, b.length, FileMode.REGULAR_FILE.getBits()));
+
+ assertEquals(0, compareSameName(
+ a, 0, a.length,
+ b, 0, b.length, FileMode.TREE.getBits()));
+ assertEquals(0, compareSameName(
+ a, 0, a.length,
+ b, 0, b.length, FileMode.REGULAR_FILE.getBits()));
+
+ a = Constants.encode("a.c");
+ b = Constants.encode("a");
+ byte[] c = Constants.encode("a0c");
+ assertEquals(-1, compare(
+ a, 0, a.length, FileMode.REGULAR_FILE.getBits(),
+ b, 0, b.length, FileMode.TREE.getBits()));
+ assertEquals(-1, compare(
+ b, 0, b.length, FileMode.TREE.getBits(),
+ c, 0, c.length, FileMode.REGULAR_FILE.getBits()));
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java
new file mode 100644
index 0000000..7c0985e
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2015, Christian Halstrick <christian.halstrick at sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.util;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.util.FS.ExecutionResult;
+import org.junit.Before;
+import org.junit.Test;
+
+public class RunExternalScriptTest {
+ private static final String LF = "\n";
+
+ private ByteArrayOutputStream out;
+
+ private ByteArrayOutputStream err;
+
+ @Before
+ public void setUp() throws Exception {
+ out = new ByteArrayOutputStream();
+ err = new ByteArrayOutputStream();
+ }
+
+ @Test
+ public void testCopyStdIn() throws IOException, InterruptedException {
+ String inputStr = "a\nb\rc\r\nd";
+ File script = writeTempFile("cat -");
+ int rc = FS.DETECTED.runProcess(
+ new ProcessBuilder("sh", script.getPath()), out, err,
+ new ByteArrayInputStream(inputStr.getBytes()));
+ assertEquals(0, rc);
+ assertEquals(inputStr, new String(out.toByteArray()));
+ assertEquals("", new String(err.toByteArray()));
+ }
+
+ @Test
+ public void testCopyNullStdIn() throws IOException, InterruptedException {
+ File script = writeTempFile("cat -");
+ int rc = FS.DETECTED.runProcess(
+ new ProcessBuilder("sh", script.getPath()), out, err,
+ (InputStream) null);
+ assertEquals(0, rc);
+ assertEquals("", new String(out.toByteArray()));
+ assertEquals("", new String(err.toByteArray()));
+ }
+
+ @Test
+ public void testArguments() throws IOException, InterruptedException {
+ File script = writeTempFile("echo $#,$1,$2,$3,$4,$5,$6");
+ int rc = FS.DETECTED.runProcess(
+ new ProcessBuilder("sh",
+ script.getPath(), "a", "b", "c"), out, err, (InputStream) null);
+ assertEquals(0, rc);
+ assertEquals("3,a,b,c,,,\n", new String(out.toByteArray()));
+ assertEquals("", new String(err.toByteArray()));
+ }
+
+ @Test
+ public void testRc() throws IOException, InterruptedException {
+ File script = writeTempFile("exit 3");
+ int rc = FS.DETECTED.runProcess(
+ new ProcessBuilder("sh", script.getPath(), "a", "b", "c"),
+ out, err, (InputStream) null);
+ assertEquals(3, rc);
+ assertEquals("", new String(out.toByteArray()));
+ assertEquals("", new String(err.toByteArray()));
+ }
+
+ @Test
+ public void testNullStdout() throws IOException, InterruptedException {
+ File script = writeTempFile("echo hi");
+ int rc = FS.DETECTED.runProcess(
+ new ProcessBuilder("sh", script.getPath()), null, err,
+ (InputStream) null);
+ assertEquals(0, rc);
+ assertEquals("", new String(out.toByteArray()));
+ assertEquals("", new String(err.toByteArray()));
+ }
+
+ @Test
+ public void testStdErr() throws IOException, InterruptedException {
+ File script = writeTempFile("echo hi >&2");
+ int rc = FS.DETECTED.runProcess(
+ new ProcessBuilder("sh", script.getPath()), null, err,
+ (InputStream) null);
+ assertEquals(0, rc);
+ assertEquals("", new String(out.toByteArray()));
+ assertEquals("hi" + LF, new String(err.toByteArray()));
+ }
+
+ @Test
+ public void testAllTogetherBin() throws IOException, InterruptedException {
+ String inputStr = "a\nb\rc\r\nd";
+ File script = writeTempFile("echo $#,$1,$2,$3,$4,$5,$6 >&2 ; cat -; exit 5");
+ int rc = FS.DETECTED.runProcess(
+ new ProcessBuilder("sh", script.getPath(), "a", "b", "c"),
+ out, err, new ByteArrayInputStream(inputStr.getBytes()));
+ assertEquals(5, rc);
+ assertEquals(inputStr, new String(out.toByteArray()));
+ assertEquals("3,a,b,c,,," + LF, new String(err.toByteArray()));
+ }
+
+ @Test(expected = IOException.class)
+ public void testWrongSh() throws IOException, InterruptedException {
+ File script = writeTempFile("cat -");
+ FS.DETECTED.runProcess(
+ new ProcessBuilder("/bin/sh-foo", script.getPath(), "a", "b",
+ "c"), out, err, (InputStream) null);
+ }
+
+ @Test
+ public void testWrongScript() throws IOException, InterruptedException {
+ File script = writeTempFile("cat-foo -");
+ int rc = FS.DETECTED.runProcess(
+ new ProcessBuilder("sh", script.getPath(), "a", "b", "c"),
+ out, err, (InputStream) null);
+ assertEquals(127, rc);
+ }
+
+ @Test
+ public void testCopyStdInExecute()
+ throws IOException, InterruptedException {
+ String inputStr = "a\nb\rc\r\nd";
+ File script = writeTempFile("cat -");
+ ProcessBuilder pb = new ProcessBuilder("sh", script.getPath());
+ ExecutionResult res = FS.DETECTED.execute(pb,
+ new ByteArrayInputStream(inputStr.getBytes()));
+ assertEquals(0, res.getRc());
+ assertEquals(inputStr, new String(res.getStdout().toByteArray()));
+ assertEquals("", new String(res.getStderr().toByteArray()));
+ }
+
+ @Test
+ public void testStdErrExecute() throws IOException, InterruptedException {
+ File script = writeTempFile("echo hi >&2");
+ ProcessBuilder pb = new ProcessBuilder("sh", script.getPath());
+ ExecutionResult res = FS.DETECTED.execute(pb, null);
+ assertEquals(0, res.getRc());
+ assertEquals("", new String(res.getStdout().toByteArray()));
+ assertEquals("hi" + LF, new String(res.getStderr().toByteArray()));
+ }
+
+ @Test
+ public void testAllTogetherBinExecute()
+ throws IOException, InterruptedException {
+ String inputStr = "a\nb\rc\r\nd";
+ File script = writeTempFile(
+ "echo $#,$1,$2,$3,$4,$5,$6 >&2 ; cat -; exit 5");
+ ProcessBuilder pb = new ProcessBuilder("sh", script.getPath(), "a",
+ "b", "c");
+ ExecutionResult res = FS.DETECTED.execute(pb,
+ new ByteArrayInputStream(inputStr.getBytes()));
+ assertEquals(5, res.getRc());
+ assertEquals(inputStr, new String(res.getStdout().toByteArray()));
+ assertEquals("3,a,b,c,,," + LF,
+ new String(res.getStderr().toByteArray()));
+ }
+
+ private File writeTempFile(String body) throws IOException {
+ File f = File.createTempFile("RunProcessTestScript_", "");
+ JGitTestUtil.write(f, body);
+ return f;
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/TemporaryBufferTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/TemporaryBufferTest.java
index 9817cdc..d978804 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/TemporaryBufferTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/TemporaryBufferTest.java
@@ -53,7 +53,9 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+
import org.eclipse.jgit.junit.TestRng;
+import org.eclipse.jgit.util.TemporaryBuffer.Block;
import org.junit.Test;
public class TemporaryBufferTest {
@@ -424,4 +426,27 @@ public class TemporaryBufferTest {
assertEquals("In-memory buffer limit exceeded", e.getMessage());
}
}
+
+ @Test
+ public void testHeapWithEstimatedSize() throws IOException {
+ int sz = 2 * Block.SZ;
+ try (TemporaryBuffer b = new TemporaryBuffer.Heap(sz / 2, sz)) {
+ for (int i = 0; i < sz; i++) {
+ b.write('x');
+ }
+ try {
+ b.write(1);
+ fail("accepted too many bytes of data");
+ } catch (IOException e) {
+ assertEquals("In-memory buffer limit exceeded", e.getMessage());
+ }
+
+ try (InputStream in = b.openInputStream()) {
+ for (int i = 0; i < sz; i++) {
+ assertEquals('x', in.read());
+ }
+ assertEquals(-1, in.read());
+ }
+ }
+ }
}
diff --git a/org.eclipse.jgit.ui/.classpath b/org.eclipse.jgit.ui/.classpath
index b3d21cc..a14ade4 100644
--- a/org.eclipse.jgit.ui/.classpath
+++ b/org.eclipse.jgit.ui/.classpath
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="resources"/>
diff --git a/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs
index 12a5556..ff39d16 100644
--- a/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit.ui/.settings/org.eclipse.jdt.core.prefs
@@ -7,9 +7,9 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
@@ -111,7 +111,7 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
-org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.compiler.source=1.7
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
diff --git a/org.eclipse.jgit.ui/BUCK b/org.eclipse.jgit.ui/BUCK
new file mode 100644
index 0000000..fcd87cf
--- /dev/null
+++ b/org.eclipse.jgit.ui/BUCK
@@ -0,0 +1,7 @@
+java_library(
+ name = 'ui',
+ srcs = glob(['src/**']),
+ resources = glob(['resources/**']),
+ deps = ['//org.eclipse.jgit:jgit'],
+ visibility = ['PUBLIC'],
+)
diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
index 6272f85..54a6eab 100644
--- a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
@@ -3,14 +3,14 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Bundle-SymbolicName: org.eclipse.jgit.ui
-Bundle-Version: 3.7.1.201504261725-r
+Bundle-Version: 4.2.0.201601211800-r
Bundle-Vendor: %provider_name
-Bundle-RequiredExecutionEnvironment: J2SE-1.5
-Export-Package: org.eclipse.jgit.awtui;version="3.7.1"
-Import-Package: org.eclipse.jgit.errors;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.lib;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.nls;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.revplot;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.revwalk;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.transport;version="[3.7.1,3.8.0)",
- org.eclipse.jgit.util;version="[3.7.1,3.8.0)"
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
+Export-Package: org.eclipse.jgit.awtui;version="4.2.0"
+Import-Package: org.eclipse.jgit.errors;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.lib;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.nls;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.revplot;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.revwalk;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.transport;version="[4.2.0,4.3.0)",
+ org.eclipse.jgit.util;version="[4.2.0,4.3.0)"
diff --git a/org.eclipse.jgit.ui/pom.xml b/org.eclipse.jgit.ui/pom.xml
index 8e92a16..8a84697 100644
--- a/org.eclipse.jgit.ui/pom.xml
+++ b/org.eclipse.jgit.ui/pom.xml
@@ -52,7 +52,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>3.7.1.201504261725-r</version>
+ <version>4.2.0.201601211800-r</version>
</parent>
<artifactId>org.eclipse.jgit.ui</artifactId>
diff --git a/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/AwtCredentialsProvider.java b/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/AwtCredentialsProvider.java
index fd26bfa..a9967ae 100644
--- a/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/AwtCredentialsProvider.java
+++ b/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/AwtCredentialsProvider.java
@@ -56,15 +56,20 @@ import javax.swing.JPasswordField;
import javax.swing.JTextField;
import org.eclipse.jgit.errors.UnsupportedCredentialItem;
+import org.eclipse.jgit.transport.ChainingCredentialsProvider;
import org.eclipse.jgit.transport.CredentialItem;
import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.NetRCCredentialsProvider;
import org.eclipse.jgit.transport.URIish;
/** Interacts with the user during authentication by using AWT/Swing dialogs. */
public class AwtCredentialsProvider extends CredentialsProvider {
/** Install this implementation as the default. */
public static void install() {
- CredentialsProvider.setDefault(new AwtCredentialsProvider());
+ final AwtCredentialsProvider c = new AwtCredentialsProvider();
+ CredentialsProvider cp = new ChainingCredentialsProvider(
+ new NetRCCredentialsProvider(), c);
+ CredentialsProvider.setDefault(cp);
}
@Override
diff --git a/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/SwingCommitList.java b/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/SwingCommitList.java
index 4a11964..7359093 100644
--- a/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/SwingCommitList.java
+++ b/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/SwingCommitList.java
@@ -82,6 +82,7 @@ class SwingCommitList extends PlotCommitList<SwingCommitList.SwingLane> {
}
static class SwingLane extends PlotLane {
+ private static final long serialVersionUID = 1L;
Color color;
}
}
diff --git a/org.eclipse.jgit/.classpath b/org.eclipse.jgit/.classpath
index d7edf52..04a2be7 100644
--- a/org.eclipse.jgit/.classpath
+++ b/org.eclipse.jgit/.classpath
@@ -2,7 +2,7 @@
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="resources"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index 20449fe..36041f8 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -1,47 +1,115 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<component id="org.eclipse.jgit" version="2">
- <resource path="META-INF/MANIFEST.MF">
- <filter comment="minor addition" id="924844039">
+ <resource path="META-INF/MANIFEST.MF" type="org.eclipse.jgit.lib.FileTreeEntry">
+ <filter id="305324134">
<message_arguments>
- <message_argument value="3.4.0"/>
- <message_argument value="3.4.0"/>
+ <message_argument value="org.eclipse.jgit.lib.FileTreeEntry"/>
+ <message_argument value="org.eclipse.jgit_4.2.0"/>
</message_arguments>
</filter>
</resource>
- <resource path="src/org/eclipse/jgit/lib/ObjectInserter.java" type="org.eclipse.jgit.lib.ObjectInserter">
- <filter id="336695337">
+ <resource path="META-INF/MANIFEST.MF" type="org.eclipse.jgit.lib.GitlinkTreeEntry">
+ <filter id="305324134">
<message_arguments>
- <message_argument value="org.eclipse.jgit.lib.ObjectInserter"/>
- <message_argument value="newReader()"/>
+ <message_argument value="org.eclipse.jgit.lib.GitlinkTreeEntry"/>
+ <message_argument value="org.eclipse.jgit_4.2.0"/>
</message_arguments>
</filter>
</resource>
- <resource path="src/org/eclipse/jgit/merge/ResolveMerger.java" type="org.eclipse.jgit.merge.ResolveMerger">
- <filter comment="Doesn't break consumers. Breaking providers is allowed also in minor versions." id="338792546">
+ <resource path="META-INF/MANIFEST.MF" type="org.eclipse.jgit.lib.SymlinkTreeEntry">
+ <filter id="305324134">
<message_arguments>
- <message_argument value="org.eclipse.jgit.merge.ResolveMerger"/>
- <message_argument value="mergeTreeWalk(TreeWalk)"/>
+ <message_argument value="org.eclipse.jgit.lib.SymlinkTreeEntry"/>
+ <message_argument value="org.eclipse.jgit_4.2.0"/>
</message_arguments>
</filter>
- <filter comment="Doesn't break consumers. Breaking providers is allowed also in minor versions." id="338792546">
+ </resource>
+ <resource path="META-INF/MANIFEST.MF" type="org.eclipse.jgit.lib.Tree">
+ <filter id="305324134">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lib.Tree"/>
+ <message_argument value="org.eclipse.jgit_4.2.0"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="META-INF/MANIFEST.MF" type="org.eclipse.jgit.lib.TreeEntry">
+ <filter id="305324134">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lib.TreeEntry"/>
+ <message_argument value="org.eclipse.jgit_4.2.0"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/jgit/attributes/AttributesNode.java" type="org.eclipse.jgit.attributes.AttributesNode">
+ <filter comment="attributes weren't really usable in earlier versions" id="338792546">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.attributes.AttributesNode"/>
+ <message_argument value="getAttributes(String, boolean, Map<String,Attribute>)"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/jgit/lib/BitmapIndex.java" type="org.eclipse.jgit.lib.BitmapIndex$BitmapBuilder">
+ <filter comment="interface is implemented by extenders but not clients of the API" id="403804204">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder"/>
+ <message_argument value="addObject(AnyObjectId, int)"/>
+ </message_arguments>
+ </filter>
+ <filter comment="interface is implemented by extenders but not clients of the API" id="403804204">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder"/>
+ <message_argument value="getBitmapIndex()"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/jgit/lib/Repository.java" type="org.eclipse.jgit.lib.Repository">
+ <filter comment="Only implementors of Repository are affected. That should be allowed" id="336695337">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lib.Repository"/>
+ <message_argument value="createAttributesNodeProvider()"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/jgit/transport/PushCertificate.java" type="org.eclipse.jgit.transport.PushCertificate">
+ <filter comment="PushCertificate wasn't really usable in 4.0" id="338722907">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.transport.PushCertificate"/>
+ <message_argument value="PushCertificate()"/>
+ </message_arguments>
+ </filter>
+ <filter comment="PushCertificate wasn't really usable in 4.0" id="338792546">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.transport.PushCertificate"/>
+ <message_argument value="getCommandList()"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/jgit/transport/PushCertificateParser.java" type="org.eclipse.jgit.transport.PushCertificateParser">
+ <filter comment="PushCertificates haven't been really usable in 4.0" id="338849923">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.transport.PushCertificateParser"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java" type="org.eclipse.jgit.treewalk.WorkingTreeIterator">
+ <filter comment="attributes weren't really usable in earlier versions" id="338792546">
<message_arguments>
- <message_argument value="org.eclipse.jgit.merge.ResolveMerger"/>
- <message_argument value="mergeTrees(AbstractTreeIterator, RevTree, RevTree)"/>
+ <message_argument value="org.eclipse.jgit.treewalk.WorkingTreeIterator"/>
+ <message_argument value="getGlobalAttributesNode()"/>
</message_arguments>
</filter>
- <filter comment="Doesn't break consumers. Breaking providers is allowed also in minor versions." id="338792546">
+ <filter comment="attributes weren't really usable in earlier versions" id="338792546">
<message_arguments>
- <message_argument value="org.eclipse.jgit.merge.ResolveMerger"/>
- <message_argument value="processEntry(CanonicalTreeParser, CanonicalTreeParser, CanonicalTreeParser, DirCacheBuildIterator, WorkingTreeIterator)"/>
+ <message_argument value="org.eclipse.jgit.treewalk.WorkingTreeIterator"/>
+ <message_argument value="getInfoAttributesNode()"/>
</message_arguments>
</filter>
</resource>
- <resource path="src/org/eclipse/jgit/transport/GitProtocolConstants.java" type="org.eclipse.jgit.transport.GitProtocolConstants">
- <filter id="388194388">
+ <resource path="src/org/eclipse/jgit/util/FileUtils.java" type="org.eclipse.jgit.util.FileUtils">
+ <filter id="338792546">
<message_arguments>
- <message_argument value="org.eclipse.jgit.transport.GitProtocolConstants"/>
- <message_argument value="CAPABILITY_ATOMIC"/>
- <message_argument value="atomic-push"/>
+ <message_argument value="org.eclipse.jgit.util.FileUtils"/>
+ <message_argument value="createSymLink(File, String)"/>
</message_arguments>
</filter>
</resource>
diff --git a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
index 12a5556..45d6d2c 100644
--- a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
@@ -1,15 +1,15 @@
eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled
+org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=enabled
org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
-org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
-org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
-org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
-org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
+org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jgit.annotations.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jgit.annotations.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jgit.annotations.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
@@ -67,11 +67,11 @@ org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
org.eclipse.jdt.core.compiler.problem.nullReference=error
org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
-org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
+org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=ignore
org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
-org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=error
org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore
org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
@@ -111,7 +111,7 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
-org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.compiler.source=1.7
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
diff --git a/org.eclipse.jgit/BUCK b/org.eclipse.jgit/BUCK
new file mode 100644
index 0000000..73e2080
--- /dev/null
+++ b/org.eclipse.jgit/BUCK
@@ -0,0 +1,20 @@
+SRCS = glob(['src/**'])
+RESOURCES = glob(['resources/**'])
+
+java_library(
+ name = 'jgit',
+ srcs = SRCS,
+ resources = RESOURCES,
+ deps = [
+ '//lib:javaewah',
+ '//lib:jsch',
+ '//lib:httpcomponents',
+ '//lib:slf4j-api',
+ ],
+ visibility = ['PUBLIC'],
+)
+
+java_sources(
+ name = 'jgit_src',
+ srcs = SRCS + RESOURCES,
+)
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index 4dc4b79..796ebf4 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -2,10 +2,12 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Bundle-SymbolicName: org.eclipse.jgit
-Bundle-Version: 3.7.1.201504261725-r
+Bundle-Version: 4.2.0.201601211800-r
Bundle-Localization: plugin
Bundle-Vendor: %provider_name
-Export-Package: org.eclipse.jgit.api;version="3.7.1";
+Bundle-ActivationPolicy: lazy
+Export-Package: org.eclipse.jgit.annotations;version="4.2.0",
+ org.eclipse.jgit.api;version="4.2.0";
uses:="org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.diff,
@@ -19,53 +21,55 @@ Export-Package: org.eclipse.jgit.api;version="3.7.1";
org.eclipse.jgit.submodule,
org.eclipse.jgit.transport,
org.eclipse.jgit.merge",
- org.eclipse.jgit.api.errors;version="3.7.1";
- uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
- org.eclipse.jgit.attributes;version="3.7.1",
- org.eclipse.jgit.blame;version="3.7.1";
+ org.eclipse.jgit.api.errors;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
+ org.eclipse.jgit.attributes;version="4.2.0",
+ org.eclipse.jgit.blame;version="4.2.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.diff",
- org.eclipse.jgit.diff;version="3.7.1";
+ org.eclipse.jgit.diff;version="4.2.0";
uses:="org.eclipse.jgit.patch,
org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.util",
- org.eclipse.jgit.dircache;version="3.7.1";
+ org.eclipse.jgit.dircache;version="4.2.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.util,
org.eclipse.jgit.events,
org.eclipse.jgit.attributes",
- org.eclipse.jgit.errors;version="3.7.1";
+ org.eclipse.jgit.errors;version="4.2.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.internal.storage.pack,
org.eclipse.jgit.transport,
org.eclipse.jgit.dircache",
- org.eclipse.jgit.events;version="3.7.1";
- uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.fnmatch;version="3.7.1",
- org.eclipse.jgit.gitrepo;version="3.7.1";
+ org.eclipse.jgit.events;version="4.2.0";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.fnmatch;version="4.2.0",
+ org.eclipse.jgit.gitrepo;version="4.2.0";
uses:="org.eclipse.jgit.api,
org.eclipse.jgit.lib,
- org.eclipse.jgit.revwalk",
- org.eclipse.jgit.gitrepo.internal;version="3.7.1";x-internal:=true,
- org.eclipse.jgit.ignore;version="3.7.1",
- org.eclipse.jgit.ignore.internal;version="3.7.1";x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal;version="3.7.1";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
- org.eclipse.jgit.internal.storage.dfs;version="3.7.1";x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.storage.file;version="3.7.1";
+ org.eclipse.jgit.revwalk,
+ org.xml.sax.helpers,
+ org.xml.sax",
+ org.eclipse.jgit.gitrepo.internal;version="4.2.0";x-internal:=true,
+ org.eclipse.jgit.hooks;version="4.2.0";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.ignore;version="4.2.0",
+ org.eclipse.jgit.ignore.internal;version="4.2.0";x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal;version="4.2.0";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
+ org.eclipse.jgit.internal.storage.dfs;version="4.2.0";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.server",
+ org.eclipse.jgit.internal.storage.file;version="4.2.0";
x-friends:="org.eclipse.jgit.test,
org.eclipse.jgit.junit,
org.eclipse.jgit.junit.http,
org.eclipse.jgit.http.server,
- org.eclipse.jgit.java7.test,
+ org.eclipse.jgit.pgm.test,
org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.pack;version="3.7.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.lib;version="3.7.1";
+ org.eclipse.jgit.internal.storage.pack;version="4.2.0";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.reftree;version="4.2.0";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.lib;version="4.2.0";
uses:="org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.util,
@@ -75,41 +79,32 @@ Export-Package: org.eclipse.jgit.api;version="3.7.1";
org.eclipse.jgit.treewalk,
org.eclipse.jgit.transport,
org.eclipse.jgit.submodule",
- org.eclipse.jgit.merge;version="3.7.1";
+ org.eclipse.jgit.merge;version="4.2.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.diff,
org.eclipse.jgit.dircache,
org.eclipse.jgit.api",
- org.eclipse.jgit.nls;version="3.7.1",
- org.eclipse.jgit.notes;version="3.7.1";
+ org.eclipse.jgit.nls;version="4.2.0",
+ org.eclipse.jgit.notes;version="4.2.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.merge",
- org.eclipse.jgit.patch;version="3.7.1";
- uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
- org.eclipse.jgit.revplot;version="3.7.1";
- uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.revwalk",
- org.eclipse.jgit.revwalk;version="3.7.1";
+ org.eclipse.jgit.patch;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
+ org.eclipse.jgit.revplot;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
+ org.eclipse.jgit.revwalk;version="4.2.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.diff,
org.eclipse.jgit.revwalk.filter",
- org.eclipse.jgit.revwalk.filter;version="3.7.1";
- uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.file;version="3.7.1";
- uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.pack;version="3.7.1";
- uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.submodule;version="3.7.1";
- uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.treewalk.filter,
- org.eclipse.jgit.treewalk",
- org.eclipse.jgit.transport;version="3.7.1";
+ org.eclipse.jgit.revwalk.filter;version="4.2.0";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.file;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.pack;version="4.2.0";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.submodule;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.transport;version="4.2.0";
uses:="org.eclipse.jgit.transport.resolver,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.internal.storage.pack,
@@ -121,28 +116,23 @@ Export-Package: org.eclipse.jgit.api;version="3.7.1";
org.eclipse.jgit.transport.http,
org.eclipse.jgit.errors,
org.eclipse.jgit.storage.pack",
- org.eclipse.jgit.transport.http;version="3.7.1";
- uses:="javax.net.ssl",
- org.eclipse.jgit.transport.resolver;version="3.7.1";
- uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.transport",
- org.eclipse.jgit.treewalk;version="3.7.1";
+ org.eclipse.jgit.transport.http;version="4.2.0";uses:="javax.net.ssl",
+ org.eclipse.jgit.transport.resolver;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
+ org.eclipse.jgit.treewalk;version="4.2.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.attributes,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.util,
org.eclipse.jgit.dircache",
- org.eclipse.jgit.treewalk.filter;version="3.7.1";
- uses:="org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util;version="3.7.1";
+ org.eclipse.jgit.treewalk.filter;version="4.2.0";uses:="org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.util;version="4.2.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.transport.http,
org.eclipse.jgit.storage.file,
org.ietf.jgss",
- org.eclipse.jgit.util.io;version="3.7.1"
-Bundle-ActivationPolicy: lazy
-Bundle-RequiredExecutionEnvironment: J2SE-1.5
+ org.eclipse.jgit.util.io;version="4.2.0"
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Require-Bundle: com.jcraft.jsch;bundle-version="[0.1.37,0.2.0)"
Import-Package: com.googlecode.javaewah;version="[0.7.9,0.8.0)",
javax.crypto,
diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
index 72e2eeb..10b5cab 100644
--- a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit - Sources
Bundle-SymbolicName: org.eclipse.jgit.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 3.7.1.201504261725-r
-Eclipse-SourceBundle: org.eclipse.jgit;version="3.7.1.201504261725-r";roots="."
+Bundle-Version: 4.2.0.201601211800-r
+Eclipse-SourceBundle: org.eclipse.jgit;version="4.2.0.201601211800-r";roots="."
diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml
index 0b668d1..965fbe8 100644
--- a/org.eclipse.jgit/pom.xml
+++ b/org.eclipse.jgit/pom.xml
@@ -53,7 +53,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>3.7.1.201504261725-r</version>
+ <version>4.2.0.201601211800-r</version>
</parent>
<artifactId>org.eclipse.jgit</artifactId>
@@ -160,8 +160,45 @@
</plugin>
<plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>clirr-maven-plugin</artifactId>
+ <groupId>com.github.siom79.japicmp</groupId>
+ <artifactId>japicmp-maven-plugin</artifactId>
+ <version>${japicmp-version}</version>
+ <configuration>
+ <oldVersion>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>${project.artifactId}</artifactId>
+ <version>${jgit-last-release-version}</version>
+ </dependency>
+ </oldVersion>
+ <newVersion>
+ <file>
+ <path>${project.build.directory}/${project.artifactId}-${project.version}.jar</path>
+ </file>
+ </newVersion>
+ <parameter>
+ <onlyModified>true</onlyModified>
+ <includes>
+ <include>org.eclipse.jgit.*</include>
+ </includes>
+ <accessModifier>public</accessModifier>
+ <breakBuildOnModifications>false</breakBuildOnModifications>
+ <breakBuildOnBinaryIncompatibleModifications>false</breakBuildOnBinaryIncompatibleModifications>
+ <onlyBinaryIncompatible>false</onlyBinaryIncompatible>
+ <includeSynthetic>false</includeSynthetic>
+ <ignoreMissingClasses>false</ignoreMissingClasses>
+ <skipPomModules>true</skipPomModules>
+ </parameter>
+ <skip>false</skip>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>verify</phase>
+ <goals>
+ <goal>cmp</goal>
+ </goals>
+ </execution>
+ </executions>
</plugin>
</plugins>
@@ -181,13 +218,44 @@
<reporting>
<plugins>
<plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>clirr-maven-plugin</artifactId>
- <version>${clirr-version}</version>
- <configuration>
- <comparisonVersion>${jgit-last-release-version}</comparisonVersion>
- <minSeverity>info</minSeverity>
- </configuration>
+ <groupId>com.github.siom79.japicmp</groupId>
+ <artifactId>japicmp-maven-plugin</artifactId>
+ <version>${japicmp-version}</version>
+ <reportSets>
+ <reportSet>
+ <reports>
+ <report>cmp-report</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ <configuration>
+ <oldVersion>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>${project.artifactId}</artifactId>
+ <version>${jgit-last-release-version}</version>
+ </dependency>
+ </oldVersion>
+ <newVersion>
+ <file>
+ <path>${project.build.directory}/${project.artifactId}-${project.version}.jar</path>
+ </file>
+ </newVersion>
+ <parameter>
+ <onlyModified>true</onlyModified>
+ <includes>
+ <include>org.eclipse.jgit.*</include>
+ </includes>
+ <accessModifier>public</accessModifier>
+ <breakBuildOnModifications>false</breakBuildOnModifications>
+ <breakBuildOnBinaryIncompatibleModifications>false</breakBuildOnBinaryIncompatibleModifications>
+ <onlyBinaryIncompatible>false</onlyBinaryIncompatible>
+ <includeSynthetic>false</includeSynthetic>
+ <ignoreMissingClasses>false</ignoreMissingClasses>
+ <skipPomModules>true</skipPomModules>
+ </parameter>
+ <skip>false</skip>
+ </configuration>
</plugin>
</plugins>
</reporting>
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
index 66b14bb..992e10b 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -20,6 +20,7 @@ argumentIsNotAValidCommentString=Invalid comment: {0}
atLeastOnePathIsRequired=At least one path is required.
atLeastOnePatternIsRequired=At least one pattern is required.
atLeastTwoFiltersNeeded=At least two filters needed.
+atomicPushNotSupported=Atomic push not supported.
authenticationNotSupported=authentication not supported
badBase64InputCharacterAt=Bad Base64 input character at {0} : {1} (decimal)
badEntryDelimiter=Bad entry delimiter
@@ -27,6 +28,7 @@ badEntryName=Bad entry name: {0}
badEscape=Bad escape: {0}
badGroupHeader=Bad group header
badObjectType=Bad object type: {0}
+badRef=Bad ref: {0}: {1}
badSectionEntry=Bad section entry: {0}
bareRepositoryNoWorkdirAndIndex=Bare Repository has neither a working tree, nor an index
base64InputNotProperlyPadded=Base64 input not properly padded.
@@ -44,6 +46,8 @@ cannotBeCombined=Cannot be combined.
cannotBeRecursiveWhenTreesAreIncluded=TreeWalk shouldn't be recursive when tree objects are included.
cannotChangeActionOnComment=Cannot change action on comment line in git-rebase-todo file, old action: {0}, new action: {1}.
cannotChangeToComment=Cannot change a non-comment line to a comment line.
+cannotCheckoutFromUnbornBranch=Cannot checkout from unborn branch
+cannotCheckoutOursSwitchBranch=Checking out ours/theirs is only possible when checking out index, not when switching branches.
cannotCombineSquashWithNoff=Cannot combine --squash with --no-ff.
cannotCombineTreeFilterWithRevFilter=Cannot combine TreeFilter {0} with RevFilter {1}.
cannotCommitOnARepoWithState=Cannot commit on a repo with state: {0}
@@ -57,16 +61,23 @@ cannotCreateIndexfile=Cannot create an index file with name {0}
cannotCreateTempDir=Cannot create a temp dir
cannotDeleteCheckedOutBranch=Branch {0} is checked out and can not be deleted
cannotDeleteFile=Cannot delete file: {0}
+cannotDeleteObjectsPath=Cannot delete {0}/{1}: {2}
cannotDeleteStaleTrackingRef=Cannot delete stale tracking ref {0}
cannotDeleteStaleTrackingRef2=Cannot delete stale tracking ref {0}: {1}
cannotDetermineProxyFor=Cannot determine proxy for {0}
cannotDownload=Cannot download {0}
+cannotEnterObjectsPath=Cannot enter {0}/objects: {1}
+cannotEnterPathFromParent=Cannot enter {0} from {1}: {2}
cannotExecute=cannot execute: {0}
cannotGet=Cannot get {0}
+cannotGetObjectsPath=Cannot get {0}/{1}: {2}
+cannotListObjectsPath=Cannot ls {0}/{1}: {2}
+cannotListPackPath=Cannot ls {0}/pack: {1}
cannotListRefs=cannot list refs
cannotLock=Cannot lock {0}
cannotLockPackIn=Cannot lock pack in {0}
cannotMatchOnEmptyString=Cannot match on empty string.
+cannotMkdirObjectPath=Cannot mkdir {0}/{1}: {2}
cannotMoveIndexTo=Cannot move index to {0}
cannotMovePackTo=Cannot move pack to {0}
cannotOpenService=cannot open {0}
@@ -78,14 +89,19 @@ cannotReadBlob=Cannot read blob {0}
cannotReadCommit=Cannot read commit {0}
cannotReadFile=Cannot read file {0}
cannotReadHEAD=cannot read HEAD: {0} {1}
+cannotReadIndex=The index file {0} exists but cannot be read
cannotReadObject=Cannot read object
+cannotReadObjectsPath=Cannot read {0}/{1}: {2}
cannotReadTree=Cannot read tree {0}
cannotRebaseWithoutCurrentHead=Can not rebase without a current HEAD
cannotResolveLocalTrackingRefForUpdating=Cannot resolve local tracking ref {0} for updating.
cannotSquashFixupWithoutPreviousCommit=Cannot {0} without previous commit.
cannotStoreObjects=cannot store objects
+cannotResolveUniquelyAbbrevObjectId=Could not resolve uniquely the abbreviated object ID
cannotUnloadAModifiedTree=Cannot unload a modified tree.
+cannotUpdateUnbornBranch=Cannot update unborn branch
cannotWorkWithOtherStagesThanZeroRightNow=Cannot work with other stages than zero right now. Won't write corrupt index.
+cannotWriteObjectsPath=Cannot write {0}/{1}: {2}
canOnlyCherryPickCommitsWithOneParent=Cannot cherry-pick commit ''{0}'' because it has {1} parents, only commits with exactly one parent are supported.
canOnlyRevertCommitsWithOneParent=Cannot revert commit ''{0}'' because it has {1} parents, only commits with exactly one parent are supported
commitDoesNotHaveGivenParent=The commit ''{0}'' does not have a parent number {1}.
@@ -99,43 +115,79 @@ checkoutUnexpectedResult=Checkout returned unexpected result {0}
classCastNotA=Not a {0}
cloneNonEmptyDirectory=Destination path "{0}" already exists and is not an empty directory
collisionOn=Collision on {0}
+commandRejectedByHook=Rejected by "{0}" hook.\n{1}
commandWasCalledInTheWrongState=Command {0} was called in the wrong state
commitAlreadyExists=exists {0}
commitMessageNotSpecified=commit message not specified
commitOnRepoWithoutHEADCurrentlyNotSupported=Commit on repo without HEAD currently not supported
commitAmendOnInitialNotPossible=Amending is not possible on initial commit.
-commitRejectedByHook=Commit rejected by "{0}" hook.\n{1}
compressingObjects=Compressing objects
connectionFailed=connection failed
connectionTimeOut=Connection time out: {0}
contextMustBeNonNegative=context must be >= 0
corruptionDetectedReReadingAt=Corruption detected re-reading at {0}
+corruptObjectBadDate=bad date
+corruptObjectBadEmail=bad email
corruptObjectBadStream=bad stream
corruptObjectBadStreamCorruptHeader=bad stream, corrupt header
+corruptObjectBadTimezone=bad time zone
+corruptObjectDuplicateEntryNames=duplicate entry names
corruptObjectGarbageAfterSize=garbage after size
corruptObjectIncorrectLength=incorrect length
+corruptObjectIncorrectSorting=incorrectly sorted
corruptObjectInvalidEntryMode=invalid entry mode
corruptObjectInvalidMode=invalid mode
-corruptObjectInvalidMode2=invalid mode {0}
+corruptObjectInvalidModeChar=invalid mode character
+corruptObjectInvalidModeStartsZero=mode starts with '0'
+corruptObjectInvalidMode2=invalid mode {0,number,#}
corruptObjectInvalidMode3=invalid mode {0} for {1} ''{2}'' in {3}.
+corruptObjectInvalidName=invalid name '%s'
+corruptObjectInvalidNameAux=invalid name 'AUX'
+corruptObjectInvalidNameCon=invalid name 'CON'
+corruptObjectInvalidNameCom=invalid name 'COM%c'
+corruptObjectInvalidNameEnd=invalid name ends with '%c'
+corruptObjectInvalidNameIgnorableUnicode=invalid name '%s' contains ignorable Unicode characters
+corruptObjectInvalidNameInvalidUtf8=invalid name contains byte sequence ''{0}'' which is not a valid UTF-8 character
+corruptObjectInvalidNameLpt=invalid name 'LPT%c'
+corruptObjectInvalidNameNul=invalid name 'NUL'
+corruptObjectInvalidNamePrn=invalid name 'PRN'
+corruptObjectInvalidObject=invalid object
+corruptObjectInvalidParent=invalid parent
+corruptObjectInvalidTree=invalid tree
corruptObjectInvalidType=invalid type
corruptObjectInvalidType2=invalid type {0}
corruptObjectMalformedHeader=malformed header: {0}
+corruptObjectMissingEmail=missing email
+corruptObjectNameContainsByte=name contains byte 0x%x
+corruptObjectNameContainsChar=name contains '%c'
+corruptObjectNameContainsNullByte=name contains byte 0x00
+corruptObjectNameContainsSlash=name contains '/'
+corruptObjectNameDot=invalid name '.'
+corruptObjectNameDotDot=invalid name '..'
+corruptObjectNameZeroLength=zero length name
corruptObjectNegativeSize=negative size
corruptObjectNoAuthor=no author
corruptObjectNoCommitter=no committer
corruptObjectNoHeader=no header
corruptObjectNoObject=no object
+corruptObjectNoObjectHeader=no object header
corruptObjectNoTaggerBadHeader=no tagger/bad header
corruptObjectNoTaggerHeader=no tagger header
+corruptObjectNoTagHeader=no tag header
corruptObjectNoTagName=no tag name
corruptObjectNotree=no tree
+corruptObjectNotreeHeader=no tree header
corruptObjectNoType=no type
+corruptObjectNoTypeHeader=no type header
corruptObjectPackfileChecksumIncorrect=Packfile checksum incorrect.
+corruptObjectTruncatedInMode=truncated in mode
+corruptObjectTruncatedInName=truncated in name
+corruptObjectTruncatedInObjectId=truncated in object id
+corruptObjectZeroId=entry points to null SHA-1
couldNotCheckOutBecauseOfConflicts=Could not check out because of conflicts
couldNotDeleteLockFileShouldNotHappen=Could not delete lock file. Should not happen
couldNotDeleteTemporaryIndexFileShouldNotHappen=Could not delete temporary index file. Should not happen
-couldNotGetAdvertisedRef=Could not get advertised Ref for branch {0}
+couldNotGetAdvertisedRef=Remote {0} did not advertise Ref for branch {1}. This Ref may not exist in the remote or may be hidden by permission settings.
couldNotGetRepoStatistics=Could not get repository statistics
couldNotLockHEAD=Could not lock HEAD
couldNotReadIndexInOneGo=Could not read index in one go, only {0} out of {1} read
@@ -143,6 +195,7 @@ couldNotReadObjectWhileParsingCommit=Could not read an object while parsing comm
couldNotRenameDeleteOldIndex=Could not rename delete old index
couldNotRenameTemporaryFile=Could not rename temporary file {0} to new location {1}
couldNotRenameTemporaryIndexFileToIndex=Could not rename temporary index file to index
+couldNotRewindToUpstreamCommit=Could not rewind to upstream commit
couldNotURLEncodeToUTF8=Could not URL encode to UTF-8
couldNotWriteFile=Could not write file {0}
countingObjects=Counting objects
@@ -179,7 +232,9 @@ duplicateStagesNotAllowed=Duplicate stages not allowed
eitherGitDirOrWorkTreeRequired=One of setGitDir or setWorkTree must be called.
emptyCommit=No changes
emptyPathNotPermitted=Empty path not permitted.
+emptyRef=Empty ref: {0}
encryptionError=Encryption error: {0}
+encryptionOnlyPBE=Encryption error: only password-based encryption (PBE) algorithms are supported.
endOfFileInEscape=End of file in escape
entryNotFoundByPath=Entry not found by path: {0}
enumValueNotSupported2=Invalid value: {0}.{1}={2}
@@ -193,7 +248,6 @@ errorInvalidProtocolWantedOldNewRef=error: invalid protocol: wanted 'old new ref
errorListing=Error listing {0}
errorOccurredDuringUnpackingOnTheRemoteEnd=error occurred during unpacking on the remote end: {0}
errorReadingInfoRefs=error reading info/refs
-errorSymlinksNotSupported=Symlinks are not supported with this OS/JRE
exceptionCaughtDuringExecutionOfHook=Exception caught during execution of "{0}" hook.
exceptionCaughtDuringExecutionOfAddCommand=Exception caught during execution of add command
exceptionCaughtDuringExecutionOfArchiveCommand=Exception caught during execution of archive command
@@ -208,6 +262,7 @@ exceptionCaughtDuringExecutionOfResetCommand=Exception caught during execution o
exceptionCaughtDuringExecutionOfRevertCommand=Exception caught during execution of revert command. {0}
exceptionCaughtDuringExecutionOfRmCommand=Exception caught during execution of rm command
exceptionCaughtDuringExecutionOfTagCommand=Exception caught during execution of tag command
+exceptionCaughtDuringExcecutionOfCommand=Exception caught during execution of command {0} in {1}
exceptionHookExecutionInterrupted=Execution of "{0}" hook interrupted.
exceptionOccurredDuringAddingOfOptionToALogCommand=Exception occurred during adding of {0} as option to a Log command
exceptionOccurredDuringReadingOfGIT_DIR=Exception occurred during reading of $GIT_DIR/{0}. {1}
@@ -230,6 +285,9 @@ fileCannotBeDeleted=File cannot be deleted: {0}
fileIsTooBigForThisConvenienceMethod=File is too big for this convenience method ({0} bytes).
fileIsTooLarge=File is too large: {0}
fileModeNotSetForPath=FileMode not set for path {0}
+filterExecutionFailed=Execution of filter command ''{0}'' on file ''{1}'' failed
+filterExecutionFailedRc=Execution of filter command ''{0}'' on file ''{1}'' failed with return code ''{2}'', message on stderr: ''{3}''
+findingGarbage=Finding garbage
flagIsDisposed={0} is disposed.
flagNotFromThis={0} not from this.
flagsAlreadyCreated={0} flags already created.
@@ -257,6 +315,7 @@ indexWriteException=Modified index could not be written
initFailedBareRepoDifferentDirs=When initializing a bare repo with directory {0} and separate git-dir {1} specified both folders must point to the same location
initFailedNonBareRepoSameDirs=When initializing a non-bare repo with directory {0} and separate git-dir {1} specified both folders should not point to the same location
inMemoryBufferLimitExceeded=In-memory buffer limit exceeded
+inputDidntMatchLength=Input did not match supplied length. {0} bytes are missing.
inputStreamMustSupportMark=InputStream must support mark()
integerValueOutOfRange=Integer value {0}.{1} out of range
internalRevisionError=internal revision error
@@ -272,8 +331,11 @@ invalidCommitParentNumber=Invalid commit parent number
invalidEncryption=Invalid encryption
invalidGitdirRef = Invalid .git reference in file ''{0}''
invalidGitType=invalid git type: {0}
-invalidId=Invalid id {0}
+invalidId=Invalid id: {0}
+invalidId0=Invalid id
invalidIdLength=Invalid id length {0}; should be {1}
+invalidIgnoreParamSubmodule=Found invalid ignore param for submodule {0}.
+invalidIgnoreRule=Exception caught while parsing ignore rule ''{0}''.
invalidIntegerValue=Invalid integer value: {0}.{1}={2}
invalidKey=Invalid key: {0}
invalidLineInConfigFile=Invalid line in config file
@@ -290,6 +352,8 @@ invalidPathReservedOnWindows=Invalid path (''{0}'' is reserved on Windows): {1}
invalidReflogRevision=Invalid reflog revision: {0}
invalidRefName=Invalid ref name: {0}
invalidRemote=Invalid remote: {0}
+invalidRepositoryStateNoHead=Invalid repository --- cannot read HEAD
+invalidShallowObject=invalid shallow object {0}, expected commit
invalidStageForPath=Invalid stage {0} for path {1}
invalidTagOption=Invalid tag option: {0}
invalidTimeout=Invalid timeout: {0}
@@ -306,8 +370,10 @@ largeObjectException={0} exceeds size limit
largeObjectOutOfMemory=Out of memory loading {0}
lengthExceedsMaximumArraySize=Length exceeds maximum array size
listingAlternates=Listing alternates
+listingPacks=Listing packs
localObjectsIncomplete=Local objects incomplete.
localRefIsMissingObjects=Local ref {0} is missing object(s).
+localRepository=local repository
lockCountMustBeGreaterOrEqual1=lockCount must be >= 1
lockError=lock error: {0}
lockOnNotClosed=Lock on {0} not closed.
@@ -369,6 +435,7 @@ noXMLParserAvailable=No XML parser available.
objectAtHasBadZlibStream=Object at {0} in {1} has bad zlib stream
objectAtPathDoesNotHaveId=Object at path "{0}" does not have an id assigned. All object ids must be assigned prior to writing a tree.
objectIsCorrupt=Object {0} is corrupt: {1}
+objectIsCorrupt3={0}: object {1}: {2}
objectIsNotA=Object {0} is not a {1}.
objectNotFound=Object {0} not found.
objectNotFoundIn=Object {0} not found in {1}.
@@ -384,13 +451,16 @@ outputHasAlreadyBeenStarted=Output has already been started.
packChecksumMismatch=Pack checksum mismatch detected for pack file {0}
packCorruptedWhileWritingToFilesystem=Pack corrupted while writing to filesystem
packDoesNotMatchIndex=Pack {0} does not match index
+packedRefsHandleIsStale=packed-refs handle is stale, {0}. retry
packetSizeMustBeAtLeast=packet size {0} must be >= {1}
packetSizeMustBeAtMost=packet size {0} must be <= {1}
packfileCorruptionDetected=Packfile corruption detected: {0}
packFileInvalid=Pack file invalid: {0}
packfileIsTruncated=Packfile {0} is truncated.
packfileIsTruncatedNoParam=Packfile is truncated.
+packHandleIsStale=Pack file {0} handle is stale, removing it from pack list
packHasUnresolvedDeltas=pack has unresolved deltas
+packInaccessible=Pack file {0} now inaccessible; removing it from pack list
packingCancelledDuringObjectsWriting=Packing cancelled during objects writing
packObjectCountMismatch=Pack object count mismatch: pack {0} index {1}: {2}
packRefs=Pack refs
@@ -405,7 +475,9 @@ pathIsNotInWorkingDir=Path is not in working dir
pathNotConfigured=Submodule path is not configured
peeledLineBeforeRef=Peeled line before ref.
peerDidNotSupplyACompleteObjectGraph=peer did not supply a complete object graph
-prefixRemote=remote:
+personIdentEmailNonNull=E-mail address of PersonIdent must not be null.
+personIdentNameNonNull=Name of PersonIdent must not be null.
+prefixRemote=remote:
problemWithResolvingPushRefSpecsLocally=Problem with resolving push ref specs locally: {0}
progressMonUploading=Uploading {0}
propertyIsAlreadyNonNull=Property is already non null
@@ -414,6 +486,10 @@ pruneLooseUnreferencedObjects=Prune loose, unreferenced objects
pullOnRepoWithoutHEADCurrentlyNotSupported=Pull on repository without HEAD currently not supported
pullTaskName=Pull
pushCancelled=push cancelled
+pushCertificateInvalidField=Push certificate has missing or invalid value for {0}
+pushCertificateInvalidFieldValue=Push certificate has missing or invalid value for {0}: {1}
+pushCertificateInvalidHeader=Push certificate has invalid header format
+pushCertificateInvalidSignature=Push certificate has invalid signature format
pushIsNotSupportedForBundleTransport=Push is not supported for bundle transport
pushNotPermitted=push not permitted
rawLogMessageDoesNotParseAsLogEntry=Raw log message does not parse as log entry
@@ -448,6 +524,7 @@ repositoryConfigFileInvalid=Repository config file {0} invalid {1}
repositoryIsRequired=Repository is required.
repositoryNotFound=repository not found: {0}
repositoryState_applyMailbox=Apply mailbox
+repositoryState_bare=Bare
repositoryState_bisecting=Bisecting
repositoryState_conflicts=Conflicts
repositoryState_merged=Merged
@@ -461,6 +538,9 @@ resettingHead=Resetting head to {0}
resolvingDeltas=Resolving deltas
resultLengthIncorrect=result length incorrect
rewinding=Rewinding to commit {0}
+s3ActionDeletion=Deletion
+s3ActionReading=Reading
+s3ActionWriting=Writing
searchForReuse=Finding sources
searchForSizes=Getting sizes
secondsAgo={0} seconds ago
@@ -468,7 +548,6 @@ selectingCommits=Selecting commits
sequenceTooLargeForDiffAlgorithm=Sequence too large for difference algorithm.
serviceNotEnabledNoName=Service not enabled
serviceNotPermitted={0} not permitted
-serviceNotPermittedNoName=Service not permitted
shallowCommitsAlreadyInitialized=Shallow commits have already been initialized
shortCompressedStreamAt=Short compressed stream at {0}
shortReadOfBlock=Short read of block.
@@ -498,9 +577,13 @@ stashDropMissingReflog=Stash reflog does not contain entry ''{0}''
stashFailed=Stashing local changes did not successfully complete
stashResolveFailed=Reference ''{0}'' does not resolve to stashed commit
statelessRPCRequiresOptionToBeEnabled=stateless RPC requires {0} to be enabled
+storePushCertMultipleRefs=Store push certificate for {0} refs
+storePushCertOneRef=Store push certificate for {0}
+storePushCertReflog=Store push certificate
submoduleExists=Submodule ''{0}'' already exists in the index
submoduleParentRemoteUrlInvalid=Cannot remove segment from remote url ''{0}''
submodulesNotSupported=Submodules are not supported
+supportOnlyPackIndexVersion2=Only support index version 2
symlinkCannotBeWrittenAsTheLinkTarget=Symlink "{0}" cannot be written as the link target cannot be read from within Java.
systemConfigFileInvalid=Systen wide config file {0} is invalid {1}
tagAlreadyExists=tag ''{0}'' already exists
@@ -516,6 +599,7 @@ transportExceptionInvalid=Invalid {0} {1}:{2}
transportExceptionMissingAssumed=Missing assumed {0}
transportExceptionReadRef=read {0}
transportNeedsRepository=Transport needs repository
+transportProvidedRefWithNoObjectId=Transport provided ref {0} with no object id
transportProtoAmazonS3=Amazon S3
transportProtoBundleFile=Git Bundle File
transportProtoFTP=FTP
@@ -524,6 +608,7 @@ transportProtoHTTP=HTTP
transportProtoLocal=Local Git Repository
transportProtoSFTP=SFTP
transportProtoSSH=SSH
+transportProtoTest=Test
transportSSHRetryInterrupt=Interrupted while waiting for retry
treeEntryAlreadyExists=Tree entry "{0}" already exists.
treeFilterMarkerTooManyFilters=Too many markTreeFilters passed, maximum number is {0} (passed {1})
@@ -534,11 +619,14 @@ truncatedHunkNewLinesMissing=Truncated hunk, at least {0} new lines is missing
truncatedHunkOldLinesMissing=Truncated hunk, at least {0} old lines is missing
tSizeMustBeGreaterOrEqual1=tSize must be >= 1
unableToCheckConnectivity=Unable to check connectivity.
+unableToCreateNewObject=Unable to create new object: {0}
unableToStore=Unable to store {0}.
unableToWrite=Unable to write {0}
+unauthorized=Unauthorized
unencodeableFile=Unencodable file: {0}
unexpectedCompareResult=Unexpected metadata comparison result: {0}
unexpectedEndOfConfigFile=Unexpected end of config file
+unexpectedEndOfInput=Unexpected end of input
unexpectedHunkTrailer=Unexpected hunk trailer
unexpectedOddResult=odd: {0} + {1} - {2}
unexpectedRefReport={0}: unexpected ref report: {1}
@@ -550,6 +638,7 @@ unknownHost=unknown host
unknownIndexVersionOrCorruptIndex=Unknown index version (or corrupt index): {0}
unknownObject=unknown object
unknownObjectType=Unknown object type {0}.
+unknownObjectType2=unknown
unknownRepositoryFormat=Unknown repository format
unknownRepositoryFormat2=Unknown repository format "{0}"; expected "0".
unknownZlibError=Unknown zlib error.
@@ -558,16 +647,21 @@ unmergedPaths=Repository contains unmerged paths
unpackException=Exception while parsing pack stream
unreadablePackIndex=Unreadable pack index: {0}
unrecognizedRef=Unrecognized ref: {0}
+unsetMark=Mark not set
+unsupportedAlternates=Alternates not supported
unsupportedArchiveFormat=Unknown archive format ''{0}''
unsupportedCommand0=unsupported command 0
unsupportedEncryptionAlgorithm=Unsupported encryption algorithm: {0}
unsupportedEncryptionVersion=Unsupported encryption version: {0}
unsupportedGC=Unsupported garbage collector for repository type: {0}
+unsupportedMark=Mark not supported
unsupportedOperationNotAddAtEnd=Not add-at-end: {0}
unsupportedPackIndexVersion=Unsupported pack index version {0}
unsupportedPackVersion=Unsupported pack version {0}.
+updatingHeadFailed=Updating HEAD failed
updatingReferences=Updating references
updatingRefFailed=Updating the ref {0} to {1} failed. ReturnCode from RefUpdate.update() was {2}
+upstreamBranchName=branch ''{0}'' of {1}
uriNotConfigured=Submodule URI not configured
uriNotFound={0} not found
URINotSupported=URI not supported: {0}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java
similarity index 69%
copy from org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java
index 46ea2aa..08f81cb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, Shawn O. Pearce <spearce at spearce.org>
+ * Copyright (C) 2015, Andrey Loskutov <loskutov at gmx.de>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -41,53 +41,29 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.util;
+package org.eclipse.jgit.annotations;
-import java.io.File;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
/**
- * FS implementaton for Java5
+ * JGit's replacement for the {@code javax.annotation.Nonnull}.
+ * <p>
+ * Denotes that a local variable, parameter, field, method return value expected
+ * to be non {@code null}.
*
- * @since 3.0
+ * @since 4.2
*/
-public class FS_POSIX_Java5 extends FS_POSIX {
- /**
- * Constructor
- */
- public FS_POSIX_Java5() {
- super();
- }
-
- /**
- * Constructor
- *
- * @param src
- * instance whose attributes to copy
- */
- public FS_POSIX_Java5(FS src) {
- super(src);
- }
-
- @Override
- public FS newInstance() {
- return new FS_POSIX_Java5(this);
- }
-
- public boolean supportsExecute() {
- return false;
- }
-
- public boolean canExecute(final File f) {
- return false;
- }
-
- public boolean setExecute(final File f, final boolean canExec) {
- return false;
- }
-
- @Override
- public boolean retryFailedLockFileCommit() {
- return false;
- }
+ at Documented
+ at Retention(RetentionPolicy.CLASS)
+ at Target({ FIELD, METHOD, PARAMETER, LOCAL_VARIABLE })
+public @interface NonNull {
+ // marker annotation with no members
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/annotations/Nullable.java b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/Nullable.java
new file mode 100644
index 0000000..7b91567
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/Nullable.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks types that can hold the value {@code null} at run time.
+ * <p>
+ * Unlike {@code org.eclipse.jdt.annotation.Nullable}, this has run-time
+ * retention, allowing the annotation to be recognized by
+ * <a href="https://github.com/google/guice/wiki/UseNullable">Guice</a>. Unlike
+ * {@code javax.annotation.Nullable}, this does not involve importing new classes
+ * to a standard (Java EE) package, so it can be deployed in an OSGi container
+ * without running into
+ * <a href="http://wiki.osgi.org/wiki/Split_Packages">split-package</a>
+ * <a href="https://gerrit-review.googlesource.com/50112">problems</a>.
+ * <p>
+ * You can use this annotation to qualify a type in a method signature or local
+ * variable declaration. The entity whose type has this annotation is allowed to
+ * hold the value {@code null} at run time. This allows annotation based null
+ * analysis to infer that
+ * <ul>
+ * <li>Binding a {@code null} value to the entity is legal.
+ * <li>Dereferencing the entity is unsafe and can trigger a
+ * {@code NullPointerException}.
+ * </ul>
+ * <p>
+ * To avoid a dependency on Java 8, this annotation does not use
+ * {@link Target @Target} {@code TYPE_USE}. That may change when JGit starts
+ * requiring Java 8.
+ * <p>
+ * <b>Warning:</b> Please do not use this annotation on arrays. Different
+ * annotation processors treat {@code @Nullable Object[]} differently: some
+ * treat it as an array of nullable objects, for consistency with versions of
+ * {@code Nullable} defined with {@code @Target} {@code TYPE_USE}, while others
+ * treat it as a nullable array of objects. JGit therefore avoids using this
+ * annotation on arrays altogether.
+ *
+ * @see <a href=
+ * "http://types.cs.washington.edu/checker-framework/current/checker-framework-manual.html#faq-array-syntax-meaning">
+ * The checker-framework manual</a>
+ *
+ * @since 4.2
+ */
+ at Documented
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target({ FIELD, METHOD, PARAMETER, LOCAL_VARIABLE })
+public @interface Nullable {
+ // marker annotation with no members
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
index c23256c..3b94f16 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
@@ -43,11 +43,16 @@
*/
package org.eclipse.jgit.api;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.FileMode.GITLINK;
+import static org.eclipse.jgit.lib.FileMode.TYPE_TREE;
+
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.LinkedList;
+import org.eclipse.jgit.api.errors.FilterFailedException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.NoFilepatternException;
@@ -57,12 +62,13 @@ import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.treewalk.FileTreeIterator;
-import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
+import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
import org.eclipse.jgit.treewalk.WorkingTreeIterator;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
@@ -133,86 +139,109 @@ public class AddCommand extends GitCommand<DirCache> {
throw new NoFilepatternException(JGitText.get().atLeastOnePatternIsRequired);
checkCallable();
DirCache dc = null;
- boolean addAll = false;
- if (filepatterns.contains(".")) //$NON-NLS-1$
- addAll = true;
+ boolean addAll = filepatterns.contains("."); //$NON-NLS-1$
- ObjectInserter inserter = repo.newObjectInserter();
- try {
+ try (ObjectInserter inserter = repo.newObjectInserter();
+ NameConflictTreeWalk tw = new NameConflictTreeWalk(repo)) {
+ tw.setOperationType(OperationType.CHECKIN_OP);
dc = repo.lockDirCache();
- DirCacheIterator c;
DirCacheBuilder builder = dc.builder();
- final TreeWalk tw = new TreeWalk(repo);
tw.addTree(new DirCacheBuildIterator(builder));
if (workingTreeIterator == null)
workingTreeIterator = new FileTreeIterator(repo);
+ workingTreeIterator.setDirCacheIterator(tw, 0);
tw.addTree(workingTreeIterator);
- tw.setRecursive(true);
if (!addAll)
tw.setFilter(PathFilterGroup.createFromStrings(filepatterns));
- String lastAddedFile = null;
+ byte[] lastAdded = null;
while (tw.next()) {
- String path = tw.getPathString();
-
+ DirCacheIterator c = tw.getTree(0, DirCacheIterator.class);
WorkingTreeIterator f = tw.getTree(1, WorkingTreeIterator.class);
- if (tw.getTree(0, DirCacheIterator.class) == null &&
- f != null && f.isEntryIgnored()) {
+ if (c == null && f != null && f.isEntryIgnored()) {
// file is not in index but is ignored, do nothing
+ continue;
+ } else if (c == null && update) {
+ // Only update of existing entries was requested.
+ continue;
+ }
+
+ DirCacheEntry entry = c != null ? c.getDirCacheEntry() : null;
+ if (entry != null && entry.getStage() > 0
+ && lastAdded != null
+ && lastAdded.length == tw.getPathLength()
+ && tw.isPathPrefix(lastAdded, lastAdded.length) == 0) {
+ // In case of an existing merge conflict the
+ // DirCacheBuildIterator iterates over all stages of
+ // this path, we however want to add only one
+ // new DirCacheEntry per path.
+ continue;
+ }
+
+ if (tw.isSubtree() && !tw.isDirectoryFileConflict()) {
+ tw.enterSubtree();
+ continue;
+ }
+
+ if (f == null) { // working tree file does not exist
+ if (entry != null
+ && (!update || GITLINK == entry.getFileMode())) {
+ builder.add(entry);
+ }
+ continue;
+ }
+
+ if (entry != null && entry.isAssumeValid()) {
+ // Index entry is marked assume valid. Even though
+ // the user specified the file to be added JGit does
+ // not consider the file for addition.
+ builder.add(entry);
+ continue;
+ }
+
+ if (f.getEntryRawMode() == TYPE_TREE) {
+ // Index entry exists and is symlink, gitlink or file,
+ // otherwise the tree would have been entered above.
+ // Replace the index entry by diving into tree of files.
+ tw.enterSubtree();
+ continue;
+ }
+
+ byte[] path = tw.getRawPath();
+ if (entry == null || entry.getStage() > 0) {
+ entry = new DirCacheEntry(path);
}
- // In case of an existing merge conflict the
- // DirCacheBuildIterator iterates over all stages of
- // this path, we however want to add only one
- // new DirCacheEntry per path.
- else if (!(path.equals(lastAddedFile))) {
- if (!(update && tw.getTree(0, DirCacheIterator.class) == null)) {
- c = tw.getTree(0, DirCacheIterator.class);
- if (f != null) { // the file exists
- long sz = f.getEntryLength();
- DirCacheEntry entry = new DirCacheEntry(path);
- if (c == null || c.getDirCacheEntry() == null
- || !c.getDirCacheEntry().isAssumeValid()) {
- FileMode mode = f.getIndexFileMode(c);
- entry.setFileMode(mode);
-
- if (FileMode.GITLINK != mode) {
- entry.setLength(sz);
- entry.setLastModified(f
- .getEntryLastModified());
- long contentSize = f
- .getEntryContentLength();
- InputStream in = f.openEntryStream();
- try {
- entry.setObjectId(inserter.insert(
- Constants.OBJ_BLOB, contentSize, in));
- } finally {
- in.close();
- }
- } else
- entry.setObjectId(f.getEntryObjectId());
- builder.add(entry);
- lastAddedFile = path;
- } else {
- builder.add(c.getDirCacheEntry());
- }
-
- } else if (c != null
- && (!update || FileMode.GITLINK == c
- .getEntryFileMode()))
- builder.add(c.getDirCacheEntry());
+ FileMode mode = f.getIndexFileMode(c);
+ entry.setFileMode(mode);
+
+ if (GITLINK != mode) {
+ entry.setLength(f.getEntryLength());
+ entry.setLastModified(f.getEntryLastModified());
+ long len = f.getEntryContentLength();
+ try (InputStream in = f.openEntryStream()) {
+ ObjectId id = inserter.insert(OBJ_BLOB, len, in);
+ entry.setObjectId(id);
}
+ } else {
+ entry.setLength(0);
+ entry.setLastModified(0);
+ entry.setObjectId(f.getEntryObjectId());
}
+ builder.add(entry);
+ lastAdded = path;
}
inserter.flush();
builder.commit();
setCallable(false);
} catch (IOException e) {
+ Throwable cause = e.getCause();
+ if (cause != null && cause instanceof FilterFailedException)
+ throw (FilterFailedException) cause;
throw new JGitInternalException(
JGitText.get().exceptionCaughtDuringExecutionOfAddCommand, e);
} finally {
- inserter.release();
if (dc != null)
dc.unlock();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddNoteCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddNoteCommand.java
index 3af8695..9cf8881 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddNoteCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddNoteCommand.java
@@ -83,11 +83,10 @@ public class AddNoteCommand extends GitCommand<Note> {
public Note call() throws GitAPIException {
checkCallable();
- RevWalk walk = new RevWalk(repo);
- ObjectInserter inserter = repo.newObjectInserter();
NoteMap map = NoteMap.newEmptyMap();
RevCommit notesCommit = null;
- try {
+ try (RevWalk walk = new RevWalk(repo);
+ ObjectInserter inserter = repo.newObjectInserter()) {
Ref ref = repo.getRef(notesRef);
// if we have a notes ref, use it
if (ref != null) {
@@ -96,13 +95,10 @@ public class AddNoteCommand extends GitCommand<Note> {
}
map.set(id, message, inserter);
commitNoteMap(walk, map, notesCommit, inserter,
- "Notes added by 'git notes add'");
+ "Notes added by 'git notes add'"); //$NON-NLS-1$
return map.getNote(id);
} catch (IOException e) {
throw new JGitInternalException(e.getMessage(), e);
- } finally {
- inserter.release();
- walk.release();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
index 6a945e4..676ae03 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
@@ -47,6 +47,7 @@ import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.StandardCopyOption;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
@@ -141,9 +142,13 @@ public class ApplyCommand extends GitCommand<ApplyResult> {
case RENAME:
f = getFile(fh.getOldPath(), false);
File dest = getFile(fh.getNewPath(), false);
- if (!f.renameTo(dest))
+ try {
+ FileUtils.rename(f, dest,
+ StandardCopyOption.ATOMIC_MOVE);
+ } catch (IOException e) {
throw new PatchApplyException(MessageFormat.format(
- JGitText.get().renameFileFailed, f, dest));
+ JGitText.get().renameFileFailed, f, dest), e);
+ }
break;
case COPY:
f = getFile(fh.getOldPath(), false);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java
index 616d5b4..8543bd5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java
@@ -48,7 +48,9 @@ import java.io.OutputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -136,6 +138,25 @@ public class ArchiveCommand extends GitCommand<OutputStream> {
T createArchiveOutputStream(OutputStream s) throws IOException;
/**
+ * Start a new archive. Entries can be included in the archive using the
+ * putEntry method, and then the archive should be closed using its
+ * close method. In addition options can be applied to the underlying
+ * stream. E.g. compression level.
+ *
+ * @param s
+ * underlying output stream to which to write the archive.
+ * @param o
+ * options to apply to the underlying output stream. Keys are
+ * option names and values are option values.
+ * @return new archive object for use in putEntry
+ * @throws IOException
+ * thrown by the underlying output stream for I/O errors
+ * @since 4.0
+ */
+ T createArchiveOutputStream(OutputStream s, Map<String, Object> o)
+ throws IOException;
+
+ /**
* Write an entry to an archive.
*
* @param out
@@ -328,6 +349,7 @@ public class ArchiveCommand extends GitCommand<OutputStream> {
private ObjectId tree;
private String prefix;
private String format;
+ private Map<String, Object> formatOptions = new HashMap<>();
private List<String> paths = new ArrayList<String>();
/** Filename suffix, for automatically choosing a format. */
@@ -342,21 +364,20 @@ public class ArchiveCommand extends GitCommand<OutputStream> {
}
private <T extends Closeable> OutputStream writeArchive(Format<T> fmt) {
- final String pfx = prefix == null ? "" : prefix; //$NON-NLS-1$
- final TreeWalk walk = new TreeWalk(repo);
try {
- final T outa = fmt.createArchiveOutputStream(out);
- try {
- final MutableObjectId idBuf = new MutableObjectId();
- final ObjectReader reader = walk.getObjectReader();
- final RevWalk rw = new RevWalk(walk.getObjectReader());
+ try (TreeWalk walk = new TreeWalk(repo);
+ RevWalk rw = new RevWalk(walk.getObjectReader())) {
+ String pfx = prefix == null ? "" : prefix; //$NON-NLS-1$
+ T outa = fmt.createArchiveOutputStream(out, formatOptions);
+ MutableObjectId idBuf = new MutableObjectId();
+ ObjectReader reader = walk.getObjectReader();
walk.reset(rw.parseTree(tree));
if (!paths.isEmpty())
walk.setFilter(PathFilterGroup.createFromStrings(paths));
while (walk.next()) {
- final String name = pfx + walk.getPathString();
+ String name = pfx + walk.getPathString();
FileMode mode = walk.getFileMode(0);
if (walk.isSubtree())
@@ -375,16 +396,14 @@ public class ArchiveCommand extends GitCommand<OutputStream> {
fmt.putEntry(outa, name, mode, reader.open(idBuf));
}
outa.close();
+ return out;
} finally {
out.close();
}
- return out;
} catch (IOException e) {
// TODO(jrn): Throw finer-grained errors.
throw new JGitInternalException(
JGitText.get().exceptionCaughtDuringExecutionOfArchiveCommand, e);
- } finally {
- walk.release();
}
}
@@ -395,7 +414,7 @@ public class ArchiveCommand extends GitCommand<OutputStream> {
public OutputStream call() throws GitAPIException {
checkCallable();
- final Format<?> fmt;
+ Format<?> fmt;
if (format == null)
fmt = formatBySuffix(suffix);
else
@@ -472,6 +491,17 @@ public class ArchiveCommand extends GitCommand<OutputStream> {
}
/**
+ * @param options
+ * archive format options (e.g., level=9 for zip compression).
+ * @return this
+ * @since 4.0
+ */
+ public ArchiveCommand setFormatOptions(Map<String, Object> options) {
+ this.formatOptions = options;
+ return this;
+ }
+
+ /**
* Set an optional parameter path. without an optional path parameter, all
* files and subdirectories of the current working directory are included in
* the archive. If one or more paths are specified, only these are included.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java
index f7ce835..a83814e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java
@@ -200,8 +200,7 @@ public class BlameCommand extends GitCommand<BlameResult> {
*/
public BlameResult call() throws GitAPIException {
checkCallable();
- BlameGenerator gen = new BlameGenerator(repo, path);
- try {
+ try (BlameGenerator gen = new BlameGenerator(repo, path)) {
if (diffAlgorithm != null)
gen.setDiffAlgorithm(diffAlgorithm);
if (textComparator != null)
@@ -231,8 +230,6 @@ public class BlameCommand extends GitCommand<BlameResult> {
return gen.computeBlameResult();
} catch (IOException e) {
throw new JGitInternalException(e.getMessage(), e);
- } finally {
- gen.release();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
index 3787ac5..4f918fa 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
@@ -208,19 +208,26 @@ public class CheckoutCommand extends GitCommand<Ref> {
}
if (createBranch) {
- Git git = new Git(repo);
- CreateBranchCommand command = git.branchCreate();
- command.setName(name);
- if (startCommit != null)
- command.setStartPoint(startCommit);
- else
- command.setStartPoint(startPoint);
- if (upstreamMode != null)
- command.setUpstreamMode(upstreamMode);
- command.call();
+ try (Git git = new Git(repo)) {
+ CreateBranchCommand command = git.branchCreate();
+ command.setName(name);
+ if (startCommit != null)
+ command.setStartPoint(startCommit);
+ else
+ command.setStartPoint(startPoint);
+ if (upstreamMode != null)
+ command.setUpstreamMode(upstreamMode);
+ command.call();
+ }
}
Ref headRef = repo.getRef(Constants.HEAD);
+ if (headRef == null) {
+ // TODO Git CLI supports checkout from unborn branch, we should
+ // also allow this
+ throw new UnsupportedOperationException(
+ JGitText.get().cannotCheckoutFromUnbornBranch);
+ }
String shortHeadRef = getShortBranchName(headRef);
String refLogMessage = "checkout: moving from " + shortHeadRef; //$NON-NLS-1$
ObjectId branch;
@@ -243,11 +250,14 @@ public class CheckoutCommand extends GitCommand<Ref> {
JGitText.get().refNotResolved, name));
}
- RevWalk revWalk = new RevWalk(repo);
- AnyObjectId headId = headRef.getObjectId();
- RevCommit headCommit = headId == null ? null : revWalk
- .parseCommit(headId);
- RevCommit newCommit = revWalk.parseCommit(branch);
+ RevCommit headCommit = null;
+ RevCommit newCommit = null;
+ try (RevWalk revWalk = new RevWalk(repo)) {
+ AnyObjectId headId = headRef.getObjectId();
+ headCommit = headId == null ? null
+ : revWalk.parseCommit(headId);
+ newCommit = revWalk.parseCommit(branch);
+ }
RevTree headTree = headCommit == null ? null : headCommit.getTree();
DirCacheCheckout dco;
DirCache dc = repo.lockDirCache();
@@ -321,9 +331,16 @@ public class CheckoutCommand extends GitCommand<Ref> {
}
private String getShortBranchName(Ref headRef) {
- if (headRef.getTarget().getName().equals(headRef.getName()))
- return headRef.getTarget().getObjectId().getName();
- return Repository.shortenRefName(headRef.getTarget().getName());
+ if (headRef.isSymbolic()) {
+ return Repository.shortenRefName(headRef.getTarget().getName());
+ }
+ // Detached HEAD. Every non-symbolic ref in the ref database has an
+ // object id, so this cannot be null.
+ ObjectId id = headRef.getObjectId();
+ if (id == null) {
+ throw new NullPointerException();
+ }
+ return id.getName();
}
/**
@@ -376,26 +393,20 @@ public class CheckoutCommand extends GitCommand<Ref> {
*/
protected CheckoutCommand checkoutPaths() throws IOException,
RefNotFoundException {
- RevWalk revWalk = new RevWalk(repo);
DirCache dc = repo.lockDirCache();
- try {
- TreeWalk treeWalk = new TreeWalk(revWalk.getObjectReader());
+ try (RevWalk revWalk = new RevWalk(repo);
+ TreeWalk treeWalk = new TreeWalk(revWalk.getObjectReader())) {
treeWalk.setRecursive(true);
if (!checkoutAllPaths)
treeWalk.setFilter(PathFilterGroup.createFromStrings(paths));
- try {
- if (isCheckoutIndex())
- checkoutPathsFromIndex(treeWalk, dc);
- else {
- RevCommit commit = revWalk.parseCommit(getStartPointObjectId());
- checkoutPathsFromCommit(treeWalk, dc, commit);
- }
- } finally {
- treeWalk.release();
+ if (isCheckoutIndex())
+ checkoutPathsFromIndex(treeWalk, dc);
+ else {
+ RevCommit commit = revWalk.parseCommit(getStartPointObjectId());
+ checkoutPathsFromCommit(treeWalk, dc, commit);
}
} finally {
dc.unlock();
- revWalk.release();
}
return this;
}
@@ -459,7 +470,7 @@ public class CheckoutCommand extends GitCommand<Ref> {
private void checkoutPath(DirCacheEntry entry, ObjectReader reader) {
try {
- DirCacheCheckout.checkoutEntry(repo, entry, reader);
+ DirCacheCheckout.checkoutEntry(repo, entry, reader, true);
} catch (IOException e) {
throw new JGitInternalException(MessageFormat.format(
JGitText.get().checkoutConflictWithFile,
@@ -675,7 +686,6 @@ public class CheckoutCommand extends GitCommand<Ref> {
private void checkOptions() {
if (checkoutStage != null && !isCheckoutIndex())
throw new IllegalStateException(
- "Checking out ours/theirs is only possible when checking out index, "
- + "not when switching branches.");
+ JGitText.get().cannotCheckoutOursSwitchBranch);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java
index d4eb0b3..d6e930a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java
@@ -123,8 +123,7 @@ public class CherryPickCommand extends GitCommand<CherryPickResult> {
List<Ref> cherryPickedRefs = new LinkedList<Ref>();
checkCallable();
- RevWalk revWalk = new RevWalk(repo);
- try {
+ try (RevWalk revWalk = new RevWalk(repo)) {
// get the head commit
Ref headRef = repo.getRef(Constants.HEAD);
@@ -153,7 +152,7 @@ public class CherryPickCommand extends GitCommand<CherryPickResult> {
ResolveMerger merger = (ResolveMerger) strategy.newMerger(repo);
merger.setWorkingTreeIterator(new FileTreeIterator(repo));
merger.setBase(srcParent.getTree());
- merger.setCommitNames(new String[] { "BASE", ourName,
+ merger.setCommitNames(new String[] { "BASE", ourName, //$NON-NLS-1$
cherryPickName });
if (merger.merge(newHead, srcCommit)) {
if (AnyObjectId.equals(newHead.getTree().getId(), merger
@@ -194,8 +193,6 @@ public class CherryPickCommand extends GitCommand<CherryPickResult> {
MessageFormat.format(
JGitText.get().exceptionCaughtDuringExecutionOfCherryPickCommand,
e), e);
- } finally {
- revWalk.release();
}
return new CherryPickResult(newHead, cherryPickedRefs);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickResult.java
index 5b3c5d4..b121291 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickResult.java
@@ -63,21 +63,21 @@ public class CherryPickResult {
OK {
@Override
public String toString() {
- return "Ok";
+ return "Ok"; //$NON-NLS-1$
}
},
/** */
FAILED {
@Override
public String toString() {
- return "Failed";
+ return "Failed"; //$NON-NLS-1$
}
},
/** */
CONFLICTING {
@Override
public String toString() {
- return "Conflicting";
+ return "Conflicting"; //$NON-NLS-1$
}
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
index f058f79..2ac8729 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
@@ -61,6 +61,7 @@ import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
@@ -127,16 +128,23 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
*/
public Git call() throws GitAPIException, InvalidRemoteException,
org.eclipse.jgit.api.errors.TransportException {
+ Repository repository = null;
try {
URIish u = new URIish(uri);
- Repository repository = init(u);
+ repository = init(u);
FetchResult result = fetch(repository, u);
if (!noCheckout)
checkout(repository, result);
- return new Git(repository);
+ return new Git(repository, true);
} catch (IOException ioe) {
+ if (repository != null) {
+ repository.close();
+ }
throw new JGitInternalException(ioe.getMessage(), ioe);
} catch (URISyntaxException e) {
+ if (repository != null) {
+ repository.close();
+ }
throw new InvalidRemoteException(MessageFormat.format(
JGitText.get().invalidRemote, remote));
}
@@ -228,7 +236,7 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
}
if (head == null || head.getObjectId() == null)
- return; // throw exception?
+ return; // TODO throw exception?
if (head.getName().startsWith(Constants.R_HEADS)) {
final RefUpdate newHead = clonedRepo.updateRef(Constants.HEAD);
@@ -280,20 +288,24 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
private Ref findBranchToCheckout(FetchResult result) {
final Ref idHEAD = result.getAdvertisedRef(Constants.HEAD);
- if (idHEAD == null)
+ ObjectId headId = idHEAD != null ? idHEAD.getObjectId() : null;
+ if (headId == null) {
return null;
+ }
Ref master = result.getAdvertisedRef(Constants.R_HEADS
+ Constants.MASTER);
- if (master != null && master.getObjectId().equals(idHEAD.getObjectId()))
+ ObjectId objectId = master != null ? master.getObjectId() : null;
+ if (headId.equals(objectId)) {
return master;
+ }
Ref foundBranch = null;
for (final Ref r : result.getAdvertisedRefs()) {
final String n = r.getName();
if (!n.startsWith(Constants.R_HEADS))
continue;
- if (r.getObjectId().equals(idHEAD.getObjectId())) {
+ if (headId.equals(r.getObjectId())) {
foundBranch = r;
break;
}
@@ -322,19 +334,17 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
private RevCommit parseCommit(final Repository clonedRepo, final Ref ref)
throws MissingObjectException, IncorrectObjectTypeException,
IOException {
- final RevWalk rw = new RevWalk(clonedRepo);
final RevCommit commit;
- try {
+ try (final RevWalk rw = new RevWalk(clonedRepo)) {
commit = rw.parseCommit(ref.getObjectId());
- } finally {
- rw.release();
}
return commit;
}
/**
* @param uri
- * the uri to clone from
+ * the URI to clone from, or {@code null} to unset the URI.
+ * The URI must be set before {@link #call} is called.
* @return this instance
*/
public CloneCommand setURI(String uri) {
@@ -349,7 +359,8 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
* @see URIish#getHumanishName()
*
* @param directory
- * the directory to clone to
+ * the directory to clone to, or {@code null} if the directory
+ * name should be taken from the source uri
* @return this instance
* @throws IllegalStateException
* if the combination of directory, gitDir and bare is illegal.
@@ -365,7 +376,8 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
/**
* @param gitDir
- * the repository meta directory
+ * the repository meta directory, or {@code null} to choose one
+ * automatically at clone time
* @return this instance
* @throws IllegalStateException
* if the combination of directory, gitDir and bare is illegal.
@@ -403,10 +415,14 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
*
* @see Constants#DEFAULT_REMOTE_NAME
* @param remote
- * name that keeps track of the upstream repository
+ * name that keeps track of the upstream repository.
+ * {@code null} means to use DEFAULT_REMOTE_NAME.
* @return this instance
*/
public CloneCommand setRemote(String remote) {
+ if (remote == null) {
+ remote = Constants.DEFAULT_REMOTE_NAME;
+ }
this.remote = remote;
return this;
}
@@ -416,9 +432,15 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
* the initial branch to check out when cloning the repository.
* Can be specified as ref name (<code>refs/heads/master</code>),
* branch name (<code>master</code>) or tag name (<code>v1.2.3</code>).
+ * The default is to use the branch pointed to by the cloned
+ * repository's HEAD and can be requested by passing {@code null}
+ * or <code>HEAD</code>.
* @return this instance
*/
public CloneCommand setBranch(String branch) {
+ if (branch == null) {
+ branch = Constants.HEAD;
+ }
this.branch = branch;
return this;
}
@@ -433,6 +455,9 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
* @return {@code this}
*/
public CloneCommand setProgressMonitor(ProgressMonitor monitor) {
+ if (monitor == null) {
+ monitor = NullProgressMonitor.INSTANCE;
+ }
this.monitor = monitor;
return this;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
index 93400aa..b5057ad 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
@@ -42,7 +42,6 @@
*/
package org.eclipse.jgit.api;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
@@ -52,13 +51,14 @@ import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
+import org.eclipse.jgit.api.errors.AbortedByHookException;
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
+import org.eclipse.jgit.api.errors.EmtpyCommitException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.NoFilepatternException;
import org.eclipse.jgit.api.errors.NoHeadException;
import org.eclipse.jgit.api.errors.NoMessageException;
-import org.eclipse.jgit.api.errors.RejectCommitException;
import org.eclipse.jgit.api.errors.UnmergedPathsException;
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
import org.eclipse.jgit.dircache.DirCache;
@@ -67,6 +67,7 @@ import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.errors.UnmergedPathException;
+import org.eclipse.jgit.hooks.Hooks;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Constants;
@@ -86,10 +87,8 @@ import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
import org.eclipse.jgit.util.ChangeIdUtil;
-import org.eclipse.jgit.util.FS;
-import org.eclipse.jgit.util.Hook;
-import org.eclipse.jgit.util.ProcessResult;
/**
* A class used to execute a {@code Commit} command. It has setters for all
@@ -126,19 +125,19 @@ public class CommitCommand extends GitCommand<RevCommit> {
private String reflogComment;
/**
- * Setting this option bypasses the {@link Hook#PRE_COMMIT pre-commit} and
- * {@link Hook#COMMIT_MSG commit-msg} hooks.
+ * Setting this option bypasses the pre-commit and commit-msg hooks.
*/
private boolean noVerify;
private PrintStream hookOutRedirect;
+ private Boolean allowEmpty;
+
/**
* @param repo
*/
protected CommitCommand(Repository repo) {
super(repo);
- hookOutRedirect = System.out;
}
/**
@@ -159,20 +158,18 @@ public class CommitCommand extends GitCommand<RevCommit> {
* else
* @throws WrongRepositoryStateException
* when repository is not in the right state for committing
- * @throws RejectCommitException
+ * @throws AbortedByHookException
* if there are either pre-commit or commit-msg hooks present in
- * the repository and at least one of them rejects the commit.
+ * the repository and one of them rejects the commit.
*/
public RevCommit call() throws GitAPIException, NoHeadException,
NoMessageException, UnmergedPathsException,
ConcurrentRefUpdateException, WrongRepositoryStateException,
- RejectCommitException {
+ AbortedByHookException {
checkCallable();
Collections.sort(only);
- RevWalk rw = new RevWalk(repo);
-
- try {
+ try (RevWalk rw = new RevWalk(repo)) {
RepositoryState state = repo.getRepositoryState();
if (!state.canCommit())
throw new WrongRepositoryStateException(MessageFormat.format(
@@ -180,26 +177,13 @@ public class CommitCommand extends GitCommand<RevCommit> {
state.name()));
if (!noVerify) {
- final ByteArrayOutputStream errorByteArray = new ByteArrayOutputStream();
- final PrintStream hookErrRedirect = new PrintStream(
- errorByteArray);
- ProcessResult preCommitHookResult = FS.DETECTED.runIfPresent(
- repo, Hook.PRE_COMMIT, new String[0], hookOutRedirect,
- hookErrRedirect, null);
- if (preCommitHookResult.getStatus() == ProcessResult.Status.OK
- && preCommitHookResult.getExitCode() != 0) {
- String errorMessage = MessageFormat.format(
- JGitText.get().commitRejectedByHook, Hook.PRE_COMMIT.getName(),
- errorByteArray.toString());
- throw new RejectCommitException(errorMessage);
- }
+ Hooks.preCommit(repo, hookOutRedirect).call();
}
processOptions(state, rw);
- if (all && !repo.isBare() && repo.getWorkTree() != null) {
- Git git = new Git(repo);
- try {
+ if (all && !repo.isBare()) {
+ try (Git git = new Git(repo)) {
git.add()
.addFilepattern(".") //$NON-NLS-1$
.setUpdate(true).call();
@@ -231,82 +215,91 @@ public class CommitCommand extends GitCommand<RevCommit> {
parents.add(0, headId);
}
+ if (!noVerify) {
+ message = Hooks.commitMsg(repo, hookOutRedirect)
+ .setCommitMessage(message).call();
+ }
+
// lock the index
DirCache index = repo.lockDirCache();
- try {
+ try (ObjectInserter odi = repo.newObjectInserter()) {
if (!only.isEmpty())
index = createTemporaryIndex(headId, index, rw);
- ObjectInserter odi = repo.newObjectInserter();
- try {
- // Write the index as tree to the object database. This may
- // fail for example when the index contains unmerged paths
- // (unresolved conflicts)
- ObjectId indexTreeId = index.writeTree(odi);
-
- if (insertChangeId)
- insertChangeId(indexTreeId);
-
- // Create a Commit object, populate it and write it
- CommitBuilder commit = new CommitBuilder();
- commit.setCommitter(committer);
- commit.setAuthor(author);
- commit.setMessage(message);
-
- commit.setParentIds(parents);
- commit.setTreeId(indexTreeId);
- ObjectId commitId = odi.insert(commit);
- odi.flush();
-
- RevCommit revCommit = rw.parseCommit(commitId);
- RefUpdate ru = repo.updateRef(Constants.HEAD);
- ru.setNewObjectId(commitId);
- if (reflogComment != null) {
- ru.setRefLogMessage(reflogComment, false);
- } else {
- String prefix = amend ? "commit (amend): " //$NON-NLS-1$
- : parents.size() == 0 ? "commit (initial): " //$NON-NLS-1$
- : "commit: "; //$NON-NLS-1$
- ru.setRefLogMessage(
- prefix + revCommit.getShortMessage(), false);
- }
- if (headId != null)
- ru.setExpectedOldObjectId(headId);
- else
- ru.setExpectedOldObjectId(ObjectId.zeroId());
- Result rc = ru.forceUpdate();
- switch (rc) {
- case NEW:
- case FORCED:
- case FAST_FORWARD: {
- setCallable(false);
- if (state == RepositoryState.MERGING_RESOLVED
- || isMergeDuringRebase(state)) {
- // Commit was successful. Now delete the files
- // used for merge commits
- repo.writeMergeCommitMsg(null);
- repo.writeMergeHeads(null);
- } else if (state == RepositoryState.CHERRY_PICKING_RESOLVED) {
- repo.writeMergeCommitMsg(null);
- repo.writeCherryPickHead(null);
- } else if (state == RepositoryState.REVERTING_RESOLVED) {
- repo.writeMergeCommitMsg(null);
- repo.writeRevertHead(null);
- }
- return revCommit;
+ // Write the index as tree to the object database. This may
+ // fail for example when the index contains unmerged paths
+ // (unresolved conflicts)
+ ObjectId indexTreeId = index.writeTree(odi);
+
+ if (insertChangeId)
+ insertChangeId(indexTreeId);
+
+ // Check for empty commits
+ if (headId != null && !allowEmpty.booleanValue()) {
+ RevCommit headCommit = rw.parseCommit(headId);
+ headCommit.getTree();
+ if (indexTreeId.equals(headCommit.getTree())) {
+ throw new EmtpyCommitException(
+ JGitText.get().emptyCommit);
}
- case REJECTED:
- case LOCK_FAILURE:
- throw new ConcurrentRefUpdateException(
- JGitText.get().couldNotLockHEAD, ru.getRef(),
- rc);
- default:
- throw new JGitInternalException(MessageFormat.format(
- JGitText.get().updatingRefFailed,
- Constants.HEAD, commitId.toString(), rc));
+ }
+
+ // Create a Commit object, populate it and write it
+ CommitBuilder commit = new CommitBuilder();
+ commit.setCommitter(committer);
+ commit.setAuthor(author);
+ commit.setMessage(message);
+
+ commit.setParentIds(parents);
+ commit.setTreeId(indexTreeId);
+ ObjectId commitId = odi.insert(commit);
+ odi.flush();
+
+ RevCommit revCommit = rw.parseCommit(commitId);
+ RefUpdate ru = repo.updateRef(Constants.HEAD);
+ ru.setNewObjectId(commitId);
+ if (reflogComment != null) {
+ ru.setRefLogMessage(reflogComment, false);
+ } else {
+ String prefix = amend ? "commit (amend): " //$NON-NLS-1$
+ : parents.size() == 0 ? "commit (initial): " //$NON-NLS-1$
+ : "commit: "; //$NON-NLS-1$
+ ru.setRefLogMessage(prefix + revCommit.getShortMessage(),
+ false);
+ }
+ if (headId != null)
+ ru.setExpectedOldObjectId(headId);
+ else
+ ru.setExpectedOldObjectId(ObjectId.zeroId());
+ Result rc = ru.forceUpdate();
+ switch (rc) {
+ case NEW:
+ case FORCED:
+ case FAST_FORWARD: {
+ setCallable(false);
+ if (state == RepositoryState.MERGING_RESOLVED
+ || isMergeDuringRebase(state)) {
+ // Commit was successful. Now delete the files
+ // used for merge commits
+ repo.writeMergeCommitMsg(null);
+ repo.writeMergeHeads(null);
+ } else if (state == RepositoryState.CHERRY_PICKING_RESOLVED) {
+ repo.writeMergeCommitMsg(null);
+ repo.writeCherryPickHead(null);
+ } else if (state == RepositoryState.REVERTING_RESOLVED) {
+ repo.writeMergeCommitMsg(null);
+ repo.writeRevertHead(null);
}
- } finally {
- odi.release();
+ return revCommit;
+ }
+ case REJECTED:
+ case LOCK_FAILURE:
+ throw new ConcurrentRefUpdateException(
+ JGitText.get().couldNotLockHEAD, ru.getRef(), rc);
+ default:
+ throw new JGitInternalException(MessageFormat.format(
+ JGitText.get().updatingRefFailed, Constants.HEAD,
+ commitId.toString(), rc));
}
} finally {
index.unlock();
@@ -316,8 +309,6 @@ public class CommitCommand extends GitCommand<RevCommit> {
} catch (IOException e) {
throw new JGitInternalException(
JGitText.get().exceptionCaughtDuringExecutionOfCommitCommand, e);
- } finally {
- rw.dispose();
}
}
@@ -350,114 +341,123 @@ public class CommitCommand extends GitCommand<RevCommit> {
onlyProcessed = new boolean[only.size()];
boolean emptyCommit = true;
- TreeWalk treeWalk = new TreeWalk(repo);
- int dcIdx = treeWalk.addTree(new DirCacheBuildIterator(existingBuilder));
- int fIdx = treeWalk.addTree(new FileTreeIterator(repo));
- int hIdx = -1;
- if (headId != null)
- hIdx = treeWalk.addTree(rw.parseTree(headId));
- treeWalk.setRecursive(true);
-
- String lastAddedFile = null;
- while (treeWalk.next()) {
- String path = treeWalk.getPathString();
- // check if current entry's path matches a specified path
- int pos = lookupOnly(path);
-
- CanonicalTreeParser hTree = null;
- if (hIdx != -1)
- hTree = treeWalk.getTree(hIdx, CanonicalTreeParser.class);
-
- DirCacheIterator dcTree = treeWalk.getTree(dcIdx,
- DirCacheIterator.class);
-
- if (pos >= 0) {
- // include entry in commit
-
- FileTreeIterator fTree = treeWalk.getTree(fIdx,
- FileTreeIterator.class);
-
- // check if entry refers to a tracked file
- boolean tracked = dcTree != null || hTree != null;
- if (!tracked)
- break;
-
- // for an unmerged path, DirCacheBuildIterator will yield 3
- // entries, we only want to add one
- if (path.equals(lastAddedFile))
- continue;
-
- lastAddedFile = path;
-
- if (fTree != null) {
- // create a new DirCacheEntry with data retrieved from disk
- final DirCacheEntry dcEntry = new DirCacheEntry(path);
- long entryLength = fTree.getEntryLength();
- dcEntry.setLength(entryLength);
- dcEntry.setLastModified(fTree.getEntryLastModified());
- dcEntry.setFileMode(fTree.getIndexFileMode(dcTree));
-
- boolean objectExists = (dcTree != null && fTree
- .idEqual(dcTree))
- || (hTree != null && fTree.idEqual(hTree));
- if (objectExists) {
- dcEntry.setObjectId(fTree.getEntryObjectId());
- } else {
- if (FileMode.GITLINK.equals(dcEntry.getFileMode()))
+ try (TreeWalk treeWalk = new TreeWalk(repo)) {
+ treeWalk.setOperationType(OperationType.CHECKIN_OP);
+ int dcIdx = treeWalk
+ .addTree(new DirCacheBuildIterator(existingBuilder));
+ FileTreeIterator fti = new FileTreeIterator(repo);
+ fti.setDirCacheIterator(treeWalk, 0);
+ int fIdx = treeWalk.addTree(fti);
+ int hIdx = -1;
+ if (headId != null)
+ hIdx = treeWalk.addTree(rw.parseTree(headId));
+ treeWalk.setRecursive(true);
+
+ String lastAddedFile = null;
+ while (treeWalk.next()) {
+ String path = treeWalk.getPathString();
+ // check if current entry's path matches a specified path
+ int pos = lookupOnly(path);
+
+ CanonicalTreeParser hTree = null;
+ if (hIdx != -1)
+ hTree = treeWalk.getTree(hIdx, CanonicalTreeParser.class);
+
+ DirCacheIterator dcTree = treeWalk.getTree(dcIdx,
+ DirCacheIterator.class);
+
+ if (pos >= 0) {
+ // include entry in commit
+
+ FileTreeIterator fTree = treeWalk.getTree(fIdx,
+ FileTreeIterator.class);
+
+ // check if entry refers to a tracked file
+ boolean tracked = dcTree != null || hTree != null;
+ if (!tracked)
+ continue;
+
+ // for an unmerged path, DirCacheBuildIterator will yield 3
+ // entries, we only want to add one
+ if (path.equals(lastAddedFile))
+ continue;
+
+ lastAddedFile = path;
+
+ if (fTree != null) {
+ // create a new DirCacheEntry with data retrieved from
+ // disk
+ final DirCacheEntry dcEntry = new DirCacheEntry(path);
+ long entryLength = fTree.getEntryLength();
+ dcEntry.setLength(entryLength);
+ dcEntry.setLastModified(fTree.getEntryLastModified());
+ dcEntry.setFileMode(fTree.getIndexFileMode(dcTree));
+
+ boolean objectExists = (dcTree != null
+ && fTree.idEqual(dcTree))
+ || (hTree != null && fTree.idEqual(hTree));
+ if (objectExists) {
dcEntry.setObjectId(fTree.getEntryObjectId());
- else {
- // insert object
- if (inserter == null)
- inserter = repo.newObjectInserter();
- long contentLength = fTree.getEntryContentLength();
- InputStream inputStream = fTree.openEntryStream();
- try {
- dcEntry.setObjectId(inserter.insert(
- Constants.OBJ_BLOB, contentLength,
- inputStream));
- } finally {
- inputStream.close();
+ } else {
+ if (FileMode.GITLINK.equals(dcEntry.getFileMode()))
+ dcEntry.setObjectId(fTree.getEntryObjectId());
+ else {
+ // insert object
+ if (inserter == null)
+ inserter = repo.newObjectInserter();
+ long contentLength = fTree
+ .getEntryContentLength();
+ InputStream inputStream = fTree
+ .openEntryStream();
+ try {
+ dcEntry.setObjectId(inserter.insert(
+ Constants.OBJ_BLOB, contentLength,
+ inputStream));
+ } finally {
+ inputStream.close();
+ }
}
}
+
+ // add to existing index
+ existingBuilder.add(dcEntry);
+ // add to temporary in-core index
+ tempBuilder.add(dcEntry);
+
+ if (emptyCommit
+ && (hTree == null || !hTree.idEqual(fTree)
+ || hTree.getEntryRawMode() != fTree
+ .getEntryRawMode()))
+ // this is a change
+ emptyCommit = false;
+ } else {
+ // if no file exists on disk, neither add it to
+ // index nor to temporary in-core index
+
+ if (emptyCommit && hTree != null)
+ // this is a change
+ emptyCommit = false;
}
- // add to existing index
- existingBuilder.add(dcEntry);
- // add to temporary in-core index
- tempBuilder.add(dcEntry);
-
- if (emptyCommit
- && (hTree == null || !hTree.idEqual(fTree) || hTree
- .getEntryRawMode() != fTree
- .getEntryRawMode()))
- // this is a change
- emptyCommit = false;
+ // keep track of processed path
+ onlyProcessed[pos] = true;
} else {
- // if no file exists on disk, neither add it to
- // index nor to temporary in-core index
-
- if (emptyCommit && hTree != null)
- // this is a change
- emptyCommit = false;
- }
+ // add entries from HEAD for all other paths
+ if (hTree != null) {
+ // create a new DirCacheEntry with data retrieved from
+ // HEAD
+ final DirCacheEntry dcEntry = new DirCacheEntry(path);
+ dcEntry.setObjectId(hTree.getEntryObjectId());
+ dcEntry.setFileMode(hTree.getEntryFileMode());
+
+ // add to temporary in-core index
+ tempBuilder.add(dcEntry);
+ }
- // keep track of processed path
- onlyProcessed[pos] = true;
- } else {
- // add entries from HEAD for all other paths
- if (hTree != null) {
- // create a new DirCacheEntry with data retrieved from HEAD
- final DirCacheEntry dcEntry = new DirCacheEntry(path);
- dcEntry.setObjectId(hTree.getEntryObjectId());
- dcEntry.setFileMode(hTree.getEntryFileMode());
-
- // add to temporary in-core index
- tempBuilder.add(dcEntry);
+ // preserve existing entry in index
+ if (dcTree != null)
+ existingBuilder.add(dcTree.getDirCacheEntry());
}
-
- // preserve existing entry in index
- if (dcTree != null)
- existingBuilder.add(dcTree.getDirCacheEntry());
}
}
@@ -470,6 +470,8 @@ public class CommitCommand extends GitCommand<RevCommit> {
// there must be at least one change
if (emptyCommit)
+ // Would like to throw a EmptyCommitException. But this would break the API
+ // TODO(ch): Change this in the next release
throw new JGitInternalException(JGitText.get().emptyCommit);
// update index
@@ -523,6 +525,12 @@ public class CommitCommand extends GitCommand<RevCommit> {
committer = new PersonIdent(repo);
if (author == null && !amend)
author = committer;
+ if (allowEmpty == null)
+ // JGit allows empty commits by default. Only when pathes are
+ // specified the commit should not be empty. This behaviour differs
+ // from native git but can only be adapted in the next release.
+ // TODO(ch) align the defaults with native git
+ allowEmpty = (only.isEmpty()) ? Boolean.TRUE : Boolean.FALSE;
// when doing a merge commit parse MERGE_HEAD and MERGE_MSG files
if (state == RepositoryState.MERGING_RESOLVED
@@ -592,6 +600,27 @@ public class CommitCommand extends GitCommand<RevCommit> {
}
/**
+ * @param allowEmpty
+ * whether it should be allowed to create a commit which has the
+ * same tree as it's sole predecessor (a commit which doesn't
+ * change anything). By default when creating standard commits
+ * (without specifying paths) JGit allows to create such commits.
+ * When this flag is set to false an attempt to create an "empty"
+ * standard commit will lead to an EmptyCommitException.
+ * <p>
+ * By default when creating a commit containing only specified
+ * paths an attempt to create an empty commit leads to a
+ * {@link JGitInternalException}. By setting this flag to
+ * <code>true</code> this exception will not be thrown.
+ * @return {@code this}
+ * @since 4.2
+ */
+ public CommitCommand setAllowEmpty(boolean allowEmpty) {
+ this.allowEmpty = Boolean.valueOf(allowEmpty);
+ return this;
+ }
+
+ /**
* @return the commit message used for the <code>commit</code>
*/
public String getMessage() {
@@ -694,7 +723,7 @@ public class CommitCommand extends GitCommand<RevCommit> {
*/
public CommitCommand setAll(boolean all) {
checkCallable();
- if (!only.isEmpty())
+ if (all && !only.isEmpty())
throw new JGitInternalException(MessageFormat.format(
JGitText.get().illegalCombinationOfArguments, "--all", //$NON-NLS-1$
"--only")); //$NON-NLS-1$
@@ -771,9 +800,9 @@ public class CommitCommand extends GitCommand<RevCommit> {
/**
* Sets the {@link #noVerify} option on this commit command.
* <p>
- * Both the {@link Hook#PRE_COMMIT pre-commit} and {@link Hook#COMMIT_MSG
- * commit-msg} hooks can block a commit by their return value; setting this
- * option to <code>true</code> will bypass these two hooks.
+ * Both the pre-commit and commit-msg hooks can block a commit by their
+ * return value; setting this option to <code>true</code> will bypass these
+ * two hooks.
* </p>
*
* @param noVerify
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CreateBranchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CreateBranchCommand.java
index a4ad2c9..a664660 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CreateBranchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CreateBranchCommand.java
@@ -124,8 +124,7 @@ public class CreateBranchCommand extends GitCommand<Ref> {
RefNotFoundException, InvalidRefNameException {
checkCallable();
processOptions();
- RevWalk revWalk = new RevWalk(repo);
- try {
+ try (RevWalk revWalk = new RevWalk(repo)) {
Ref refToCheck = repo.getRef(name);
boolean exists = refToCheck != null
&& refToCheck.getName().startsWith(Constants.R_HEADS);
@@ -270,8 +269,6 @@ public class CreateBranchCommand extends GitCommand<Ref> {
return result;
} catch (IOException ioe) {
throw new JGitInternalException(ioe.getMessage(), ioe);
- } finally {
- revWalk.release();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteBranchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteBranchCommand.java
index 30b27f2..61beb2f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteBranchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DeleteBranchCommand.java
@@ -108,18 +108,21 @@ public class DeleteBranchCommand extends GitCommand<List<String>> {
if (!force) {
// check if the branches to be deleted
// are all merged into the current branch
- RevWalk walk = new RevWalk(repo);
- RevCommit tip = walk.parseCommit(repo.resolve(Constants.HEAD));
- for (String branchName : branchNames) {
- if (branchName == null)
- continue;
- Ref currentRef = repo.getRef(branchName);
- if (currentRef == null)
- continue;
+ try (RevWalk walk = new RevWalk(repo)) {
+ RevCommit tip = walk
+ .parseCommit(repo.resolve(Constants.HEAD));
+ for (String branchName : branchNames) {
+ if (branchName == null)
+ continue;
+ Ref currentRef = repo.getRef(branchName);
+ if (currentRef == null)
+ continue;
- RevCommit base = walk.parseCommit(repo.resolve(branchName));
- if (!walk.isMergedInto(base, tip)) {
- throw new NotMergedException();
+ RevCommit base = walk
+ .parseCommit(repo.resolve(branchName));
+ if (!walk.isMergedInto(base, tip)) {
+ throw new NotMergedException();
+ }
}
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
index 08e41e4..be45666 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
@@ -89,6 +89,11 @@ public class DescribeCommand extends GitCommand<String> {
private int maxCandidates = 10;
/**
+ * Whether to always use long output format or not.
+ */
+ private boolean longDesc;
+
+ /**
*
* @param repo
*/
@@ -139,6 +144,32 @@ public class DescribeCommand extends GitCommand<String> {
}
/**
+ * Determine whether always to use the long format or not. When set to
+ * <code>true</code> the long format is used even the commit matches a tag.
+ *
+ * @param longDesc
+ * <code>true</code> if always the long format should be used.
+ * @return {@code this}
+ *
+ * @see <a
+ * href="https://www.kernel.org/pub/software/scm/git/docs/git-describe.html"
+ * >Git documentation about describe</a>
+ * @since 4.0
+ */
+ public DescribeCommand setLong(boolean longDesc) {
+ this.longDesc = longDesc;
+ return this;
+ }
+
+ private String longDescription(Ref tag, int depth, ObjectId tip)
+ throws IOException {
+ return String.format(
+ "%s-%d-g%s", tag.getName().substring(R_TAGS.length()), //$NON-NLS-1$
+ Integer.valueOf(depth), w.getObjectReader().abbreviate(tip)
+ .name());
+ }
+
+ /**
* Describes the specified commit. Target defaults to HEAD if no commit was
* set explicitly.
*
@@ -205,16 +236,18 @@ public class DescribeCommand extends GitCommand<String> {
}
String describe(ObjectId tip) throws IOException {
- return String.format("%s-%d-g%s", tag.getName().substring(R_TAGS.length()), //$NON-NLS-1$
- Integer.valueOf(depth), w.getObjectReader().abbreviate(tip).name());
+ return longDescription(tag, depth, tip);
}
+
}
List<Candidate> candidates = new ArrayList<Candidate>(); // all the candidates we find
// is the target already pointing to a tag? if so, we are done!
Ref lucky = tags.get(target);
- if (lucky != null)
- return lucky.getName().substring(R_TAGS.length());
+ if (lucky != null) {
+ return longDesc ? longDescription(lucky, 0, target) : lucky
+ .getName().substring(R_TAGS.length());
+ }
w.markStart(target);
@@ -282,7 +315,7 @@ public class DescribeCommand extends GitCommand<String> {
throw new JGitInternalException(e.getMessage(), e);
} finally {
setCallable(false);
- w.release();
+ w.close();
}
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DiffCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DiffCommand.java
index f31198f..3e3a7a8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DiffCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DiffCommand.java
@@ -124,11 +124,8 @@ public class DiffCommand extends GitCommand<List<DiffEntry>> {
if (head == null)
throw new NoHeadException(JGitText.get().cannotReadTree);
CanonicalTreeParser p = new CanonicalTreeParser();
- ObjectReader reader = repo.newObjectReader();
- try {
+ try (ObjectReader reader = repo.newObjectReader()) {
p.reset(reader, head);
- } finally {
- reader.release();
}
oldTree = p;
}
@@ -159,7 +156,7 @@ public class DiffCommand extends GitCommand<List<DiffEntry>> {
} catch (IOException e) {
throw new JGitInternalException(e.getMessage(), e);
} finally {
- diffFmt.release();
+ diffFmt.close();
}
}
@@ -271,7 +268,10 @@ public class DiffCommand extends GitCommand<List<DiffEntry>> {
* @return this instance
*/
public DiffCommand setProgressMonitor(ProgressMonitor monitor) {
+ if (monitor == null) {
+ monitor = NullProgressMonitor.INSTANCE;
+ }
this.monitor = monitor;
return this;
}
-}
\ No newline at end of file
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
index 29d475a..de51276 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
@@ -116,22 +116,17 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
org.eclipse.jgit.api.errors.TransportException {
checkCallable();
- try {
- Transport transport = Transport.open(repo, remote);
- try {
- transport.setCheckFetchedObjects(checkFetchedObjects);
- transport.setRemoveDeletedRefs(isRemoveDeletedRefs());
- transport.setDryRun(dryRun);
- if (tagOption != null)
- transport.setTagOpt(tagOption);
- transport.setFetchThin(thin);
- configure(transport);
-
- FetchResult result = transport.fetch(monitor, refSpecs);
- return result;
- } finally {
- transport.close();
- }
+ try (Transport transport = Transport.open(repo, remote)) {
+ transport.setCheckFetchedObjects(checkFetchedObjects);
+ transport.setRemoveDeletedRefs(isRemoveDeletedRefs());
+ transport.setDryRun(dryRun);
+ if (tagOption != null)
+ transport.setTagOpt(tagOption);
+ transport.setFetchThin(thin);
+ configure(transport);
+
+ FetchResult result = transport.fetch(monitor, refSpecs);
+ return result;
} catch (NoRemoteRepositoryException e) {
throw new InvalidRemoteException(MessageFormat.format(
JGitText.get().invalidRemote, remote), e);
@@ -244,6 +239,9 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
*/
public FetchCommand setProgressMonitor(ProgressMonitor monitor) {
checkCallable();
+ if (monitor == null) {
+ monitor = NullProgressMonitor.INSTANCE;
+ }
this.monitor = monitor;
return this;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
index 67e9a25..2cd5f59 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
@@ -82,10 +82,12 @@ import org.eclipse.jgit.util.FS;
* methods in this class may for example offer too much functionality or they
* offer the functionality with the wrong arguments.
*/
-public class Git {
+public class Git implements AutoCloseable {
/** The git repository this class is interacting with */
private final Repository repo;
+ private final boolean closeRepo;
+
/**
* @param dir
* the repository to open. May be either the GIT_DIR, or the
@@ -103,44 +105,50 @@ public class Git {
* working tree directory that contains {@code .git}.
* @param fs
* filesystem abstraction to use when accessing the repository.
- * @return a {@link Git} object for the existing git repository
+ * @return a {@link Git} object for the existing git repository. Closing this
+ * instance will close the repo.
* @throws IOException
*/
public static Git open(File dir, FS fs) throws IOException {
RepositoryCache.FileKey key;
key = RepositoryCache.FileKey.lenient(dir, fs);
- return wrap(new RepositoryBuilder().setFS(fs).setGitDir(key.getFile())
- .setMustExist(true).build());
+ Repository db = new RepositoryBuilder().setFS(fs).setGitDir(key.getFile())
+ .setMustExist(true).build();
+ return new Git(db, true);
}
/**
* @param repo
- * the git repository this class is interacting with.
- * {@code null} is not allowed
- * @return a {@link Git} object for the existing git repository
+ * the git repository this class is interacting with;
+ * {@code null} is not allowed.
+ * @return a {@link Git} object for the existing git repository. The caller is
+ * responsible for closing the repository; {@link #close()} on this
+ * instance does not close the repo.
*/
public static Git wrap(Repository repo) {
return new Git(repo);
}
/**
- * Frees resources held by the underlying {@link Repository} instance. It is
- * recommended to call this method as soon as you don't need a reference to
- * this {@link Git} instance and the underlying {@link Repository} instance
- * anymore. This method closes the underlying object and ref databases. This
- * will free memory and file handles. E.g. on Windows the repository will
- * keep file handles on pack files unless you call this method. Such open
- * file handles may for example prevent that the repository folder in the
- * filesystem can be deleted.
+ * Frees resources associated with this instance.
+ * <p>
+ * If the repository was opened by a static factory method in this class, then
+ * this method calls {@link Repository#close()} on the underlying repository
+ * instance. (Whether this actually releases underlying resources, such as
+ * file handles, may vary; see {@link Repository} for more details.)
* <p>
- * After calling close() you should not use this {@link Git} instance and
- * the underlying {@link Repository} instance anymore.
+ * If the repository was created by a caller and passed into {@link
+ * #Git(Repository)} or a static factory method in this class, then this
+ * method does not call close on the underlying repository.
+ * <p>
+ * In all cases, after calling this method you should not use this {@link Git}
+ * instance anymore.
*
* @since 3.2
*/
public void close() {
- if (repo != null)
+ if (closeRepo)
repo.close();
}
@@ -183,17 +191,27 @@ public class Git {
/**
* Constructs a new {@link Git} object which can interact with the specified
- * git repository. All command classes returned by methods of this class
- * will always interact with this git repository.
+ * git repository.
+ * <p>
+ * All command classes returned by methods of this class will always interact
+ * with this git repository.
+ * <p>
+ * The caller is responsible for closing the repository; {@link #close()} on
+ * this instance does not close the repo.
*
* @param repo
- * the git repository this class is interacting with.
- * {@code null} is not allowed
+ * the git repository this class is interacting with;
+ * {@code null} is not allowed.
*/
public Git(Repository repo) {
+ this(repo, false);
+ }
+
+ Git(Repository repo, boolean closeRepo) {
if (repo == null)
throw new NullPointerException();
this.repo = repo;
+ this.closeRepo = closeRepo;
}
/**
@@ -695,7 +713,48 @@ public class Git {
}
/**
- * @return the git repository this class is interacting with
+ * Return a command used to list the available remotes.
+ *
+ * @return a {@link RemoteListCommand}
+ * @since 4.2
+ */
+ public RemoteListCommand remoteList() {
+ return new RemoteListCommand(repo);
+ }
+
+ /**
+ * Return a command used to add a new remote.
+ *
+ * @return a {@link RemoteAddCommand}
+ * @since 4.2
+ */
+ public RemoteAddCommand remoteAdd() {
+ return new RemoteAddCommand(repo);
+ }
+
+ /**
+ * Return a command used to remove an existing remote.
+ *
+ * @return a {@link RemoteRemoveCommand}
+ * @since 4.2
+ */
+ public RemoteRemoveCommand remoteRemove() {
+ return new RemoteRemoveCommand(repo);
+ }
+
+ /**
+ * Return a command used to change the URL of an existing remote.
+ *
+ * @return a {@link RemoteSetUrlCommand}
+ * @since 4.2
+ */
+ public RemoteSetUrlCommand remoteSetUrl() {
+ return new RemoteSetUrlCommand(repo);
+ }
+
+ /**
+ * @return the git repository this class is interacting with; see
+ * {@link #close()} for notes on closing this repository.
*/
public Repository getRepository() {
return repo;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java
index 10f33de..904c74f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListBranchCommand.java
@@ -139,8 +139,7 @@ public class ListBranchCommand extends GitCommand<List<Ref>> {
if (containsCommitish == null)
return refs;
- RevWalk walk = new RevWalk(repo);
- try {
+ try (RevWalk walk = new RevWalk(repo)) {
ObjectId resolved = repo.resolve(containsCommitish);
if (resolved == null)
throw new RefNotFoundException(MessageFormat.format(
@@ -149,8 +148,6 @@ public class ListBranchCommand extends GitCommand<List<Ref>> {
RevCommit containsCommit = walk.parseCommit(resolved);
return RevWalkUtils.findBranchesReachableFrom(containsCommit, walk,
refs);
- } finally {
- walk.release();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListNotesCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListNotesCommand.java
index 84fa355..796ac79 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListNotesCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListNotesCommand.java
@@ -80,9 +80,8 @@ public class ListNotesCommand extends GitCommand<List<Note>> {
public List<Note> call() throws GitAPIException {
checkCallable();
List<Note> notes = new ArrayList<Note>();
- RevWalk walk = new RevWalk(repo);
NoteMap map = NoteMap.newEmptyMap();
- try {
+ try (RevWalk walk = new RevWalk(repo)) {
Ref ref = repo.getRef(notesRef);
// if we have a notes ref, use it
if (ref != null) {
@@ -95,8 +94,6 @@ public class ListNotesCommand extends GitCommand<List<Note>> {
notes.add(i.next());
} catch (IOException e) {
throw new JGitInternalException(e.getMessage(), e);
- } finally {
- walk.release();
}
return notes;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java
index a0a5d95..a3b701b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java
@@ -78,16 +78,13 @@ public class ListTagCommand extends GitCommand<List<Ref>> {
checkCallable();
Map<String, Ref> refList;
List<Ref> tags = new ArrayList<Ref>();
- RevWalk revWalk = new RevWalk(repo);
- try {
+ try (RevWalk revWalk = new RevWalk(repo)) {
refList = repo.getRefDatabase().getRefs(Constants.R_TAGS);
for (Ref ref : refList.values()) {
tags.add(ref);
}
} catch (IOException e) {
throw new JGitInternalException(e.getMessage(), e);
- } finally {
- revWalk.release();
}
Collections.sort(tags, new Comparator<Ref>() {
public int compare(Ref o1, Ref o2) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/LsRemoteCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/LsRemoteCommand.java
index 3363a0f..f3527fd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/LsRemoteCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/LsRemoteCommand.java
@@ -182,13 +182,9 @@ public class LsRemoteCommand extends
org.eclipse.jgit.api.errors.TransportException {
checkCallable();
- Transport transport = null;
- FetchConnection fc = null;
- try {
- if (repo != null)
- transport = Transport.open(repo, remote);
- else
- transport = Transport.open(new URIish(remote));
+ try (Transport transport = repo != null
+ ? Transport.open(repo, remote)
+ : Transport.open(new URIish(remote))) {
transport.setOptionUploadPack(uploadPack);
configure(transport);
Collection<RefSpec> refSpecs = new ArrayList<RefSpec>(1);
@@ -199,19 +195,20 @@ public class LsRemoteCommand extends
refSpecs.add(new RefSpec("refs/heads/*:refs/remotes/origin/*")); //$NON-NLS-1$
Collection<Ref> refs;
Map<String, Ref> refmap = new HashMap<String, Ref>();
- fc = transport.openFetch();
- refs = fc.getRefs();
- if (refSpecs.isEmpty())
- for (Ref r : refs)
- refmap.put(r.getName(), r);
- else
- for (Ref r : refs)
- for (RefSpec rs : refSpecs)
- if (rs.matchSource(r)) {
- refmap.put(r.getName(), r);
- break;
- }
- return refmap;
+ try (FetchConnection fc = transport.openFetch()) {
+ refs = fc.getRefs();
+ if (refSpecs.isEmpty())
+ for (Ref r : refs)
+ refmap.put(r.getName(), r);
+ else
+ for (Ref r : refs)
+ for (RefSpec rs : refSpecs)
+ if (rs.matchSource(r)) {
+ refmap.put(r.getName(), r);
+ break;
+ }
+ return refmap;
+ }
} catch (URISyntaxException e) {
throw new InvalidRemoteException(MessageFormat.format(
JGitText.get().invalidRemote, remote));
@@ -223,11 +220,6 @@ public class LsRemoteCommand extends
throw new org.eclipse.jgit.api.errors.TransportException(
e.getMessage(),
e);
- } finally {
- if (fc != null)
- fc.close();
- if (transport != null)
- transport.close();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
index 83026a0..bfe90a3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2010, Christian Halstrick <christian.halstrick at sap.com>
* Copyright (C) 2010-2014, Stefan Lay <stefan.lay at sap.com>
+ * Copyright (C) 2016, Laurent Delaigue <laurent.delaigue at obeo.fr>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -65,8 +66,10 @@ import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Config.ConfigEnum;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Ref.Storage;
import org.eclipse.jgit.lib.RefUpdate;
@@ -106,6 +109,8 @@ public class MergeCommand extends GitCommand<MergeResult> {
private String message;
+ private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
+
/**
* The modes available for fast forward merges corresponding to the
* <code>--ff</code>, <code>--no-ff</code> and <code>--ff-only</code>
@@ -330,6 +335,7 @@ public class MergeCommand extends GitCommand<MergeResult> {
repo.writeSquashCommitMsg(squashMessage);
}
Merger merger = mergeStrategy.newMerger(repo);
+ merger.setProgressMonitor(monitor);
boolean noProblems;
Map<String, org.eclipse.jgit.merge.MergeResult<?>> lowLevelResults = null;
Map<String, MergeFailureReason> failingPaths = null;
@@ -369,9 +375,11 @@ public class MergeCommand extends GitCommand<MergeResult> {
mergeStatus = MergeStatus.MERGED_NOT_COMMITTED;
}
if (commit && !squash) {
- newHeadId = new Git(getRepository()).commit()
- .setReflogComment(refLogMessage.toString())
- .call().getId();
+ try (Git git = new Git(getRepository())) {
+ newHeadId = git.commit()
+ .setReflogComment(refLogMessage.toString())
+ .call().getId();
+ }
mergeStatus = MergeStatus.MERGED;
}
if (commit && squash) {
@@ -416,7 +424,7 @@ public class MergeCommand extends GitCommand<MergeResult> {
e), e);
} finally {
if (revWalk != null)
- revWalk.release();
+ revWalk.close();
}
}
@@ -584,4 +592,23 @@ public class MergeCommand extends GitCommand<MergeResult> {
this.message = message;
return this;
}
+
+ /**
+ * The progress monitor associated with the diff operation. By default, this
+ * is set to <code>NullProgressMonitor</code>
+ *
+ * @see NullProgressMonitor
+ *
+ * @param monitor
+ * A progress monitor
+ * @return this instance
+ * @since 4.2
+ */
+ public MergeCommand setProgressMonitor(ProgressMonitor monitor) {
+ if (monitor == null) {
+ monitor = NullProgressMonitor.INSTANCE;
+ }
+ this.monitor = monitor;
+ return this;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java
index 9dc33b5..6141e0c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeResult.java
@@ -69,7 +69,7 @@ public class MergeResult {
FAST_FORWARD {
@Override
public String toString() {
- return "Fast-forward";
+ return "Fast-forward"; //$NON-NLS-1$
}
@Override
@@ -83,7 +83,7 @@ public class MergeResult {
FAST_FORWARD_SQUASHED {
@Override
public String toString() {
- return "Fast-forward-squashed";
+ return "Fast-forward-squashed"; //$NON-NLS-1$
}
@Override
@@ -95,7 +95,7 @@ public class MergeResult {
ALREADY_UP_TO_DATE {
@Override
public String toString() {
- return "Already-up-to-date";
+ return "Already-up-to-date"; //$NON-NLS-1$
}
@Override
@@ -107,7 +107,7 @@ public class MergeResult {
FAILED {
@Override
public String toString() {
- return "Failed";
+ return "Failed"; //$NON-NLS-1$
}
@Override
@@ -119,7 +119,7 @@ public class MergeResult {
MERGED {
@Override
public String toString() {
- return "Merged";
+ return "Merged"; //$NON-NLS-1$
}
@Override
@@ -133,7 +133,7 @@ public class MergeResult {
MERGED_SQUASHED {
@Override
public String toString() {
- return "Merged-squashed";
+ return "Merged-squashed"; //$NON-NLS-1$
}
@Override
@@ -147,7 +147,7 @@ public class MergeResult {
MERGED_SQUASHED_NOT_COMMITTED {
@Override
public String toString() {
- return "Merged-squashed-not-committed";
+ return "Merged-squashed-not-committed"; //$NON-NLS-1$
}
@Override
@@ -159,7 +159,7 @@ public class MergeResult {
CONFLICTING {
@Override
public String toString() {
- return "Conflicting";
+ return "Conflicting"; //$NON-NLS-1$
}
@Override
@@ -173,7 +173,7 @@ public class MergeResult {
ABORTED {
@Override
public String toString() {
- return "Aborted";
+ return "Aborted"; //$NON-NLS-1$
}
@Override
@@ -186,7 +186,7 @@ public class MergeResult {
**/
MERGED_NOT_COMMITTED {
public String toString() {
- return "Merged-not-committed";
+ return "Merged-not-committed"; //$NON-NLS-1$
}
@Override
@@ -198,7 +198,7 @@ public class MergeResult {
NOT_SUPPORTED {
@Override
public String toString() {
- return "Not-yet-supported";
+ return "Not-yet-supported"; //$NON-NLS-1$
}
@Override
@@ -213,7 +213,7 @@ public class MergeResult {
*/
CHECKOUT_CONFLICT {
public String toString() {
- return "Checkout Conflict";
+ return "Checkout Conflict"; //$NON-NLS-1$
}
@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/NameRevCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/NameRevCommand.java
index cce42fc..fd28d0e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/NameRevCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/NameRevCommand.java
@@ -185,11 +185,11 @@ public class NameRevCommand extends GitCommand<Map<ObjectId, String>> {
}
setCallable(false);
- walk.release();
return result;
} catch (IOException e) {
- walk.reset();
throw new JGitInternalException(e.getMessage(), e);
+ } finally {
+ walk.close();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
index cdaf3ec..549ef6c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2010, Christian Halstrick <christian.halstrick at sap.com>
* Copyright (C) 2010, Mathias Kinzler <mathias.kinzler at sap.com>
+ * Copyright (C) 2016, Laurent Delaigue <laurent.delaigue at obeo.fr>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -54,6 +55,7 @@ import org.eclipse.jgit.api.errors.InvalidConfigurationException;
import org.eclipse.jgit.api.errors.InvalidRemoteException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.NoHeadException;
+import org.eclipse.jgit.api.errors.RefNotAdvertisedException;
import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
import org.eclipse.jgit.internal.JGitText;
@@ -129,6 +131,9 @@ public class PullCommand extends TransportCommand<PullCommand, PullResult> {
* @return this instance
*/
public PullCommand setProgressMonitor(ProgressMonitor monitor) {
+ if (monitor == null) {
+ monitor = NullProgressMonitor.INSTANCE;
+ }
this.monitor = monitor;
return this;
}
@@ -171,6 +176,7 @@ public class PullCommand extends TransportCommand<PullCommand, PullResult> {
* @throws InvalidRemoteException
* @throws CanceledException
* @throws RefNotFoundException
+ * @throws RefNotAdvertisedException
* @throws NoHeadException
* @throws org.eclipse.jgit.api.errors.TransportException
* @throws GitAPIException
@@ -178,7 +184,7 @@ public class PullCommand extends TransportCommand<PullCommand, PullResult> {
public PullResult call() throws GitAPIException,
WrongRepositoryStateException, InvalidConfigurationException,
DetachedHeadException, InvalidRemoteException, CanceledException,
- RefNotFoundException, NoHeadException,
+ RefNotFoundException, RefNotAdvertisedException, NoHeadException,
org.eclipse.jgit.api.errors.TransportException {
checkCallable();
@@ -261,7 +267,7 @@ public class PullCommand extends TransportCommand<PullCommand, PullResult> {
fetchRes = fetch.call();
} else {
// we can skip the fetch altogether
- remoteUri = "local repository";
+ remoteUri = JGitText.get().localRepository;
fetchRes = null;
}
@@ -284,11 +290,13 @@ public class PullCommand extends TransportCommand<PullCommand, PullResult> {
r = fetchRes.getAdvertisedRef(Constants.R_HEADS
+ remoteBranchName);
}
- if (r == null)
- throw new JGitInternalException(MessageFormat.format(JGitText
- .get().couldNotGetAdvertisedRef, remoteBranchName));
- else
+ if (r == null) {
+ throw new RefNotAdvertisedException(MessageFormat.format(
+ JGitText.get().couldNotGetAdvertisedRef, remote,
+ remoteBranchName));
+ } else {
commitToMerge = r.getObjectId();
+ }
} else {
try {
commitToMerge = repo.resolve(remoteBranchName);
@@ -302,9 +310,9 @@ public class PullCommand extends TransportCommand<PullCommand, PullResult> {
}
}
- String upstreamName = "branch \'"
- + Repository.shortenRefName(remoteBranchName) + "\' of "
- + remoteUri;
+ String upstreamName = MessageFormat.format(
+ JGitText.get().upstreamBranchName,
+ Repository.shortenRefName(remoteBranchName), remoteUri);
PullResult result;
if (pullRebaseMode.rebase) {
@@ -319,6 +327,7 @@ public class PullCommand extends TransportCommand<PullCommand, PullResult> {
MergeCommand merge = new MergeCommand(repo);
merge.include(upstreamName, commitToMerge);
merge.setStrategy(strategy);
+ merge.setProgressMonitor(monitor);
MergeResult mergeRes = merge.call();
monitor.update(1);
result = new PullResult(fetchRes, remote, mergeRes);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java
index c719f0a..f5b82bd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java
@@ -55,6 +55,7 @@ import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.InvalidRemoteException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.errors.NotSupportedException;
+import org.eclipse.jgit.errors.TooLargePackException;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
@@ -88,9 +89,8 @@ public class PushCommand extends
private String receivePack = RemoteConfig.DEFAULT_RECEIVE_PACK;
private boolean dryRun;
-
+ private boolean atomic;
private boolean force;
-
private boolean thin = Transport.DEFAULT_PUSH_THIN;
private OutputStream out;
@@ -144,6 +144,7 @@ public class PushCommand extends
transports = Transport.openAll(repo, remote, Transport.Operation.PUSH);
for (final Transport transport : transports) {
transport.setPushThin(thin);
+ transport.setPushAtomic(atomic);
if (receivePack != null)
transport.setOptionReceivePack(receivePack);
transport.setDryRun(dryRun);
@@ -156,6 +157,9 @@ public class PushCommand extends
PushResult result = transport.push(monitor, toPush, out);
pushResults.add(result);
+ } catch (TooLargePackException e) {
+ throw new org.eclipse.jgit.api.errors.TooLargePackException(
+ e.getMessage(), e);
} catch (TransportException e) {
throw new org.eclipse.jgit.api.errors.TransportException(
e.getMessage(), e);
@@ -253,6 +257,9 @@ public class PushCommand extends
*/
public PushCommand setProgressMonitor(ProgressMonitor monitor) {
checkCallable();
+ if (monitor == null) {
+ monitor = NullProgressMonitor.INSTANCE;
+ }
this.monitor = monitor;
return this;
}
@@ -390,6 +397,29 @@ public class PushCommand extends
}
/**
+ * @return true if all-or-nothing behavior is requested.
+ * @since 4.2
+ */
+ public boolean isAtomic() {
+ return atomic;
+ }
+
+ /**
+ * Requests atomic push (all references updated, or no updates).
+ *
+ * Default setting is false.
+ *
+ * @param atomic
+ * @return {@code this}
+ * @since 4.2
+ */
+ public PushCommand setAtomic(boolean atomic) {
+ checkCallable();
+ this.atomic = atomic;
+ return this;
+ }
+
+ /**
* @return the force preference for push operation
*/
public boolean isForce() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
index 7d3e823..643ec7a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2010, 2013 Mathias Kinzler <mathias.kinzler at sap.com>
+ * Copyright (C) 2016, Laurent Delaigue <laurent.delaigue at obeo.fr>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -405,11 +406,12 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
.call();
} catch (StashApplyFailureException e) {
conflicts = true;
- RevWalk rw = new RevWalk(repo);
- ObjectId stashId = repo.resolve(stash);
- RevCommit commit = rw.parseCommit(stashId);
- updateStashRef(commit, commit.getAuthorIdent(),
- commit.getShortMessage());
+ try (RevWalk rw = new RevWalk(repo)) {
+ ObjectId stashId = repo.resolve(stash);
+ RevCommit commit = rw.parseCommit(stashId);
+ updateStashRef(commit, commit.getAuthorIdent(),
+ commit.getShortMessage());
+ }
}
}
return conflicts;
@@ -444,7 +446,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
Collection<ObjectId> ids = or.resolve(step.getCommit());
if (ids.size() != 1)
throw new JGitInternalException(
- "Could not resolve uniquely the abbreviated object ID");
+ JGitText.get().cannotResolveUniquelyAbbrevObjectId);
RevCommit commitToPick = walk.parseCommit(ids.iterator().next());
if (shouldPick) {
if (monitor.isCancelled())
@@ -518,21 +520,23 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
// here we should skip this step in order to avoid
// confusing pseudo-changed
String ourCommitName = getOurCommitName();
- CherryPickResult cherryPickResult = new Git(repo).cherryPick()
+ try (Git git = new Git(repo)) {
+ CherryPickResult cherryPickResult = git.cherryPick()
.include(commitToPick).setOurCommitName(ourCommitName)
.setReflogPrefix(REFLOG_PREFIX).setStrategy(strategy)
.call();
- switch (cherryPickResult.getStatus()) {
- case FAILED:
- if (operation == Operation.BEGIN)
- return abort(RebaseResult.failed(cherryPickResult
- .getFailingPaths()));
- else
+ switch (cherryPickResult.getStatus()) {
+ case FAILED:
+ if (operation == Operation.BEGIN)
+ return abort(RebaseResult
+ .failed(cherryPickResult.getFailingPaths()));
+ else
+ return stop(commitToPick, Status.STOPPED);
+ case CONFLICTING:
return stop(commitToPick, Status.STOPPED);
- case CONFLICTING:
- return stop(commitToPick, Status.STOPPED);
- case OK:
- newHead = cherryPickResult.getNewHead();
+ case OK:
+ newHead = cherryPickResult.getNewHead();
+ }
}
}
return null;
@@ -557,68 +561,76 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
lastStepWasForward = newHead != null;
if (!lastStepWasForward) {
ObjectId headId = getHead().getObjectId();
+ // getHead() checks for null
+ assert headId != null;
if (!AnyObjectId.equals(headId, newParents.get(0)))
checkoutCommit(headId.getName(), newParents.get(0));
// Use the cherry-pick strategy if all non-first parents did not
// change. This is different from C Git, which always uses the merge
// strategy (see below).
- if (otherParentsUnchanged) {
- boolean isMerge = commitToPick.getParentCount() > 1;
- String ourCommitName = getOurCommitName();
- CherryPickCommand pickCommand = new Git(repo).cherryPick()
- .include(commitToPick).setOurCommitName(ourCommitName)
- .setReflogPrefix(REFLOG_PREFIX).setStrategy(strategy);
- if (isMerge) {
- pickCommand.setMainlineParentNumber(1);
- // We write a MERGE_HEAD and later commit explicitly
- pickCommand.setNoCommit(true);
- writeMergeInfo(commitToPick, newParents);
- }
- CherryPickResult cherryPickResult = pickCommand.call();
- switch (cherryPickResult.getStatus()) {
- case FAILED:
- if (operation == Operation.BEGIN)
- return abort(RebaseResult.failed(cherryPickResult
- .getFailingPaths()));
- else
- return stop(commitToPick, Status.STOPPED);
- case CONFLICTING:
- return stop(commitToPick, Status.STOPPED);
- case OK:
+ try (Git git = new Git(repo)) {
+ if (otherParentsUnchanged) {
+ boolean isMerge = commitToPick.getParentCount() > 1;
+ String ourCommitName = getOurCommitName();
+ CherryPickCommand pickCommand = git.cherryPick()
+ .include(commitToPick)
+ .setOurCommitName(ourCommitName)
+ .setReflogPrefix(REFLOG_PREFIX)
+ .setStrategy(strategy);
if (isMerge) {
- // Commit the merge (setup above using writeMergeInfo())
- CommitCommand commit = new Git(repo).commit();
+ pickCommand.setMainlineParentNumber(1);
+ // We write a MERGE_HEAD and later commit explicitly
+ pickCommand.setNoCommit(true);
+ writeMergeInfo(commitToPick, newParents);
+ }
+ CherryPickResult cherryPickResult = pickCommand.call();
+ switch (cherryPickResult.getStatus()) {
+ case FAILED:
+ if (operation == Operation.BEGIN)
+ return abort(RebaseResult.failed(
+ cherryPickResult.getFailingPaths()));
+ else
+ return stop(commitToPick, Status.STOPPED);
+ case CONFLICTING:
+ return stop(commitToPick, Status.STOPPED);
+ case OK:
+ if (isMerge) {
+ // Commit the merge (setup above using
+ // writeMergeInfo())
+ CommitCommand commit = git.commit();
+ commit.setAuthor(commitToPick.getAuthorIdent());
+ commit.setReflogComment(REFLOG_PREFIX + " " //$NON-NLS-1$
+ + commitToPick.getShortMessage());
+ newHead = commit.call();
+ } else
+ newHead = cherryPickResult.getNewHead();
+ break;
+ }
+ } else {
+ // Use the merge strategy to redo merges, which had some of
+ // their non-first parents rewritten
+ MergeCommand merge = git.merge()
+ .setFastForward(MergeCommand.FastForwardMode.NO_FF)
+ .setProgressMonitor(monitor)
+ .setCommit(false);
+ for (int i = 1; i < commitToPick.getParentCount(); i++)
+ merge.include(newParents.get(i));
+ MergeResult mergeResult = merge.call();
+ if (mergeResult.getMergeStatus().isSuccessful()) {
+ CommitCommand commit = git.commit();
commit.setAuthor(commitToPick.getAuthorIdent());
+ commit.setMessage(commitToPick.getFullMessage());
commit.setReflogComment(REFLOG_PREFIX + " " //$NON-NLS-1$
+ commitToPick.getShortMessage());
newHead = commit.call();
- } else
- newHead = cherryPickResult.getNewHead();
- break;
- }
- } else {
- // Use the merge strategy to redo merges, which had some of
- // their non-first parents rewritten
- MergeCommand merge = new Git(repo).merge()
- .setFastForward(MergeCommand.FastForwardMode.NO_FF)
- .setCommit(false);
- for (int i = 1; i < commitToPick.getParentCount(); i++)
- merge.include(newParents.get(i));
- MergeResult mergeResult = merge.call();
- if (mergeResult.getMergeStatus().isSuccessful()) {
- CommitCommand commit = new Git(repo).commit();
- commit.setAuthor(commitToPick.getAuthorIdent());
- commit.setMessage(commitToPick.getFullMessage());
- commit.setReflogComment(REFLOG_PREFIX + " " //$NON-NLS-1$
- + commitToPick.getShortMessage());
- newHead = commit.call();
- } else {
- if (operation == Operation.BEGIN
- && mergeResult.getMergeStatus() == MergeResult.MergeStatus.FAILED)
- return abort(RebaseResult.failed(mergeResult
- .getFailingPaths()));
- return stop(commitToPick, Status.STOPPED);
+ } else {
+ if (operation == Operation.BEGIN && mergeResult
+ .getMergeStatus() == MergeResult.MergeStatus.FAILED)
+ return abort(RebaseResult
+ .failed(mergeResult.getFailingPaths()));
+ return stop(commitToPick, Status.STOPPED);
+ }
}
}
}
@@ -660,12 +672,15 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
}
private void writeRewrittenHashes() throws RevisionSyntaxException,
- IOException {
+ IOException, RefNotFoundException {
File currentCommitFile = rebaseState.getFile(CURRENT_COMMIT);
if (!currentCommitFile.exists())
return;
- String head = repo.resolve(Constants.HEAD).getName();
+ ObjectId headId = getHead().getObjectId();
+ // getHead() checks for null
+ assert headId != null;
+ String head = headId.getName();
String currentCommits = rebaseState.readFile(CURRENT_COMMIT);
for (String current : currentCommits.split("\n")) //$NON-NLS-1$
RebaseState
@@ -735,8 +750,8 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
private void resetSoftToParent() throws IOException,
GitAPIException, CheckoutConflictException {
- Ref orig_head = repo.getRef(Constants.ORIG_HEAD);
- ObjectId orig_headId = orig_head.getObjectId();
+ Ref ref = repo.getRef(Constants.ORIG_HEAD);
+ ObjectId orig_head = ref == null ? null : ref.getObjectId();
try {
// we have already commited the cherry-picked commit.
// what we need is to have changes introduced by this
@@ -747,7 +762,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
} finally {
// set ORIG_HEAD back to where we started because soft
// reset moved it
- repo.writeOrigHead(orig_headId);
+ repo.writeOrigHead(orig_head);
}
}
@@ -758,24 +773,25 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
String commitMessage = rebaseState
.readFile(MESSAGE_SQUASH);
- if (nextStep == null
- || ((nextStep.getAction() != Action.FIXUP) && (nextStep
- .getAction() != Action.SQUASH))) {
- // this is the last step in this sequence
- if (sequenceContainsSquash) {
- commitMessage = interactiveHandler
- .modifyCommitMessage(commitMessage);
- }
- retNewHead = new Git(repo).commit()
- .setMessage(stripCommentLines(commitMessage))
- .setAmend(true).setNoVerify(true).call();
- rebaseState.getFile(MESSAGE_SQUASH).delete();
- rebaseState.getFile(MESSAGE_FIXUP).delete();
+ try (Git git = new Git(repo)) {
+ if (nextStep == null || ((nextStep.getAction() != Action.FIXUP)
+ && (nextStep.getAction() != Action.SQUASH))) {
+ // this is the last step in this sequence
+ if (sequenceContainsSquash) {
+ commitMessage = interactiveHandler
+ .modifyCommitMessage(commitMessage);
+ }
+ retNewHead = git.commit()
+ .setMessage(stripCommentLines(commitMessage))
+ .setAmend(true).setNoVerify(true).call();
+ rebaseState.getFile(MESSAGE_SQUASH).delete();
+ rebaseState.getFile(MESSAGE_FIXUP).delete();
- } else {
- // Next step is either Squash or Fixup
- retNewHead = new Git(repo).commit().setMessage(commitMessage)
- .setAmend(true).setNoVerify(true).call();
+ } else {
+ // Next step is either Squash or Fixup
+ retNewHead = git.commit().setMessage(commitMessage)
+ .setAmend(true).setNoVerify(true).call();
+ }
}
return retNewHead;
}
@@ -878,7 +894,8 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
case NO_CHANGE:
break;
default:
- throw new JGitInternalException("Updating HEAD failed");
+ throw new JGitInternalException(
+ JGitText.get().updatingHeadFailed);
}
rup = repo.updateRef(Constants.HEAD);
rup.setRefLogMessage("rebase finished: returning to " + headName, //$NON-NLS-1$
@@ -890,7 +907,8 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
case NO_CHANGE:
break;
default:
- throw new JGitInternalException("Updating HEAD failed");
+ throw new JGitInternalException(
+ JGitText.get().updatingHeadFailed);
}
}
}
@@ -917,10 +935,10 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
} finally {
dc.unlock();
}
- RevWalk rw = new RevWalk(repo);
- RevCommit commit = rw.parseCommit(repo.resolve(Constants.HEAD));
- rw.release();
- return commit;
+ try (RevWalk rw = new RevWalk(repo)) {
+ RevCommit commit = rw.parseCommit(repo.resolve(Constants.HEAD));
+ return commit;
+ }
}
/**
@@ -936,27 +954,29 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
throw new UnmergedPathsException();
// determine whether we need to commit
- TreeWalk treeWalk = new TreeWalk(repo);
- treeWalk.reset();
- treeWalk.setRecursive(true);
- treeWalk.addTree(new DirCacheIterator(dc));
- ObjectId id = repo.resolve(Constants.HEAD + "^{tree}"); //$NON-NLS-1$
- if (id == null)
- throw new NoHeadException(
- JGitText.get().cannotRebaseWithoutCurrentHead);
-
- treeWalk.addTree(id);
+ boolean needsCommit;
+ try (TreeWalk treeWalk = new TreeWalk(repo)) {
+ treeWalk.reset();
+ treeWalk.setRecursive(true);
+ treeWalk.addTree(new DirCacheIterator(dc));
+ ObjectId id = repo.resolve(Constants.HEAD + "^{tree}"); //$NON-NLS-1$
+ if (id == null)
+ throw new NoHeadException(
+ JGitText.get().cannotRebaseWithoutCurrentHead);
- treeWalk.setFilter(TreeFilter.ANY_DIFF);
+ treeWalk.addTree(id);
- boolean needsCommit = treeWalk.next();
- treeWalk.release();
+ treeWalk.setFilter(TreeFilter.ANY_DIFF);
+ needsCommit = treeWalk.next();
+ }
if (needsCommit) {
- CommitCommand commit = new Git(repo).commit();
- commit.setMessage(rebaseState.readFile(MESSAGE));
- commit.setAuthor(parseAuthor());
- return commit.call();
+ try (Git git = new Git(repo)) {
+ CommitCommand commit = git.commit();
+ commit.setMessage(rebaseState.readFile(MESSAGE));
+ commit.setAuthor(parseAuthor());
+ return commit.call();
+ }
}
return null;
}
@@ -967,6 +987,9 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
try {
raw = IO.readFully(authorScriptFile);
} catch (FileNotFoundException notFound) {
+ if (authorScriptFile.exists()) {
+ throw notFound;
+ }
return null;
}
return parseAuthor(raw);
@@ -979,9 +1002,10 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
rebaseState.createFile(AUTHOR_SCRIPT, authorScript);
rebaseState.createFile(MESSAGE, commitToPick.getFullMessage());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
- DiffFormatter df = new DiffFormatter(bos);
- df.setRepository(repo);
- df.format(commitToPick.getParent(0), commitToPick);
+ try (DiffFormatter df = new DiffFormatter(bos)) {
+ df.setRepository(repo);
+ df.format(commitToPick.getParent(0), commitToPick);
+ }
rebaseState.createFile(PATCH, new String(bos.toByteArray(),
Constants.CHARACTER_ENCODING));
rebaseState.createFile(STOPPED_SHA,
@@ -1055,11 +1079,12 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
Ref head = getHead();
- String headName = getHeadName(head);
ObjectId headId = head.getObjectId();
- if (headId == null)
+ if (headId == null) {
throw new RefNotFoundException(MessageFormat.format(
JGitText.get().refNotResolved, Constants.HEAD));
+ }
+ String headName = getHeadName(head);
RevCommit headCommit = walk.lookupCommit(headId);
RevCommit upstream = walk.lookupCommit(upstreamCommit.getId());
@@ -1088,7 +1113,9 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
rebaseState.createFile(HEAD_NAME, headName);
rebaseState.createFile(ONTO, upstreamCommit.name());
rebaseState.createFile(ONTO_NAME, upstreamCommitName);
- rebaseState.createFile(INTERACTIVE, ""); //$NON-NLS-1$
+ if (isInteractive()) {
+ rebaseState.createFile(INTERACTIVE, ""); //$NON-NLS-1$
+ }
rebaseState.createFile(QUIET, ""); //$NON-NLS-1$
ArrayList<RebaseTodoLine> toDoSteps = new ArrayList<RebaseTodoLine>();
@@ -1122,9 +1149,11 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
private List<RevCommit> calculatePickList(RevCommit headCommit)
throws GitAPIException, NoHeadException, IOException {
- LogCommand cmd = new Git(repo).log().addRange(upstreamCommit,
- headCommit);
- Iterable<RevCommit> commitsToUse = cmd.call();
+ Iterable<RevCommit> commitsToUse;
+ try (Git git = new Git(repo)) {
+ LogCommand cmd = git.log().addRange(upstreamCommit, headCommit);
+ commitsToUse = cmd.call();
+ }
List<RevCommit> cherryPickList = new ArrayList<RevCommit>();
for (RevCommit commit : commitsToUse) {
if (preserveMerges || commit.getParentCount() == 1)
@@ -1166,10 +1195,14 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
private static String getHeadName(Ref head) {
String headName;
- if (head.isSymbolic())
+ if (head.isSymbolic()) {
headName = head.getTarget().getName();
- else
- headName = head.getObjectId().getName();
+ } else {
+ ObjectId headId = head.getObjectId();
+ // the callers are checking this already
+ assert headId != null;
+ headName = headId.getName();
+ }
return headName;
}
@@ -1225,7 +1258,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
RefUpdate rup = repo.updateRef(headName);
rup.setExpectedOldObjectId(oldCommit);
rup.setNewObjectId(newCommit);
- rup.setRefLogMessage("Fast-foward from " + oldCommit.name() //$NON-NLS-1$
+ rup.setRefLogMessage("Fast-forward from " + oldCommit.name() //$NON-NLS-1$
+ " to " + newCommit.name(), false); //$NON-NLS-1$
Result res = rup.update(walk);
switch (res) {
@@ -1275,7 +1308,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
if (this.upstreamCommit == null)
throw new JGitInternalException(MessageFormat
.format(JGitText.get().missingRequiredParameter,
- "upstream"));
+ "upstream")); //$NON-NLS-1$
return;
default:
throw new WrongRepositoryStateException(MessageFormat.format(
@@ -1310,7 +1343,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
}
dco.setFailOnConflict(false);
dco.checkout();
- walk.release();
+ walk.close();
} finally {
monitor.endTask();
}
@@ -1382,10 +1415,11 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
case FORCED:
break;
default:
- throw new IOException("Could not rewind to upstream commit");
+ throw new IOException(
+ JGitText.get().couldNotRewindToUpstreamCommit);
}
} finally {
- walk.release();
+ walk.close();
monitor.endTask();
}
return true;
@@ -1452,7 +1486,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
public RebaseCommand setUpstreamName(String upstreamName) {
if (upstreamCommit == null) {
throw new IllegalStateException(
- "setUpstreamName must be called after setUpstream.");
+ "setUpstreamName must be called after setUpstream."); //$NON-NLS-1$
}
this.upstreamCommitName = upstreamName;
return this;
@@ -1474,6 +1508,9 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
* @return this instance
*/
public RebaseCommand setProgressMonitor(ProgressMonitor monitor) {
+ if (monitor == null) {
+ monitor = NullProgressMonitor.INSTANCE;
+ }
this.monitor = monitor;
return this;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ShowNoteCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteAddCommand.java
similarity index 56%
copy from org.eclipse.jgit/src/org/eclipse/jgit/api/ShowNoteCommand.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteAddCommand.java
index 7d411c3..6795669 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ShowNoteCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteAddCommand.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, Chris Aniszczyk <caniszczyk at gmail.com>
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r at zend.com>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -43,82 +43,91 @@
package org.eclipse.jgit.api;
import java.io.IOException;
+import java.net.URISyntaxException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.notes.Note;
-import org.eclipse.jgit.notes.NoteMap;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.URIish;
/**
- * Show an object note.
+ * Used to add a new remote.
*
- * @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-notes.html"
- * >Git documentation about Notes</a>
+ * This class has setters for all supported options and arguments of this
+ * command and a {@link #call()} method to finally execute the command.
+ *
+ * @see <a href=
+ * "http://www.kernel.org/pub/software/scm/git/docs/git-remote.html" > Git
+ * documentation about Remote</a>
+ *
+ * @since 4.2
*/
-public class ShowNoteCommand extends GitCommand<Note> {
+public class RemoteAddCommand extends GitCommand<RemoteConfig> {
- private RevObject id;
+ private String name;
- private String notesRef = Constants.R_NOTES_COMMITS;
+ private URIish uri;
/**
* @param repo
*/
- protected ShowNoteCommand(Repository repo) {
+ protected RemoteAddCommand(Repository repo) {
super(repo);
}
- public Note call() throws GitAPIException {
- checkCallable();
- RevWalk walk = new RevWalk(repo);
- NoteMap map = NoteMap.newEmptyMap();
- RevCommit notesCommit = null;
- try {
- Ref ref = repo.getRef(notesRef);
- // if we have a notes ref, use it
- if (ref != null) {
- notesCommit = walk.parseCommit(ref.getObjectId());
- map = NoteMap.read(walk.getObjectReader(), notesCommit);
- }
- return map.getNote(id);
- } catch (IOException e) {
- throw new JGitInternalException(e.getMessage(), e);
- } finally {
- walk.release();
- }
+ /**
+ * The name of the remote to add.
+ *
+ * @param name
+ * a remote name
+ */
+ public void setName(String name) {
+ this.name = name;
}
/**
- * Sets the object id of object you want a note on
+ * The URL of the repository for the new remote.
*
- * @param id
- * @return {@code this}
+ * @param uri
+ * an URL for the remote
*/
- public ShowNoteCommand setObjectId(RevObject id) {
- checkCallable();
- this.id = id;
- return this;
+ public void setUri(URIish uri) {
+ this.uri = uri;
}
/**
- * @param notesRef
- * the ref to read notes from. Note, the default value of
- * {@link Constants#R_NOTES_COMMITS} will be used if nothing is
- * set
- * @return {@code this}
+ * Executes the {@code remote add} command with all the options and
+ * parameters collected by the setter methods of this class.
*
- * @see Constants#R_NOTES_COMMITS
+ * @return the {@link RemoteConfig} object of the added remote
*/
- public ShowNoteCommand setNotesRef(String notesRef) {
+ @Override
+ public RemoteConfig call() throws GitAPIException {
checkCallable();
- this.notesRef = notesRef;
- return this;
+
+ try {
+ StoredConfig config = repo.getConfig();
+ RemoteConfig remote = new RemoteConfig(config, name);
+
+ RefSpec refSpec = new RefSpec();
+ refSpec = refSpec.setForceUpdate(true);
+ refSpec = refSpec.setSourceDestination(Constants.R_HEADS + "*", //$NON-NLS-1$
+ Constants.R_REMOTES + name + "/*"); //$NON-NLS-1$
+ remote.addFetchRefSpec(refSpec);
+
+ remote.addURI(uri);
+
+ remote.update(config);
+ config.save();
+ return remote;
+ } catch (IOException | URISyntaxException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteListCommand.java
similarity index 66%
copy from org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteListCommand.java
index a0a5d95..f778eaa 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteListCommand.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, Ketan Padegaonkar <ketanpadegaonkar at gmail.com>
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r at zend.com>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -42,60 +42,50 @@
*/
package org.eclipse.jgit.api;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
+import java.net.URISyntaxException;
import java.util.List;
-import java.util.Map;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.RemoteConfig;
/**
- * Used to obtain a list of tags.
+ * Used to obtain the list of remotes.
*
- * @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-tag.html"
- * >Git documentation about Tag</a>
+ * This class has setters for all supported options and arguments of this
+ * command and a {@link #call()} method to finally execute the command.
+ *
+ * @see <a href=
+ * "http://www.kernel.org/pub/software/scm/git/docs/git-remote.html" > Git
+ * documentation about Remote</a>
+ *
+ * @since 4.2
*/
-public class ListTagCommand extends GitCommand<List<Ref>> {
+public class RemoteListCommand extends GitCommand<List<RemoteConfig>> {
/**
* @param repo
*/
- protected ListTagCommand(Repository repo) {
+ protected RemoteListCommand(Repository repo) {
super(repo);
}
/**
- * @return the tags available
+ * Executes the {@code remote} command with all the options and parameters
+ * collected by the setter methods of this class.
+ *
+ * @return a list of {@link RemoteConfig} objects.
*/
- public List<Ref> call() throws GitAPIException {
+ @Override
+ public List<RemoteConfig> call() throws GitAPIException {
checkCallable();
- Map<String, Ref> refList;
- List<Ref> tags = new ArrayList<Ref>();
- RevWalk revWalk = new RevWalk(repo);
+
try {
- refList = repo.getRefDatabase().getRefs(Constants.R_TAGS);
- for (Ref ref : refList.values()) {
- tags.add(ref);
- }
- } catch (IOException e) {
+ return RemoteConfig.getAllRemoteConfigs(repo.getConfig());
+ } catch (URISyntaxException e) {
throw new JGitInternalException(e.getMessage(), e);
- } finally {
- revWalk.release();
}
- Collections.sort(tags, new Comparator<Ref>() {
- public int compare(Ref o1, Ref o2) {
- return o1.getName().compareTo(o2.getName());
- }
- });
- setCallable(false);
- return tags;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java
similarity index 62%
copy from org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java
index a0a5d95..5782bf6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, Ketan Padegaonkar <ketanpadegaonkar at gmail.com>
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r at zend.com>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -43,59 +43,68 @@
package org.eclipse.jgit.api;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Map;
+import java.net.URISyntaxException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.transport.RemoteConfig;
/**
- * Used to obtain a list of tags.
+ * Used to remove an existing remote.
*
- * @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-tag.html"
- * >Git documentation about Tag</a>
+ * This class has setters for all supported options and arguments of this
+ * command and a {@link #call()} method to finally execute the command.
+ *
+ * @see <a href=
+ * "http://www.kernel.org/pub/software/scm/git/docs/git-remote.html" > Git
+ * documentation about Remote</a>
+ *
+ * @since 4.2
*/
-public class ListTagCommand extends GitCommand<List<Ref>> {
+public class RemoteRemoveCommand extends GitCommand<RemoteConfig> {
+
+ private String name;
/**
* @param repo
*/
- protected ListTagCommand(Repository repo) {
+ protected RemoteRemoveCommand(Repository repo) {
super(repo);
}
/**
- * @return the tags available
+ * The name of the remote to remove.
+ *
+ * @param name
+ * a remote name
*/
- public List<Ref> call() throws GitAPIException {
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Executes the {@code remote} command with all the options and parameters
+ * collected by the setter methods of this class.
+ *
+ * @return the {@link RemoteConfig} object of the removed remote
+ */
+ @Override
+ public RemoteConfig call() throws GitAPIException {
checkCallable();
- Map<String, Ref> refList;
- List<Ref> tags = new ArrayList<Ref>();
- RevWalk revWalk = new RevWalk(repo);
+
try {
- refList = repo.getRefDatabase().getRefs(Constants.R_TAGS);
- for (Ref ref : refList.values()) {
- tags.add(ref);
- }
- } catch (IOException e) {
+ StoredConfig config = repo.getConfig();
+ RemoteConfig remote = new RemoteConfig(config, name);
+ config.unsetSection(ConfigConstants.CONFIG_KEY_REMOTE, name);
+ config.save();
+ return remote;
+ } catch (IOException | URISyntaxException e) {
throw new JGitInternalException(e.getMessage(), e);
- } finally {
- revWalk.release();
}
- Collections.sort(tags, new Comparator<Ref>() {
- public int compare(Ref o1, Ref o2) {
- return o1.getName().compareTo(o2.getName());
- }
- });
- setCallable(false);
- return tags;
+
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java
new file mode 100644
index 0000000..6bd2ac7
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r at zend.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.URIish;
+
+/**
+ * Used to to change the URL of a remote.
+ *
+ * This class has setters for all supported options and arguments of this
+ * command and a {@link #call()} method to finally execute the command.
+ *
+ * @see <a href=
+ * "http://www.kernel.org/pub/software/scm/git/docs/git-remote.html" > Git
+ * documentation about Remote</a>
+ *
+ * @since 4.2
+ */
+public class RemoteSetUrlCommand extends GitCommand<RemoteConfig> {
+
+ private String name;
+
+ private URIish uri;
+
+ private boolean push;
+
+ /**
+ * @param repo
+ */
+ protected RemoteSetUrlCommand(Repository repo) {
+ super(repo);
+ }
+
+ /**
+ * The name of the remote to change the URL for.
+ *
+ * @param name
+ * a remote name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * The new URL for the remote.
+ *
+ * @param uri
+ * an URL for the remote
+ */
+ public void setUri(URIish uri) {
+ this.uri = uri;
+ }
+
+ /**
+ * Whether to change the push URL of the remote instead of the fetch URL.
+ *
+ * @param push
+ * <code>true</code> to set the push url, <code>false</code> to
+ * set the fetch url
+ */
+ public void setPush(boolean push) {
+ this.push = push;
+ }
+
+ /**
+ * Executes the {@code remote} command with all the options and parameters
+ * collected by the setter methods of this class.
+ *
+ * @return the {@link RemoteConfig} object of the modified remote
+ */
+ @Override
+ public RemoteConfig call() throws GitAPIException {
+ checkCallable();
+
+ try {
+ StoredConfig config = repo.getConfig();
+ RemoteConfig remote = new RemoteConfig(config, name);
+ if (push) {
+ List<URIish> uris = remote.getPushURIs();
+ if (uris.size() > 1) {
+ throw new JGitInternalException(
+ "remote.newtest.pushurl has multiple values"); //$NON-NLS-1$
+ } else if (uris.size() == 1) {
+ remote.removePushURI(uris.get(0));
+ }
+ remote.addPushURI(uri);
+ } else {
+ List<URIish> uris = remote.getURIs();
+ if (uris.size() > 1) {
+ throw new JGitInternalException(
+ "remote.newtest.url has multiple values"); //$NON-NLS-1$
+ } else if (uris.size() == 1) {
+ remote.removeURI(uris.get(0));
+ }
+ remote.addURI(uri);
+ }
+
+ remote.update(config);
+ config.save();
+ return remote;
+ } catch (IOException | URISyntaxException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+ }
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoveNoteCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoveNoteCommand.java
index d1e2770..a526c27 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoveNoteCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoveNoteCommand.java
@@ -81,11 +81,10 @@ public class RemoveNoteCommand extends GitCommand<Note> {
public Note call() throws GitAPIException {
checkCallable();
- RevWalk walk = new RevWalk(repo);
- ObjectInserter inserter = repo.newObjectInserter();
- NoteMap map = NoteMap.newEmptyMap();
- RevCommit notesCommit = null;
- try {
+ try (RevWalk walk = new RevWalk(repo);
+ ObjectInserter inserter = repo.newObjectInserter()) {
+ NoteMap map = NoteMap.newEmptyMap();
+ RevCommit notesCommit = null;
Ref ref = repo.getRef(notesRef);
// if we have a notes ref, use it
if (ref != null) {
@@ -94,13 +93,10 @@ public class RemoveNoteCommand extends GitCommand<Note> {
}
map.set(id, null, inserter);
commitNoteMap(walk, map, notesCommit, inserter,
- "Notes removed by 'git notes remove'");
+ "Notes removed by 'git notes remove'"); //$NON-NLS-1$
return map.getNote(id);
} catch (IOException e) {
throw new JGitInternalException(e.getMessage(), e);
- } finally {
- inserter.release();
- walk.release();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java
index 607253b..0731dd4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java
@@ -51,6 +51,7 @@ import org.eclipse.jgit.api.errors.DetachedHeadException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.InvalidRefNameException;
import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.api.errors.NoHeadException;
import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.internal.JGitText;
@@ -121,6 +122,10 @@ public class RenameBranchCommand extends GitCommand<Ref> {
fullOldName = ref.getName();
} else {
fullOldName = repo.getFullBranch();
+ if (fullOldName == null) {
+ throw new NoHeadException(
+ JGitText.get().invalidRepositoryStateNoHead);
+ }
if (ObjectId.isId(fullOldName))
throw new DetachedHeadException();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java
index 17b1242..4c91e6c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ResetCommand.java
@@ -157,8 +157,8 @@ public class ResetCommand extends GitCommand<Ref> {
if (ref != null && commitId == null) {
// @TODO throw an InvalidRefNameException. We can't do that
// now because this would break the API
- throw new JGitInternalException("Invalid ref " + ref
- + " specified");
+ throw new JGitInternalException(MessageFormat
+ .format(JGitText.get().invalidRefName, ref));
}
final ObjectId commitTree;
@@ -190,10 +190,8 @@ public class ResetCommand extends GitCommand<Ref> {
ObjectId origHead = ru.getOldObjectId();
if (origHead != null)
repo.writeOrigHead(origHead);
- result = ru.getRef();
- } else {
- result = repo.getRef(Constants.HEAD);
}
+ result = repo.exactRef(Constants.HEAD);
if (mode == null)
mode = ResetType.MIXED;
@@ -227,24 +225,19 @@ public class ResetCommand extends GitCommand<Ref> {
setCallable(false);
return result;
} catch (IOException e) {
- throw new JGitInternalException(
+ throw new JGitInternalException(MessageFormat.format(
JGitText.get().exceptionCaughtDuringExecutionOfResetCommand,
- e);
+ e.getMessage()), e);
}
}
private RevCommit parseCommit(final ObjectId commitId) {
- RevCommit commit;
- RevWalk rw = new RevWalk(repo);
- try {
- commit = rw.parseCommit(commitId);
+ try (RevWalk rw = new RevWalk(repo)) {
+ return rw.parseCommit(commitId);
} catch (IOException e) {
throw new JGitInternalException(MessageFormat.format(
JGitText.get().cannotReadCommit, commitId.toString()), e);
- } finally {
- rw.release();
}
- return commit;
}
private ObjectId resolveRefToCommitId() {
@@ -276,7 +269,7 @@ public class ResetCommand extends GitCommand<Ref> {
if (!filepaths.isEmpty())
throw new JGitInternalException(MessageFormat.format(
JGitText.get().illegalCombinationOfArguments,
- "[--mixed | --soft | --hard]", "<paths>...")); //$NON-NLS-1$
+ "[--mixed | --soft | --hard]", "<paths>...")); //$NON-NLS-1$ //$NON-NLS-2$
this.mode = mode;
return this;
}
@@ -290,7 +283,7 @@ public class ResetCommand extends GitCommand<Ref> {
public ResetCommand addPath(String path) {
if (mode != null)
throw new JGitInternalException(MessageFormat.format(
- JGitText.get().illegalCombinationOfArguments, "<paths>...",
+ JGitText.get().illegalCombinationOfArguments, "<paths>...", //$NON-NLS-1$
"[--mixed | --soft | --hard]")); //$NON-NLS-1$
filepaths.add(path);
return this;
@@ -305,11 +298,10 @@ public class ResetCommand extends GitCommand<Ref> {
private void resetIndexForPaths(ObjectId commitTree) {
DirCache dc = null;
- try {
+ try (final TreeWalk tw = new TreeWalk(repo)) {
dc = repo.lockDirCache();
DirCacheBuilder builder = dc.builder();
- final TreeWalk tw = new TreeWalk(repo);
tw.addTree(new DirCacheBuildIterator(builder));
if (commitTree != null)
tw.addTree(commitTree);
@@ -342,11 +334,9 @@ public class ResetCommand extends GitCommand<Ref> {
private void resetIndex(ObjectId commitTree) throws IOException {
DirCache dc = repo.lockDirCache();
- TreeWalk walk = null;
- try {
+ try (TreeWalk walk = new TreeWalk(repo)) {
DirCacheBuilder builder = dc.builder();
- walk = new TreeWalk(repo);
if (commitTree != null)
walk.addTree(commitTree);
else
@@ -380,8 +370,6 @@ public class ResetCommand extends GitCommand<Ref> {
builder.commit();
} finally {
dc.unlock();
- if (walk != null)
- walk.release();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
index 470d823..8015773 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
@@ -126,8 +126,7 @@ public class RevertCommand extends GitCommand<RevCommit> {
RevCommit newHead = null;
checkCallable();
- RevWalk revWalk = new RevWalk(repo);
- try {
+ try (RevWalk revWalk = new RevWalk(repo)) {
// get the head commit
Ref headRef = repo.getRef(Constants.HEAD);
@@ -182,9 +181,11 @@ public class RevertCommand extends GitCommand<RevCommit> {
merger.getResultTreeId());
dco.setFailOnConflict(true);
dco.checkout();
- newHead = new Git(getRepository()).commit()
- .setMessage(newMessage)
- .setReflogComment("revert: " + shortMessage).call(); //$NON-NLS-1$
+ try (Git git = new Git(getRepository())) {
+ newHead = git.commit().setMessage(newMessage)
+ .setReflogComment("revert: " + shortMessage) //$NON-NLS-1$
+ .call();
+ }
revertedRefs.add(src);
headCommit = newHead;
} else {
@@ -220,8 +221,6 @@ public class RevertCommand extends GitCommand<RevCommit> {
MessageFormat.format(
JGitText.get().exceptionCaughtDuringExecutionOfRevertCommand,
e), e);
- } finally {
- revWalk.release();
}
return newHead;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RmCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RmCommand.java
index c70b4ae..fd2cbe0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RmCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RmCommand.java
@@ -144,10 +144,9 @@ public class RmCommand extends GitCommand<DirCache> {
checkCallable();
DirCache dc = null;
- try {
+ try (final TreeWalk tw = new TreeWalk(repo)) {
dc = repo.lockDirCache();
DirCacheBuilder builder = dc.builder();
- final TreeWalk tw = new TreeWalk(repo);
tw.reset(); // drop the first empty tree, which we do not need here
tw.setRecursive(true);
tw.setFilter(PathFilterGroup.createFromStrings(filepatterns));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ShowNoteCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ShowNoteCommand.java
index 7d411c3..82db881 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ShowNoteCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ShowNoteCommand.java
@@ -76,10 +76,9 @@ public class ShowNoteCommand extends GitCommand<Note> {
public Note call() throws GitAPIException {
checkCallable();
- RevWalk walk = new RevWalk(repo);
NoteMap map = NoteMap.newEmptyMap();
RevCommit notesCommit = null;
- try {
+ try (RevWalk walk = new RevWalk(repo)) {
Ref ref = repo.getRef(notesRef);
// if we have a notes ref, use it
if (ref != null) {
@@ -89,8 +88,6 @@ public class ShowNoteCommand extends GitCommand<Note> {
return map.getNote(id);
} catch (IOException e) {
throw new JGitInternalException(e.getMessage(), e);
- } finally {
- walk.release();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
index 356723d..8ef5508 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
@@ -166,9 +166,8 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
JGitText.get().stashApplyOnUnsafeRepository,
repo.getRepositoryState()));
- ObjectReader reader = repo.newObjectReader();
- try {
- RevWalk revWalk = new RevWalk(reader);
+ try (ObjectReader reader = repo.newObjectReader();
+ RevWalk revWalk = new RevWalk(reader)) {
ObjectId headCommit = repo.resolve(Constants.HEAD);
if (headCommit == null)
@@ -192,8 +191,8 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
untrackedCommit = revWalk.parseCommit(stashCommit.getParent(2));
ResolveMerger merger = (ResolveMerger) strategy.newMerger(repo);
- merger.setCommitNames(new String[] { "stashed HEAD", "HEAD",
- "stash" });
+ merger.setCommitNames(new String[] { "stashed HEAD", "HEAD", //$NON-NLS-1$ //$NON-NLS-2$
+ "stash" }); //$NON-NLS-1$
merger.setBase(stashHeadCommit);
merger.setWorkingTreeIterator(new FileTreeIterator(repo));
if (merger.merge(headCommit, stashCommit)) {
@@ -205,8 +204,8 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
if (applyIndex) {
ResolveMerger ixMerger = (ResolveMerger) strategy
.newMerger(repo, true);
- ixMerger.setCommitNames(new String[] { "stashed HEAD",
- "HEAD", "stashed index" });
+ ixMerger.setCommitNames(new String[] { "stashed HEAD", //$NON-NLS-1$
+ "HEAD", "stashed index" }); //$NON-NLS-1$//$NON-NLS-2$
ixMerger.setBase(stashHeadCommit);
boolean ok = ixMerger.merge(headCommit, stashIndexCommit);
if (ok) {
@@ -250,8 +249,6 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
throw e;
} catch (IOException e) {
throw new JGitInternalException(JGitText.get().stashApplyFailed, e);
- } finally {
- reader.release();
}
}
@@ -286,11 +283,9 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
private void resetIndex(RevTree tree) throws IOException {
DirCache dc = repo.lockDirCache();
- TreeWalk walk = null;
- try {
+ try (TreeWalk walk = new TreeWalk(repo)) {
DirCacheBuilder builder = dc.builder();
- walk = new TreeWalk(repo);
walk.addTree(tree);
walk.addTree(new DirCacheIterator(dc));
walk.setRecursive(true);
@@ -321,15 +316,13 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
builder.commit();
} finally {
dc.unlock();
- if (walk != null)
- walk.release();
}
}
private void resetUntracked(RevTree tree) throws CheckoutConflictException,
IOException {
- TreeWalk walk = new TreeWalk(repo); // maybe NameConflictTreeWalk;
- try {
+ // TODO maybe NameConflictTreeWalk ?
+ try (TreeWalk walk = new TreeWalk(repo)) {
walk.addTree(tree);
walk.addTree(new FileTreeIterator(repo));
walk.setRecursive(true);
@@ -359,14 +352,12 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
checkoutPath(entry, reader);
}
- } finally {
- walk.release();
}
}
private void checkoutPath(DirCacheEntry entry, ObjectReader reader) {
try {
- DirCacheCheckout.checkoutEntry(repo, entry, reader);
+ DirCacheCheckout.checkoutEntry(repo, entry, reader, true);
} catch (IOException e) {
throw new JGitInternalException(MessageFormat.format(
JGitText.get().checkoutConflictWithFile,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java
index af35f77..2cdaf24 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java
@@ -94,11 +94,11 @@ import org.eclipse.jgit.util.FileUtils;
*/
public class StashCreateCommand extends GitCommand<RevCommit> {
- private static final String MSG_INDEX = "index on {0}: {1} {2}";
+ private static final String MSG_INDEX = "index on {0}: {1} {2}"; //$NON-NLS-1$
- private static final String MSG_UNTRACKED = "untracked files on {0}: {1} {2}";
+ private static final String MSG_UNTRACKED = "untracked files on {0}: {1} {2}"; //$NON-NLS-1$
- private static final String MSG_WORKING_DIR = "WIP on {0}: {1} {2}";
+ private static final String MSG_WORKING_DIR = "WIP on {0}: {1} {2}"; //$NON-NLS-1$
private String indexMessage = MSG_INDEX;
@@ -187,9 +187,9 @@ public class StashCreateCommand extends GitCommand<RevCommit> {
private RevCommit parseCommit(final ObjectReader reader,
final ObjectId headId) throws IOException {
- final RevWalk walk = new RevWalk(reader);
- walk.setRetainBody(true);
- return walk.parseCommit(headId);
+ try (final RevWalk walk = new RevWalk(reader)) {
+ return walk.parseCommit(headId);
+ }
}
private CommitBuilder createBuilder() {
@@ -240,14 +240,13 @@ public class StashCreateCommand extends GitCommand<RevCommit> {
checkCallable();
Ref head = getHead();
- ObjectReader reader = repo.newObjectReader();
- try {
+ try (ObjectReader reader = repo.newObjectReader()) {
RevCommit headCommit = parseCommit(reader, head.getObjectId());
DirCache cache = repo.lockDirCache();
- ObjectInserter inserter = repo.newObjectInserter();
ObjectId commitId;
- try {
- TreeWalk treeWalk = new TreeWalk(reader);
+ try (ObjectInserter inserter = repo.newObjectInserter();
+ TreeWalk treeWalk = new TreeWalk(reader)) {
+
treeWalk.setRecursive(true);
treeWalk.addTree(headCommit.getTree());
treeWalk.addTree(new DirCacheIterator(cache));
@@ -381,7 +380,6 @@ public class StashCreateCommand extends GitCommand<RevCommit> {
}
} finally {
- inserter.release();
cache.unlock();
}
@@ -392,8 +390,6 @@ public class StashCreateCommand extends GitCommand<RevCommit> {
return parseCommit(reader, commitId);
} catch (IOException e) {
throw new JGitInternalException(JGitText.get().stashFailed, e);
- } finally {
- reader.release();
}
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java
index 6cbcd06..f6903be 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java
@@ -46,12 +46,14 @@ import static org.eclipse.jgit.lib.Constants.R_STASH;
import java.io.File;
import java.io.IOException;
+import java.nio.file.StandardCopyOption;
import java.text.MessageFormat;
import java.util.List;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.InvalidRefNameException;
import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.errors.LockFailedException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.file.ReflogWriter;
@@ -184,6 +186,10 @@ public class StashDropCommand extends GitCommand<ObjectId> {
List<ReflogEntry> entries;
try {
ReflogReader reader = repo.getReflogReader(R_STASH);
+ if (reader == null) {
+ throw new RefNotFoundException(MessageFormat
+ .format(JGitText.get().refNotResolved, stashRef));
+ }
entries = reader.getReverseEntries();
} catch (IOException e) {
throw new JGitInternalException(JGitText.get().stashDropFailed, e);
@@ -215,12 +221,14 @@ public class StashDropCommand extends GitCommand<ObjectId> {
entry.getWho(), entry.getComment());
entryId = entry.getNewId();
}
- if (!stashLockFile.renameTo(stashFile)) {
- FileUtils.delete(stashFile);
- if (!stashLockFile.renameTo(stashFile))
+ try {
+ FileUtils.rename(stashLockFile, stashFile,
+ StandardCopyOption.ATOMIC_MOVE);
+ } catch (IOException e) {
throw new JGitInternalException(MessageFormat.format(
JGitText.get().renameFileFailed,
- stashLockFile.getPath(), stashFile.getPath()));
+ stashLockFile.getPath(), stashFile.getPath()),
+ e);
}
} catch (IOException e) {
throw new JGitInternalException(JGitText.get().stashDropFailed, e);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashListCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashListCommand.java
index bbbb7ac..59a83aa 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashListCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashListCommand.java
@@ -96,10 +96,8 @@ public class StashListCommand extends GitCommand<Collection<RevCommit>> {
final List<RevCommit> stashCommits = new ArrayList<RevCommit>(
stashEntries.size());
- final RevWalk walk = new RevWalk(repo);
- walk.setRetainBody(true);
- try {
- for (ReflogEntry entry : stashEntries)
+ try (RevWalk walk = new RevWalk(repo)) {
+ for (ReflogEntry entry : stashEntries) {
try {
stashCommits.add(walk.parseCommit(entry.getNewId()));
} catch (IOException e) {
@@ -107,8 +105,7 @@ public class StashListCommand extends GitCommand<Collection<RevCommit>> {
JGitText.get().cannotReadCommit, entry.getNewId()),
e);
}
- } finally {
- walk.dispose();
+ }
}
return stashCommits;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java
index 06c8f41..fbb24c1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java
@@ -173,6 +173,8 @@ public class SubmoduleAddCommand extends
CloneCommand clone = Git.cloneRepository();
configure(clone);
clone.setDirectory(moduleDirectory);
+ clone.setGitDir(new File(new File(repo.getDirectory(),
+ Constants.MODULES), path));
clone.setURI(resolvedUri);
if (monitor != null)
clone.setProgressMonitor(monitor);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
index 81a3015..342d7f4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2011, GitHub Inc.
+ * Copyright (C) 2016, Laurent Delaigue <laurent.delaigue at obeo.fr>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -143,8 +144,7 @@ public class SubmoduleUpdateCommand extends
RefNotFoundException, GitAPIException {
checkCallable();
- try {
- SubmoduleWalk generator = SubmoduleWalk.forIndex(repo);
+ try (SubmoduleWalk generator = SubmoduleWalk.forIndex(repo)) {
if (!paths.isEmpty())
generator.setFilter(PathFilterGroup.createFromStrings(paths));
List<String> updated = new ArrayList<String>();
@@ -171,8 +171,7 @@ public class SubmoduleUpdateCommand extends
submoduleRepo = clone.call().getRepository();
}
- try {
- RevWalk walk = new RevWalk(submoduleRepo);
+ try (RevWalk walk = new RevWalk(submoduleRepo)) {
RevCommit commit = walk
.parseCommit(generator.getObjectId());
@@ -180,11 +179,13 @@ public class SubmoduleUpdateCommand extends
if (ConfigConstants.CONFIG_KEY_MERGE.equals(update)) {
MergeCommand merge = new MergeCommand(submoduleRepo);
merge.include(commit);
+ merge.setProgressMonitor(monitor);
merge.setStrategy(strategy);
merge.call();
} else if (ConfigConstants.CONFIG_KEY_REBASE.equals(update)) {
RebaseCommand rebase = new RebaseCommand(submoduleRepo);
rebase.setUpstream(commit);
+ rebase.setProgressMonitor(monitor);
rebase.setStrategy(strategy);
rebase.call();
} else {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
index 8570baa..ca98ced 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/TagCommand.java
@@ -128,8 +128,7 @@ public class TagCommand extends GitCommand<Ref> {
RepositoryState state = repo.getRepositoryState();
processOptions(state);
- RevWalk revWalk = new RevWalk(repo);
- try {
+ try (RevWalk revWalk = new RevWalk(repo)) {
// if no id is set, we should attempt to use HEAD
if (id == null) {
ObjectId objectId = repo.resolve(Constants.HEAD + "^{commit}"); //$NON-NLS-1$
@@ -157,24 +156,19 @@ public class TagCommand extends GitCommand<Ref> {
newTag.setObjectId(id);
// write the tag object
- ObjectInserter inserter = repo.newObjectInserter();
- try {
+ try (ObjectInserter inserter = repo.newObjectInserter()) {
ObjectId tagId = inserter.insert(newTag);
inserter.flush();
String tag = newTag.getTag();
return updateTagRef(tagId, revWalk, tag, newTag.toString());
- } finally {
- inserter.release();
}
} catch (IOException e) {
throw new JGitInternalException(
JGitText.get().exceptionCaughtDuringExecutionOfTagCommand,
e);
- } finally {
- revWalk.release();
}
}
@@ -222,8 +216,9 @@ public class TagCommand extends GitCommand<Ref> {
if (tagger == null && annotated)
tagger = new PersonIdent(repo);
if (name == null || !Repository.isValidRefName(Constants.R_TAGS + name))
- throw new InvalidTagNameException(MessageFormat.format(JGitText
- .get().tagNameInvalid, name == null ? "<null>" : name));
+ throw new InvalidTagNameException(
+ MessageFormat.format(JGitText.get().tagNameInvalid,
+ name == null ? "<null>" : name)); //$NON-NLS-1$
if (signed)
throw new UnsupportedOperationException(
JGitText.get().signingNotSupportedOnTag);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/TransportCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/TransportCommand.java
index 1aeb610..3d2e46b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/TransportCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/TransportCommand.java
@@ -79,6 +79,7 @@ public abstract class TransportCommand<C extends GitCommand, T> extends
*/
protected TransportCommand(final Repository repo) {
super(repo);
+ setCredentialsProvider(CredentialsProvider.getDefault());
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/LockFailedException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/AbortedByHookException.java
old mode 100644
new mode 100755
similarity index 63%
copy from org.eclipse.jgit/src/org/eclipse/jgit/errors/LockFailedException.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/api/errors/AbortedByHookException.java
index 18aa9d9..995611e
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/LockFailedException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/AbortedByHookException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, GitHub Inc.
+ * Copyright (C) 2015 Obeo.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -40,51 +40,65 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.errors;
+package org.eclipse.jgit.api.errors;
-import java.io.File;
-import java.io.IOException;
import java.text.MessageFormat;
import org.eclipse.jgit.internal.JGitText;
/**
- * An exception occurring when a file cannot be locked
+ * Exception thrown when a hook returns a process result with a value different
+ * from 0. It is up to the caller to decide whether this should block execution
+ * or not.
+ *
+ * @since 4.0
*/
-public class LockFailedException extends IOException {
+public class AbortedByHookException extends GitAPIException {
private static final long serialVersionUID = 1L;
- private File file;
+ /**
+ * The hook that caused this exception.
+ */
+ private final String hookName;
+
+ /**
+ * The process result.
+ */
+ private final int returnCode;
/**
- * Construct a CannotLockException for the given file and message
- *
- * @param file
- * file that could not be locked
* @param message
- * exception message
+ * The error details.
+ * @param hookName
+ * The name of the hook that interrupted the command, must not be
+ * null.
+ * @param returnCode
+ * The return code of the hook process that has been run.
*/
- public LockFailedException(File file, String message) {
+ public AbortedByHookException(String message, String hookName,
+ int returnCode) {
super(message);
- this.file = file;
+ this.hookName = hookName;
+ this.returnCode = returnCode;
}
/**
- * Construct a CannotLockException for the given file
- *
- * @param file
- * file that could not be locked
+ * @return the type of the hook that interrupted the git command.
*/
- public LockFailedException(File file) {
- this(file, MessageFormat.format(JGitText.get().cannotLock, file));
+ public String getHookName() {
+ return hookName;
}
/**
- * Get the file that could not be locked
- *
- * @return file
+ * @return the hook process result.
*/
- public File getFile() {
- return file;
+ public int getReturnCode() {
+ return returnCode;
+ }
+
+ @Override
+ public String getMessage() {
+ return MessageFormat.format(JGitText.get().commandRejectedByHook,
+ hookName, super.getMessage());
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RefNotFoundException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/EmtpyCommitException.java
similarity index 80%
copy from org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RefNotFoundException.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/api/errors/EmtpyCommitException.java
index c8d96a0..b3cc1bf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RefNotFoundException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/EmtpyCommitException.java
@@ -1,6 +1,6 @@
/*
- * Copyright (C) 2010,Mathias Kinzler <mathias.kinzler at sap.com> and
- * other copyright owners as documented in the project's IP log.
+ * Copyright (C) 2015, Christian Halstrick <christian.halstrick at sap.com>
+ * and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v1.0 which accompanies this
@@ -38,15 +38,25 @@
package org.eclipse.jgit.api.errors;
/**
- * Thrown when a Ref can not be resolved
+ * Exception thrown when a newly created commit does not contain any changes
+ *
+ * @since 4.2
*/
-public class RefNotFoundException extends GitAPIException {
+public class EmtpyCommitException extends GitAPIException {
private static final long serialVersionUID = 1L;
/**
* @param message
+ * @param cause
+ */
+ public EmtpyCommitException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * @param message
*/
- public RefNotFoundException(String message) {
+ public EmtpyCommitException(String message) {
super(message);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/FilterFailedException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/FilterFailedException.java
new file mode 100644
index 0000000..fbc30ef
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/FilterFailedException.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2015, Christian Halstrick <christian.halstrick at sap.com> and
+ * other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v1.0 which accompanies this
+ * distribution, is reproduced below, and is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.api.errors;
+
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.internal.JGitText;
+
+/**
+ * Exception thrown when the execution of a filter command failed
+ *
+ * @since 4.2
+ */
+public class FilterFailedException extends GitAPIException {
+ private static final long serialVersionUID = 1L;
+
+ private String filterCommand;
+
+ private String path;
+
+ private byte[] stdout;
+
+ private String stderr;
+
+ private int rc;
+
+ /**
+ * Thrown if during execution of filter command an exception occurred
+ *
+ * @param cause
+ * the exception
+ * @param filterCommand
+ * the command which failed
+ * @param path
+ * the path processed by the filter
+ */
+ public FilterFailedException(Exception cause, String filterCommand,
+ String path) {
+ super(MessageFormat.format(JGitText.get().filterExecutionFailed,
+ filterCommand, path), cause);
+ this.filterCommand = filterCommand;
+ this.path = path;
+ }
+
+ /**
+ * Thrown if a filter command returns a non-zero return code
+ *
+ * @param rc
+ * the return code
+ * @param filterCommand
+ * the command which failed
+ * @param path
+ * the path processed by the filter
+ * @param stdout
+ * the output the filter generated so far. This should be limited
+ * to reasonable size.
+ * @param stderr
+ * the stderr output of the filter
+ */
+ @SuppressWarnings("boxing")
+ public FilterFailedException(int rc, String filterCommand, String path,
+ byte[] stdout, String stderr) {
+ super(MessageFormat.format(JGitText.get().filterExecutionFailedRc,
+ filterCommand, path, rc, stderr));
+ this.rc = rc;
+ this.filterCommand = filterCommand;
+ this.path = path;
+ this.stdout = stdout;
+ this.stderr = stderr;
+ }
+
+ /**
+ * @return the filterCommand
+ */
+ public String getFilterCommand() {
+ return filterCommand;
+ }
+
+ /**
+ * @return the path of the file processed by the filter command
+ */
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * @return the output generated by the filter command. Might be truncated to
+ * limit memory consumption.
+ */
+ public byte[] getOutput() {
+ return stdout;
+ }
+
+ /**
+ * @return the error output returned by the filter command
+ */
+ public String getError() {
+ return stderr;
+ }
+
+ /**
+ * @return the return code returned by the filter command
+ */
+ public int getReturnCode() {
+ return rc;
+ }
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RefNotFoundException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RefNotAdvertisedException.java
similarity index 88%
copy from org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RefNotFoundException.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RefNotAdvertisedException.java
index c8d96a0..2bd8477 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RefNotFoundException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RefNotAdvertisedException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010,Mathias Kinzler <mathias.kinzler at sap.com> and
+ * Copyright (C) 2015,Matthias Sohn <matthias.sohn at sap.com> and
* other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available under the
@@ -38,15 +38,17 @@
package org.eclipse.jgit.api.errors;
/**
- * Thrown when a Ref can not be resolved
+ * Thrown when a ref is not found in advertised refs
+ *
+ * @since 4.0
*/
-public class RefNotFoundException extends GitAPIException {
+public class RefNotAdvertisedException extends GitAPIException {
private static final long serialVersionUID = 1L;
/**
* @param message
*/
- public RefNotFoundException(String message) {
+ public RefNotAdvertisedException(String message) {
super(message);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RefNotFoundException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RefNotFoundException.java
index c8d96a0..b9f2a56 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RefNotFoundException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RefNotFoundException.java
@@ -45,6 +45,15 @@ public class RefNotFoundException extends GitAPIException {
/**
* @param message
+ * @param cause
+ * @since 4.1
+ */
+ public RefNotFoundException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * @param message
*/
public RefNotFoundException(String message) {
super(message);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/StashApplyFailureException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/StashApplyFailureException.java
index 25d7e4d..1d54f77 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/StashApplyFailureException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/StashApplyFailureException.java
@@ -10,6 +10,15 @@ public class StashApplyFailureException extends GitAPIException {
private static final long serialVersionUID = 1L;
/**
+ * @param message
+ * @param cause
+ * @since 4.1
+ */
+ public StashApplyFailureException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
* Create a StashApplyFailedException
*
* @param message
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/UnmergedPathsException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/TooLargePackException.java
similarity index 78%
copy from org.eclipse.jgit/src/org/eclipse/jgit/api/errors/UnmergedPathsException.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/api/errors/TooLargePackException.java
index 0990040..3833054 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/UnmergedPathsException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/TooLargePackException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010,Mathias Kinzler <mathias.kinzler at sap.com> and
+ * Copyright (C) 2015, Christian Halstrick <christian.halstrick at sap.com> and
* other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available under the
@@ -37,28 +37,29 @@
*/
package org.eclipse.jgit.api.errors;
-import org.eclipse.jgit.internal.JGitText;
-
/**
- * Thrown when branch deletion fails due to unmerged data
+ * Exception thrown when the server rejected a too large pack
+ *
+ * @since 4.0
*/
-public class UnmergedPathsException extends GitAPIException {
+public class TooLargePackException extends TransportException {
private static final long serialVersionUID = 1L;
/**
- * The default constructor with a default message
+ * @param msg
+ * message describing the transport failure.
*/
- public UnmergedPathsException() {
- this(null);
+ public TooLargePackException(String msg) {
+ super(msg);
}
/**
- * The default constructor with a default message
- *
+ * @param msg
+ * message describing the transport exception.
* @param cause
- * original exception
+ * why the transport failed.
*/
- public UnmergedPathsException(Throwable cause) {
- super(JGitText.get().unmergedPaths, cause);
+ public TooLargePackException(String msg, Throwable cause) {
+ super(msg, cause);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/UnmergedPathsException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/UnmergedPathsException.java
index 0990040..082f94c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/UnmergedPathsException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/UnmergedPathsException.java
@@ -61,4 +61,13 @@ public class UnmergedPathsException extends GitAPIException {
public UnmergedPathsException(Throwable cause) {
super(JGitText.get().unmergedPaths, cause);
}
+
+ /**
+ * @param message
+ * @param cause
+ * @since 4.1
+ */
+ public UnmergedPathsException(String message, Throwable cause) {
+ super(message, cause);
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java
index d3ce685..905ad76 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java
@@ -50,8 +50,10 @@ package org.eclipse.jgit.attributes;
* <li>Set - represented by {@link State#SET}</li>
* <li>Unset - represented by {@link State#UNSET}</li>
* <li>Set to a value - represented by {@link State#CUSTOM}</li>
- * <li>Unspecified - <code>null</code> is used instead of an instance of this
- * class</li>
+ * <li>Unspecified - used to revert an attribute . This is crucial in order to
+ * mark an attribute as unspecified in the attributes map and thus preventing
+ * following (with lower priority) nodes from setting the attribute to a value
+ * at all</li>
* </ul>
* </p>
*
@@ -61,6 +63,7 @@ public final class Attribute {
/**
* The attribute value state
+ * see also https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html
*/
public static enum State {
/** the attribute is set */
@@ -69,6 +72,13 @@ public final class Attribute {
/** the attribute is unset */
UNSET,
+ /**
+ * the attribute appears as if it would not be defined at all
+ *
+ * @since 4.2
+ */
+ UNSPECIFIED,
+
/** the attribute is set to a custom value */
CUSTOM
}
@@ -176,6 +186,8 @@ public final class Attribute {
return key;
case UNSET:
return "-" + key; //$NON-NLS-1$
+ case UNSPECIFIED:
+ return "!" + key; //$NON-NLS-1$
case CUSTOM:
default:
return key + "=" + value; //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attributes.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attributes.java
new file mode 100644
index 0000000..0810e31
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attributes.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2015, Ivan Motsch <ivan.motsch at bsiag.com>
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.attributes;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.eclipse.jgit.attributes.Attribute.State;
+
+/**
+ * Represents a set of attributes for a path
+ * <p>
+ *
+ * @since 4.2
+ */
+public final class Attributes {
+ private final Map<String, Attribute> map = new LinkedHashMap<>();
+
+ /**
+ * Creates a new instance
+ *
+ * @param attributes
+ */
+ public Attributes(Attribute... attributes) {
+ if (attributes != null) {
+ for (Attribute a : attributes) {
+ put(a);
+ }
+ }
+ }
+
+ /**
+ * @return true if the set does not contain any attributes
+ */
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ /**
+ * @param key
+ * @return the attribute or null
+ */
+ public Attribute get(String key) {
+ return map.get(key);
+ }
+
+ /**
+ * @return all attributes
+ */
+ public Collection<Attribute> getAll() {
+ return new ArrayList<>(map.values());
+ }
+
+ /**
+ * @param a
+ */
+ public void put(Attribute a) {
+ map.put(a.getKey(), a);
+ }
+
+ /**
+ * @param key
+ */
+ public void remove(String key) {
+ map.remove(key);
+ }
+
+ /**
+ * @param key
+ * @return true if the {@link Attributes} contains this key
+ */
+ public boolean containsKey(String key) {
+ return map.containsKey(key);
+ }
+
+ /**
+ * Returns the state.
+ *
+ * @param key
+ *
+ * @return the state (never returns <code>null</code>)
+ */
+ public Attribute.State getState(String key) {
+ Attribute a = map.get(key);
+ return a != null ? a.getState() : Attribute.State.UNSPECIFIED;
+ }
+
+ /**
+ * @param key
+ * @return true if the key is {@link State#SET}, false in all other cases
+ */
+ public boolean isSet(String key) {
+ return (getState(key) == State.SET);
+ }
+
+ /**
+ * @param key
+ * @return true if the key is {@link State#UNSET}, false in all other cases
+ */
+ public boolean isUnset(String key) {
+ return (getState(key) == State.UNSET);
+ }
+
+ /**
+ * @param key
+ * @return true if the key is {@link State#UNSPECIFIED}, false in all other
+ * cases
+ */
+ public boolean isUnspecified(String key) {
+ return (getState(key) == State.UNSPECIFIED);
+ }
+
+ /**
+ * @param key
+ * @return true if the key is {@link State#CUSTOM}, false in all other cases
+ * see {@link #getValue(String)} for the value of the key
+ */
+ public boolean isCustom(String key) {
+ return (getState(key) == State.CUSTOM);
+ }
+
+ /**
+ * @param key
+ * @return the attribute value (may be <code>null</code>)
+ */
+ public String getValue(String key) {
+ Attribute a = map.get(key);
+ return a != null ? a.getValue() : null;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append(getClass().getSimpleName());
+ buf.append("["); //$NON-NLS-1$
+ buf.append(" "); //$NON-NLS-1$
+ for (Attribute a : map.values()) {
+ buf.append(a.toString());
+ buf.append(" "); //$NON-NLS-1$
+ }
+ buf.append("]"); //$NON-NLS-1$
+ return buf.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return map.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!(obj instanceof Attributes))
+ return false;
+ Attributes other = (Attributes) obj;
+ return this.map.equals(other.map);
+ }
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java
index 70f56ff..5c0aba2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java
@@ -50,7 +50,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
-import java.util.Map;
import org.eclipse.jgit.lib.Constants;
@@ -134,11 +133,12 @@ public class AttributesNode {
* true if the target item is a directory.
* @param attributes
* Map that will hold the attributes matching this entry path. If
- * it is not empty, this method will NOT override any
- * existing entry.
+ * it is not empty, this method will NOT override any existing
+ * entry.
+ * @since 4.2
*/
- public void getAttributes(String entryPath, boolean isDirectory,
- Map<String, Attribute> attributes) {
+ public void getAttributes(String entryPath,
+ boolean isDirectory, Attributes attributes) {
// Parse rules in the reverse order that they were read since the last
// entry should be used
ListIterator<AttributesRule> ruleIterator = rules.listIterator(rules
@@ -153,7 +153,7 @@ public class AttributesNode {
while (attributeIte.hasPrevious()) {
Attribute attr = attributeIte.previous();
if (!attributes.containsKey(attr.getKey()))
- attributes.put(attr.getKey(), attr);
+ attributes.put(attr);
}
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/LockFailedException.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java
similarity index 63%
copy from org.eclipse.jgit/src/org/eclipse/jgit/errors/LockFailedException.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java
index 18aa9d9..6f2ebad 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/LockFailedException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, GitHub Inc.
+ * Copyright (C) 2014, Arthur Daussy <arthur.daussy at obeo.fr>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -40,51 +40,42 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.errors;
+package org.eclipse.jgit.attributes;
-import java.io.File;
import java.io.IOException;
-import java.text.MessageFormat;
-import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.CoreConfig;
/**
- * An exception occurring when a file cannot be locked
+ * An interface used to retrieve the global and info {@link AttributesNode}s.
+ *
+ * @since 4.2
+ *
*/
-public class LockFailedException extends IOException {
- private static final long serialVersionUID = 1L;
-
- private File file;
+public interface AttributesNodeProvider {
/**
- * Construct a CannotLockException for the given file and message
+ * Retrieve the {@link AttributesNode} that holds the information located
+ * in $GIT_DIR/info/attributes file.
*
- * @param file
- * file that could not be locked
- * @param message
- * exception message
+ * @return the {@link AttributesNode} that holds the information located in
+ * $GIT_DIR/info/attributes file.
+ * @throws IOException
+ * if an error is raised while parsing the attributes file
*/
- public LockFailedException(File file, String message) {
- super(message);
- this.file = file;
- }
+ public AttributesNode getInfoAttributesNode() throws IOException;
/**
- * Construct a CannotLockException for the given file
+ * Retrieve the {@link AttributesNode} that holds the information located
+ * in the global gitattributes file.
*
- * @param file
- * file that could not be locked
+ * @return the {@link AttributesNode} that holds the information located in
+ * the global gitattributes file.
+ * @throws IOException
+ * IOException if an error is raised while parsing the
+ * attributes file
+ * @see CoreConfig#getAttributesFile()
*/
- public LockFailedException(File file) {
- this(file, MessageFormat.format(JGitText.get().cannotLock, file));
- }
+ public AttributesNode getGlobalAttributesNode() throws IOException;
- /**
- * Get the file that could not be locked
- *
- * @return file
- */
- public File getFile() {
- return file;
- }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RejectCommitException.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java
similarity index 80%
rename from org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RejectCommitException.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java
index 6036a27..1037f69 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/RejectCommitException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 Obeo.
+ * Copyright (C) 2015, Christian Halstrick <christian.halstrick at sap.com>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -40,22 +40,16 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.api.errors;
+package org.eclipse.jgit.attributes;
/**
- * Exception thrown when a commit is rejected by a hook (either
- * {@link org.eclipse.jgit.util.Hook#PRE_COMMIT pre-commit} or
- * {@link org.eclipse.jgit.util.Hook#COMMIT_MSG commit-msg}).
+ * Interface for classes which provide git attributes
*
- * @since 3.7
+ * @since 4.2
*/
-public class RejectCommitException extends GitAPIException {
- private static final long serialVersionUID = 1L;
-
+public interface AttributesProvider {
/**
- * @param message
+ * @return the currently active attributes
*/
- public RejectCommitException(String message) {
- super(message);
- }
+ public Attributes getAttributes();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java
index bcac14b..35d18c4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java
@@ -84,6 +84,13 @@ public class AttributesRule {
continue;
}
+ if (attribute.startsWith("!")) {//$NON-NLS-1$
+ if (attribute.length() > 1)
+ result.add(new Attribute(attribute.substring(1),
+ State.UNSPECIFIED));
+ continue;
+ }
+
final int equalsIndex = attribute.indexOf("="); //$NON-NLS-1$
if (equalsIndex == -1)
result.add(new Attribute(attribute, State.SET));
@@ -200,4 +207,16 @@ public class AttributesRule {
boolean match = matcher.matches(relativeTarget, isDirectory);
return match;
}
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(pattern);
+ for (Attribute a : attributes) {
+ sb.append(" "); //$NON-NLS-1$
+ sb.append(a);
+ }
+ return sb.toString();
+
+ }
}
\ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java
index e69cfa9..fa6fe75 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameGenerator.java
@@ -113,7 +113,7 @@ import org.eclipse.jgit.treewalk.filter.TreeFilter;
* the ancestor, until there are no more lines to acquire information on, or the
* file's creation point is discovered in history.
*/
-public class BlameGenerator {
+public class BlameGenerator implements AutoCloseable {
private final Repository repository;
private final PathFilter resultPath;
@@ -172,14 +172,13 @@ public class BlameGenerator {
throw new IllegalStateException();
if (revPool != null)
- revPool.release();
+ revPool.close();
if (reverse)
revPool = new ReverseWalk(getRepository());
else
revPool = new RevWalk(getRepository());
- revPool.setRetainBody(true);
SEEN = revPool.newFlag("SEEN"); //$NON-NLS-1$
reader = revPool.getObjectReader();
treeWalk = new TreeWalk(reader);
@@ -451,7 +450,7 @@ public class BlameGenerator {
r.computeAll();
return r;
} finally {
- release();
+ close();
}
}
@@ -514,7 +513,7 @@ public class BlameGenerator {
}
private boolean done() {
- release();
+ close();
return false;
}
@@ -937,9 +936,14 @@ public class BlameGenerator {
return queue != null ? queue.sourceText : null;
}
- /** Release the current blame session. */
- public void release() {
- revPool.release();
+ /**
+ * Release the current blame session.
+ *
+ * @since 4.0
+ */
+ @Override
+ public void close() {
+ revPool.close();
queue = null;
outCandidate = null;
outRegion = null;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameResult.java
index 735eef7..e34db38 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/blame/BlameResult.java
@@ -85,7 +85,7 @@ public class BlameResult {
String path = gen.getResultPath();
RawText contents = gen.getResultContents();
if (contents == null) {
- gen.release();
+ gen.close();
return null;
}
return new BlameResult(gen, path, contents);
@@ -239,7 +239,7 @@ public class BlameResult {
while (gen.next())
loadFrom(gen);
} finally {
- gen.release();
+ gen.close();
generator = null;
}
}
@@ -265,7 +265,7 @@ public class BlameResult {
lastLength = gen.getRegionLength();
return gen.getResultStart();
} else {
- gen.release();
+ gen.close();
generator = null;
return -1;
}
@@ -300,7 +300,7 @@ public class BlameResult {
return;
if (!gen.next()) {
- gen.release();
+ gen.close();
generator = null;
return;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java
index 3c780e7..444ab1c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java
@@ -132,7 +132,11 @@ public abstract class ContentSource {
@Override
public long size(String path, ObjectId id) throws IOException {
- return reader.getObjectSize(id, Constants.OBJ_BLOB);
+ try {
+ return reader.getObjectSize(id, Constants.OBJ_BLOB);
+ } catch (MissingObjectException ignore) {
+ return 0;
+ }
}
@Override
@@ -148,7 +152,7 @@ public abstract class ContentSource {
private String current;
- private WorkingTreeIterator ptr;
+ WorkingTreeIterator ptr;
WorkingTreeSource(WorkingTreeIterator iterator) {
this.tw = new TreeWalk((ObjectReader) null);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
index d339d6a..4c0ed38 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
@@ -104,7 +104,7 @@ import org.eclipse.jgit.util.io.DisabledOutputStream;
/**
* Format a Git style patch script.
*/
-public class DiffFormatter {
+public class DiffFormatter implements AutoCloseable {
private static final int DEFAULT_BINARY_FILE_THRESHOLD = PackConfig.DEFAULT_BIG_FILE_THRESHOLD;
private static final byte[] noNewLine = encodeASCII("\\ No newline at end of file\n"); //$NON-NLS-1$
@@ -173,7 +173,7 @@ public class DiffFormatter {
*/
public void setRepository(Repository repository) {
if (reader != null)
- reader.release();
+ reader.close();
db = repository;
reader = db.newObjectReader();
@@ -380,10 +380,15 @@ public class DiffFormatter {
out.flush();
}
- /** Release the internal ObjectReader state. */
- public void release() {
+ /**
+ * Release the internal ObjectReader state.
+ *
+ * @since 4.0
+ */
+ @Override
+ public void close() {
if (reader != null)
- reader.release();
+ reader.close();
}
/**
@@ -409,10 +414,11 @@ public class DiffFormatter {
throws IOException {
assertHaveRepository();
- RevWalk rw = new RevWalk(reader);
- RevTree aTree = a != null ? rw.parseTree(a) : null;
- RevTree bTree = b != null ? rw.parseTree(b) : null;
- return scan(aTree, bTree);
+ try (RevWalk rw = new RevWalk(reader)) {
+ RevTree aTree = a != null ? rw.parseTree(a) : null;
+ RevTree bTree = b != null ? rw.parseTree(b) : null;
+ return scan(aTree, bTree);
+ }
}
/**
@@ -659,16 +665,9 @@ public class DiffFormatter {
format(res.header, res.a, res.b);
}
- private static void writeGitLinkDiffText(OutputStream o, DiffEntry ent)
- throws IOException {
- if (ent.getOldMode() == GITLINK) {
- o.write(encodeASCII("-Subproject commit " + ent.getOldId().name() //$NON-NLS-1$
- + "\n")); //$NON-NLS-1$
- }
- if (ent.getNewMode() == GITLINK) {
- o.write(encodeASCII("+Subproject commit " + ent.getNewId().name() //$NON-NLS-1$
- + "\n")); //$NON-NLS-1$
- }
+ private static byte[] writeGitLinkText(AbbreviatedObjectId id) {
+ return encodeASCII("Subproject commit " + id.name() //$NON-NLS-1$
+ + "\n"); //$NON-NLS-1$
}
private String format(AbbreviatedObjectId id) {
@@ -932,13 +931,7 @@ public class DiffFormatter {
formatHeader(buf, ent);
- if (ent.getOldMode() == GITLINK || ent.getNewMode() == GITLINK) {
- formatOldNewPaths(buf, ent);
- writeGitLinkDiffText(buf, ent);
- editList = new EditList();
- type = PatchType.UNIFIED;
-
- } else if (ent.getOldId() == null || ent.getNewId() == null) {
+ if (ent.getOldId() == null || ent.getNewId() == null) {
// Content not changed (e.g. only mode, pure rename)
editList = new EditList();
type = PatchType.UNIFIED;
@@ -946,8 +939,15 @@ public class DiffFormatter {
} else {
assertHaveRepository();
- byte[] aRaw = open(OLD, ent);
- byte[] bRaw = open(NEW, ent);
+ byte[] aRaw, bRaw;
+
+ if (ent.getOldMode() == GITLINK || ent.getNewMode() == GITLINK) {
+ aRaw = writeGitLinkText(ent.getOldId());
+ bRaw = writeGitLinkText(ent.getNewId());
+ } else {
+ aRaw = open(OLD, ent);
+ bRaw = open(NEW, ent);
+ }
if (aRaw == BINARY || bRaw == BINARY //
|| RawText.isBinary(aRaw) || RawText.isBinary(bRaw)) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/HistogramDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/HistogramDiff.java
index e57faaf..2f5c9ea 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/HistogramDiff.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/HistogramDiff.java
@@ -94,7 +94,7 @@ import java.util.List;
*/
public class HistogramDiff extends LowLevelDiffAlgorithm {
/** Algorithm to use when there are too many element occurrences. */
- private DiffAlgorithm fallback = MyersDiff.INSTANCE;
+ DiffAlgorithm fallback = MyersDiff.INSTANCE;
/**
* Maximum number of positions to consider for a given element hash.
@@ -103,7 +103,7 @@ public class HistogramDiff extends LowLevelDiffAlgorithm {
* size is capped to ensure search is linear time at O(len_A + len_B) rather
* than quadratic at O(len_A * len_B).
*/
- private int maxChainLength = 64;
+ int maxChainLength = 64;
/**
* Set the algorithm used when there are too many element occurrences.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/MyersDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/MyersDiff.java
index 71247eb..9810a6a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/MyersDiff.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/MyersDiff.java
@@ -46,6 +46,7 @@ package org.eclipse.jgit.diff;
import java.text.MessageFormat;
+import org.eclipse.jgit.errors.DiffInterruptedException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.util.IntList;
import org.eclipse.jgit.util.LongList;
@@ -113,6 +114,7 @@ import org.eclipse.jgit.util.LongList;
public class MyersDiff<S extends Sequence> {
/** Singleton instance of MyersDiff. */
public static final DiffAlgorithm INSTANCE = new LowLevelDiffAlgorithm() {
+ @SuppressWarnings("unused")
@Override
public <S extends Sequence> void diffNonCommon(EditList edits,
HashedSequenceComparator<S> cmp, HashedSequence<S> a,
@@ -406,6 +408,9 @@ if (k < beginK || k > endK)
// TODO: move end points out of the loop to avoid conditionals inside the loop
// go backwards so that we can avoid temp vars
for (int k = endK; k >= beginK; k -= 2) {
+ if (Thread.interrupted()) {
+ throw new DiffInterruptedException();
+ }
int left = -1, right = -1;
long leftSnake = -1L, rightSnake = -1L;
// TODO: refactor into its own function
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
index b819ad0..8865b62 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
@@ -323,7 +323,7 @@ public class RenameDetector {
try {
return compute(objectReader, pm);
} finally {
- objectReader.release();
+ objectReader.close();
}
}
return Collections.unmodifiableList(entries);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java
index f376b8e..1c40d7f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/SimilarityIndex.java
@@ -63,10 +63,13 @@ import org.eclipse.jgit.lib.ObjectStream;
* will not exceed 1 MiB per instance. The index starts out at a smaller size
* (closer to 2 KiB), but may grow as more distinct blocks within the scanned
* file are discovered.
+ *
+ * @since 4.0
*/
-class SimilarityIndex {
+public class SimilarityIndex {
/** A special {@link TableFullException} used in place of OutOfMemoryError. */
- private static final TableFullException TABLE_FULL_OUT_OF_MEMORY = new TableFullException();
+ public static final TableFullException
+ TABLE_FULL_OUT_OF_MEMORY = new TableFullException();
/**
* Shift to apply before storing a key.
@@ -105,6 +108,26 @@ class SimilarityIndex {
/** {@code idHash.length == 1 << idHashBits}. */
private int idHashBits;
+ /**
+ * Create a new similarity index for the given object
+ *
+ * @param obj
+ * the object to hash
+ * @return similarity index for this object
+ * @throws IOException
+ * file contents cannot be read from the repository.
+ * @throws TableFullException
+ * object hashing overflowed the storage capacity of the
+ * SimilarityIndex.
+ */
+ public static SimilarityIndex create(ObjectLoader obj) throws IOException,
+ TableFullException {
+ SimilarityIndex idx = new SimilarityIndex();
+ idx.hash(obj);
+ idx.sort();
+ return idx;
+ }
+
SimilarityIndex() {
idHashBits = 8;
idHash = new long[1 << idHashBits];
@@ -212,7 +235,27 @@ class SimilarityIndex {
Arrays.sort(idHash);
}
- int score(SimilarityIndex dst, int maxScore) {
+ /**
+ * Compute the similarity score between this index and another.
+ * <p>
+ * A region of a file is defined as a line in a text file or a fixed-size
+ * block in a binary file. To prepare an index, each region in the file is
+ * hashed; the values and counts of hashes are retained in a sorted table.
+ * Define the similarity fraction F as the the count of matching regions
+ * between the two files divided between the maximum count of regions in
+ * either file. The similarity score is F multiplied by the maxScore
+ * constant, yielding a range [0, maxScore]. It is defined as maxScore for
+ * the degenerate case of two empty files.
+ * <p>
+ * The similarity score is symmetrical; i.e. a.score(b) == b.score(a).
+ *
+ * @param dst
+ * the other index
+ * @param maxScore
+ * the score representing a 100% match
+ * @return the similarity score
+ */
+ public int score(SimilarityIndex dst, int maxScore) {
long max = Math.max(hashedCnt, dst.hashedCnt);
if (max == 0)
return maxScore;
@@ -381,7 +424,8 @@ class SimilarityIndex {
return v & MAX_COUNT;
}
- static class TableFullException extends Exception {
+ /** Thrown by {@code create()} when file is too large. */
+ public static class TableFullException extends Exception {
private static final long serialVersionUID = 1L;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/BaseDirCacheEditor.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/BaseDirCacheEditor.java
index 70f80ae..0fbc1f8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/BaseDirCacheEditor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/BaseDirCacheEditor.java
@@ -44,8 +44,13 @@
package org.eclipse.jgit.dircache;
+import static org.eclipse.jgit.lib.FileMode.TYPE_TREE;
+import static org.eclipse.jgit.util.Paths.compareSameName;
+
import java.io.IOException;
+import org.eclipse.jgit.errors.DirCacheNameConflictException;
+
/**
* Generic update/editing support for {@link DirCache}.
* <p>
@@ -168,6 +173,7 @@ abstract class BaseDirCacheEditor {
* {@link #finish()}, and only after {@link #entries} is sorted.
*/
protected void replace() {
+ checkNameConflicts();
if (entryCnt < entries.length / 2) {
final DirCacheEntry[] n = new DirCacheEntry[entryCnt];
System.arraycopy(entries, 0, n, 0, entryCnt);
@@ -176,6 +182,76 @@ abstract class BaseDirCacheEditor {
cache.replace(entries, entryCnt);
}
+ private void checkNameConflicts() {
+ int end = entryCnt - 1;
+ for (int eIdx = 0; eIdx < end; eIdx++) {
+ DirCacheEntry e = entries[eIdx];
+ if (e.getStage() != 0) {
+ continue;
+ }
+
+ byte[] ePath = e.path;
+ int prefixLen = lastSlash(ePath) + 1;
+
+ for (int nIdx = eIdx + 1; nIdx < entryCnt; nIdx++) {
+ DirCacheEntry n = entries[nIdx];
+ if (n.getStage() != 0) {
+ continue;
+ }
+
+ byte[] nPath = n.path;
+ if (!startsWith(ePath, nPath, prefixLen)) {
+ // Different prefix; this entry is in another directory.
+ break;
+ }
+
+ int s = nextSlash(nPath, prefixLen);
+ int m = s < nPath.length ? TYPE_TREE : n.getRawMode();
+ int cmp = compareSameName(
+ ePath, prefixLen, ePath.length,
+ nPath, prefixLen, s, m);
+ if (cmp < 0) {
+ break;
+ } else if (cmp == 0) {
+ throw new DirCacheNameConflictException(
+ e.getPathString(),
+ n.getPathString());
+ }
+ }
+ }
+ }
+
+ private static int lastSlash(byte[] path) {
+ for (int i = path.length - 1; i >= 0; i--) {
+ if (path[i] == '/') {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private static int nextSlash(byte[] b, int p) {
+ final int n = b.length;
+ for (; p < n; p++) {
+ if (b[p] == '/') {
+ return p;
+ }
+ }
+ return n;
+ }
+
+ private static boolean startsWith(byte[] a, byte[] b, int n) {
+ if (b.length < n) {
+ return false;
+ }
+ for (n--; n >= 0; n--) {
+ if (a[n] != b[n]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
/**
* Finish, write, commit this change, and release the index lock.
* <p>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
index 98a1c8c..ecdfe82 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
@@ -63,6 +63,7 @@ import java.util.Comparator;
import java.util.List;
import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.IndexReadException;
import org.eclipse.jgit.errors.LockFailedException;
import org.eclipse.jgit.errors.UnmergedPathException;
import org.eclipse.jgit.events.IndexChangedEvent;
@@ -70,12 +71,15 @@ import org.eclipse.jgit.events.IndexChangedListener;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.file.FileSnapshot;
import org.eclipse.jgit.internal.storage.file.LockFile;
+import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.IO;
@@ -145,6 +149,28 @@ public class DirCache {
}
/**
+ * Create a new in memory index read from the contents of a tree.
+ *
+ * @param reader
+ * reader to access the tree objects from a repository.
+ * @param treeId
+ * tree to read. Must identify a tree, not a tree-ish.
+ * @return a new cache which has no backing store file, but contains the
+ * contents of {@code treeId}.
+ * @throws IOException
+ * one or more trees not available from the ObjectReader.
+ * @since 4.2
+ */
+ public static DirCache read(ObjectReader reader, AnyObjectId treeId)
+ throws IOException {
+ DirCache d = newInCore();
+ DirCacheBuilder b = d.builder();
+ b.addTree(null, DirCacheEntry.STAGE_0, reader, treeId);
+ b.finish();
+ return d;
+ }
+
+ /**
* Create a new in-core index representation and read an index from disk.
* <p>
* The new index will be read before it is returned to the caller. Read
@@ -417,6 +443,12 @@ public class DirCache {
}
}
} catch (FileNotFoundException fnfe) {
+ if (liveFile.exists()) {
+ // Panic: the index file exists but we can't read it
+ throw new IndexReadException(
+ MessageFormat.format(JGitText.get().cannotReadIndex,
+ liveFile.getAbsolutePath(), fnfe));
+ }
// Someone must have deleted it between our exists test
// and actually opening the path. That's fine, its empty.
//
@@ -768,8 +800,11 @@ public class DirCache {
* information. If < 0 the entry does not exist in the index.
* @since 3.4
*/
- public int findEntry(final byte[] p, final int pLen) {
- int low = 0;
+ public int findEntry(byte[] p, int pLen) {
+ return findEntry(0, p, pLen);
+ }
+
+ int findEntry(int low, byte[] p, int pLen) {
int high = entryCnt;
while (low < high) {
int mid = (low + high) >>> 1;
@@ -869,8 +904,8 @@ public class DirCache {
*/
public DirCacheEntry[] getEntriesWithin(String path) {
if (path.length() == 0) {
- final DirCacheEntry[] r = new DirCacheEntry[sortedEntries.length];
- System.arraycopy(sortedEntries, 0, r, 0, sortedEntries.length);
+ DirCacheEntry[] r = new DirCacheEntry[entryCnt];
+ System.arraycopy(sortedEntries, 0, r, 0, entryCnt);
return r;
}
if (!path.endsWith("/")) //$NON-NLS-1$
@@ -961,9 +996,9 @@ public class DirCache {
* @throws IOException
*/
private void updateSmudgedEntries() throws IOException {
- TreeWalk walk = new TreeWalk(repository);
List<String> paths = new ArrayList<String>(128);
- try {
+ try (TreeWalk walk = new TreeWalk(repository)) {
+ walk.setOperationType(OperationType.CHECKIN_OP);
for (int i = 0; i < entryCnt; i++)
if (sortedEntries[i].isSmudged())
paths.add(sortedEntries[i].getPathString());
@@ -975,6 +1010,7 @@ public class DirCache {
FileTreeIterator fIter = new FileTreeIterator(repository);
walk.addTree(iIter);
walk.addTree(fIter);
+ fIter.setDirCacheIterator(walk, 0);
walk.setRecursive(true);
while (walk.next()) {
iIter = walk.getTree(0, DirCacheIterator.class);
@@ -989,8 +1025,6 @@ public class DirCache {
entry.setLastModified(fIter.getEntryLastModified());
}
}
- } finally {
- walk.release();
}
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuildIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuildIterator.java
index da55306..c10e416 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuildIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuildIterator.java
@@ -130,4 +130,9 @@ public class DirCacheBuildIterator extends DirCacheIterator {
if (cur < cnt)
builder.keep(cur, cnt - cur);
}
+
+ @Override
+ protected boolean needsStopWalk() {
+ return ptr < cache.getEntryCount();
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java
index 6c7a70c..cfebe2d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java
@@ -44,6 +44,9 @@
package org.eclipse.jgit.dircache;
+import static org.eclipse.jgit.lib.FileMode.TYPE_MASK;
+import static org.eclipse.jgit.lib.FileMode.TYPE_TREE;
+
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Arrays;
@@ -51,9 +54,7 @@ import java.util.Arrays;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
-import org.eclipse.jgit.treewalk.TreeWalk;
/**
* Updates a {@link DirCache} by adding individual {@link DirCacheEntry}s.
@@ -102,8 +103,9 @@ public class DirCacheBuilder extends BaseDirCacheEditor {
*/
public void add(final DirCacheEntry newEntry) {
if (newEntry.getRawMode() == 0)
- throw new IllegalArgumentException(MessageFormat.format(JGitText.get().fileModeNotSetForPath
- , newEntry.getPathString()));
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().fileModeNotSetForPath,
+ newEntry.getPathString()));
beforeAdd(newEntry);
fastAdd(newEntry);
}
@@ -162,27 +164,56 @@ public class DirCacheBuilder extends BaseDirCacheEditor {
* @throws IOException
* a tree cannot be read to iterate through its entries.
*/
- public void addTree(final byte[] pathPrefix, final int stage,
- final ObjectReader reader, final AnyObjectId tree) throws IOException {
- final TreeWalk tw = new TreeWalk(reader);
- tw.addTree(new CanonicalTreeParser(pathPrefix, reader, tree
- .toObjectId()));
- tw.setRecursive(true);
- if (tw.next()) {
- final DirCacheEntry newEntry = toEntry(stage, tw);
- beforeAdd(newEntry);
- fastAdd(newEntry);
- while (tw.next())
- fastAdd(toEntry(stage, tw));
+ public void addTree(byte[] pathPrefix, int stage, ObjectReader reader,
+ AnyObjectId tree) throws IOException {
+ CanonicalTreeParser p = createTreeParser(pathPrefix, reader, tree);
+ while (!p.eof()) {
+ if (isTree(p)) {
+ p = enterTree(p, reader);
+ continue;
+ }
+
+ DirCacheEntry first = toEntry(stage, p);
+ beforeAdd(first);
+ fastAdd(first);
+ p = p.next();
+ break;
+ }
+
+ // Rest of tree entries are correctly sorted; use fastAdd().
+ while (!p.eof()) {
+ if (isTree(p)) {
+ p = enterTree(p, reader);
+ } else {
+ fastAdd(toEntry(stage, p));
+ p = p.next();
+ }
}
}
- private DirCacheEntry toEntry(final int stage, final TreeWalk tw) {
- final DirCacheEntry e = new DirCacheEntry(tw.getRawPath(), stage);
- final AbstractTreeIterator i;
+ private static CanonicalTreeParser createTreeParser(byte[] pathPrefix,
+ ObjectReader reader, AnyObjectId tree) throws IOException {
+ return new CanonicalTreeParser(pathPrefix, reader, tree);
+ }
+
+ private static boolean isTree(CanonicalTreeParser p) {
+ return (p.getEntryRawMode() & TYPE_MASK) == TYPE_TREE;
+ }
+
+ private static CanonicalTreeParser enterTree(CanonicalTreeParser p,
+ ObjectReader reader) throws IOException {
+ p = p.createSubtreeIterator(reader);
+ return p.eof() ? p.next() : p;
+ }
+
+ private static DirCacheEntry toEntry(int stage, CanonicalTreeParser i) {
+ byte[] buf = i.getEntryPathBuffer();
+ int len = i.getEntryPathLength();
+ byte[] path = new byte[len];
+ System.arraycopy(buf, 0, path, 0, len);
- i = tw.getTree(0, AbstractTreeIterator.class);
- e.setFileMode(tw.getFileMode(0));
+ DirCacheEntry e = new DirCacheEntry(path, stage);
+ e.setFileMode(i.getEntryRawMode());
e.setObjectIdFromRaw(i.idBuffer(), i.idOffset());
return e;
}
@@ -242,9 +273,9 @@ public class DirCacheBuilder extends BaseDirCacheEditor {
sorted = true;
}
- private static IllegalStateException bad(final DirCacheEntry a,
- final String msg) {
- return new IllegalStateException(msg + ": " + a.getStage() + " " //$NON-NLS-1$ //$NON-NLS-2$
- + a.getPathString());
+ private static IllegalStateException bad(DirCacheEntry a, String msg) {
+ return new IllegalStateException(String.format(
+ "%s: %d %s", //$NON-NLS-1$
+ msg, Integer.valueOf(a.getStage()), a.getPathString()));
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
index 015d9d6..a1e1d15 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -46,21 +46,25 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.nio.file.StandardCopyOption;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.eclipse.jgit.api.errors.FilterFailedException;
import org.eclipse.jgit.errors.CheckoutConflictException;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.IndexWriteException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
import org.eclipse.jgit.lib.CoreConfig.SymLinks;
import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectChecker;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
@@ -76,6 +80,7 @@ import org.eclipse.jgit.treewalk.WorkingTreeIterator;
import org.eclipse.jgit.treewalk.WorkingTreeOptions;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.FS.ExecutionResult;
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.RawParseUtils;
import org.eclipse.jgit.util.SystemReader;
@@ -85,9 +90,10 @@ import org.eclipse.jgit.util.io.AutoCRLFOutputStream;
* This class handles checking out one or two trees merging with the index.
*/
public class DirCacheCheckout {
+ private static final int MAX_EXCEPTION_TEXT_SIZE = 10 * 1024;
private Repository repo;
- private HashMap<String, ObjectId> updated = new HashMap<String, ObjectId>();
+ private HashMap<String, String> updated = new HashMap<String, String>();
private ArrayList<String> conflicts = new ArrayList<String>();
@@ -112,9 +118,9 @@ public class DirCacheCheckout {
private boolean emptyDirCache;
/**
- * @return a list of updated paths and objectIds
+ * @return a list of updated paths and smudgeFilterCommands
*/
- public Map<String, ObjectId> getUpdated() {
+ public Map<String, String> getUpdated() {
return updated;
}
@@ -403,8 +409,7 @@ public class DirCacheCheckout {
MissingObjectException, IncorrectObjectTypeException,
CheckoutConflictException, IndexWriteException {
toBeDeleted.clear();
- ObjectReader objectReader = repo.getObjectDatabase().newReader();
- try {
+ try (ObjectReader objectReader = repo.getObjectDatabase().newReader()) {
if (headCommitTree != null)
preScanTwoTrees();
else
@@ -448,14 +453,13 @@ public class DirCacheCheckout {
for (String path : updated.keySet()) {
DirCacheEntry entry = dc.getEntry(path);
if (!FileMode.GITLINK.equals(entry.getRawMode()))
- checkoutEntry(repo, entry, objectReader);
+ checkoutEntry(repo, entry, objectReader, false,
+ updated.get(path));
}
// commit the index builder - a new index is persisted
if (!builder.commit())
throw new IndexWriteException();
- } finally {
- objectReader.release();
}
return toBeDeleted.size() == 0;
}
@@ -999,9 +1003,12 @@ public class DirCacheCheckout {
removed.add(path);
}
- private void update(String path, ObjectId mId, FileMode mode) {
+ private void update(String path, ObjectId mId, FileMode mode)
+ throws IOException {
if (!FileMode.TREE.equals(mode)) {
- updated.put(path, mId);
+ updated.put(path,
+ walk.getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE));
+
DirCacheEntry entry = new DirCacheEntry(path, DirCacheEntry.STAGE_0);
entry.setObjectId(mId);
entry.setFileMode(mode);
@@ -1056,8 +1063,7 @@ public class DirCacheCheckout {
*/
private boolean isModifiedSubtree_IndexWorkingtree(String path)
throws CorruptObjectException, IOException {
- NameConflictTreeWalk tw = new NameConflictTreeWalk(repo);
- try {
+ try (NameConflictTreeWalk tw = new NameConflictTreeWalk(repo)) {
tw.addTree(new DirCacheIterator(dc));
tw.addTree(new FileTreeIterator(repo));
tw.setRecursive(true);
@@ -1075,8 +1081,6 @@ public class DirCacheCheckout {
}
}
return false;
- } finally {
- tw.release();
}
}
@@ -1105,8 +1109,7 @@ public class DirCacheCheckout {
*/
private boolean isModifiedSubtree_IndexTree(String path, ObjectId tree)
throws CorruptObjectException, IOException {
- NameConflictTreeWalk tw = new NameConflictTreeWalk(repo);
- try {
+ try (NameConflictTreeWalk tw = new NameConflictTreeWalk(repo)) {
tw.addTree(new DirCacheIterator(dc));
tw.addTree(tree);
tw.setRecursive(true);
@@ -1124,8 +1127,6 @@ public class DirCacheCheckout {
return true;
}
return false;
- } finally {
- tw.release();
}
}
@@ -1133,11 +1134,14 @@ public class DirCacheCheckout {
* Updates the file in the working tree with content and mode from an entry
* in the index. The new content is first written to a new temporary file in
* the same directory as the real file. Then that new file is renamed to the
- * final filename. Use this method only for checkout of a single entry.
- * Otherwise use
- * {@code checkoutEntry(Repository, File f, DirCacheEntry, ObjectReader)}
- * instead which allows to reuse one {@code ObjectReader} for multiple
- * entries.
+ * final filename.
+ *
+ * <p>
+ * <b>Note:</b> if the entry path on local file system exists as a non-empty
+ * directory, and the target entry type is a link or file, the checkout will
+ * fail with {@link IOException} since existing non-empty directory cannot
+ * be renamed to file or link without deleting it recursively.
+ * </p>
*
* <p>
* TODO: this method works directly on File IO, we may need another
@@ -1145,23 +1149,18 @@ public class DirCacheCheckout {
* Eclipse that Files in the workspace got changed
* </p>
*
- * @param repository
- * @param f
- * this parameter is ignored.
+ * @param repo
+ * repository managing the destination work tree.
* @param entry
* the entry containing new mode and content
+ * @param or
+ * object reader to use for checkout
* @throws IOException
- * @deprecated Use the overloaded form that accepts {@link ObjectReader}.
+ * @since 3.6
*/
- @Deprecated
- public static void checkoutEntry(final Repository repository, File f,
- DirCacheEntry entry) throws IOException {
- ObjectReader or = repository.newObjectReader();
- try {
- checkoutEntry(repository, f, entry, or);
- } finally {
- or.release();
- }
+ public static void checkoutEntry(Repository repo, DirCacheEntry entry,
+ ObjectReader or) throws IOException {
+ checkoutEntry(repo, entry, or, false, null);
}
/**
@@ -1171,29 +1170,33 @@ public class DirCacheCheckout {
* final filename.
*
* <p>
+ * <b>Note:</b> if the entry path on local file system exists as a file, it
+ * will be deleted and if it exists as a directory, it will be deleted
+ * recursively, independently if has any content.
+ * </p>
+ *
+ * <p>
* TODO: this method works directly on File IO, we may need another
* abstraction (like WorkingTreeIterator). This way we could tell e.g.
* Eclipse that Files in the workspace got changed
* </p>
*
* @param repo
- * @param f
- * this parameter is ignored.
+ * repository managing the destination work tree.
* @param entry
* the entry containing new mode and content
* @param or
* object reader to use for checkout
+ * @param deleteRecursive
+ * true to recursively delete final path if it exists on the file
+ * system
+ *
* @throws IOException
- * @deprecated Do not pass File object.
+ * @since 4.2
*/
- @Deprecated
- public static void checkoutEntry(final Repository repo, File f,
- DirCacheEntry entry, ObjectReader or) throws IOException {
- if (f == null || repo.getWorkTree() == null)
- throw new IllegalArgumentException();
- if (!f.equals(new File(repo.getWorkTree(), entry.getPathString())))
- throw new IllegalArgumentException();
- checkoutEntry(repo, entry, or);
+ public static void checkoutEntry(Repository repo, DirCacheEntry entry,
+ ObjectReader or, boolean deleteRecursive) throws IOException {
+ checkoutEntry(repo, entry, or, deleteRecursive, null);
}
/**
@@ -1203,6 +1206,12 @@ public class DirCacheCheckout {
* final filename.
*
* <p>
+ * <b>Note:</b> if the entry path on local file system exists as a file, it
+ * will be deleted and if it exists as a directory, it will be deleted
+ * recursively, independently if has any content.
+ * </p>
+ *
+ * <p>
* TODO: this method works directly on File IO, we may need another
* abstraction (like WorkingTreeIterator). This way we could tell e.g.
* Eclipse that Files in the workspace got changed
@@ -1214,11 +1223,19 @@ public class DirCacheCheckout {
* the entry containing new mode and content
* @param or
* object reader to use for checkout
+ * @param deleteRecursive
+ * true to recursively delete final path if it exists on the file
+ * system
+ * @param smudgeFilterCommand
+ * the filter command to be run for smudging the entry to be
+ * checked out
+ *
* @throws IOException
- * @since 3.6
+ * @since 4.2
*/
public static void checkoutEntry(Repository repo, DirCacheEntry entry,
- ObjectReader or) throws IOException {
+ ObjectReader or, boolean deleteRecursive,
+ String smudgeFilterCommand) throws IOException {
ObjectLoader ol = or.open(entry.getObjectId());
File f = new File(repo.getWorkTree(), entry.getPathString());
File parentDir = f.getParentFile();
@@ -1229,6 +1246,9 @@ public class DirCacheCheckout {
&& opt.getSymLinks() == SymLinks.TRUE) {
byte[] bytes = ol.getBytes();
String target = RawParseUtils.decode(bytes);
+ if (deleteRecursive && f.isDirectory()) {
+ FileUtils.delete(f, FileUtils.RECURSIVE);
+ }
fs.createSymLink(f, target);
entry.setLength(bytes.length);
entry.setLastModified(fs.lastModified(f));
@@ -1240,14 +1260,52 @@ public class DirCacheCheckout {
OutputStream channel = new FileOutputStream(tmpFile);
if (opt.getAutoCRLF() == AutoCRLF.TRUE)
channel = new AutoCRLFOutputStream(channel);
- try {
- ol.copyTo(channel);
- } finally {
- channel.close();
+ if (smudgeFilterCommand != null) {
+ ProcessBuilder filterProcessBuilder = fs
+ .runInShell(smudgeFilterCommand, new String[0]);
+ filterProcessBuilder.directory(repo.getWorkTree());
+ filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY,
+ repo.getDirectory().getAbsolutePath());
+ ExecutionResult result;
+ int rc;
+ try {
+ // TODO: wire correctly with AUTOCRLF
+ result = fs.execute(filterProcessBuilder, ol.openStream());
+ rc = result.getRc();
+ if (rc == 0) {
+ result.getStdout().writeTo(channel,
+ NullProgressMonitor.INSTANCE);
+ }
+ } catch (IOException | InterruptedException e) {
+ throw new IOException(new FilterFailedException(e,
+ smudgeFilterCommand, entry.getPathString()));
+
+ } finally {
+ channel.close();
+ }
+ if (rc != 0) {
+ throw new IOException(new FilterFailedException(rc,
+ smudgeFilterCommand, entry.getPathString(),
+ result.getStdout().toByteArray(MAX_EXCEPTION_TEXT_SIZE),
+ RawParseUtils.decode(result.getStderr()
+ .toByteArray(MAX_EXCEPTION_TEXT_SIZE))));
+ }
+ } else {
+ try {
+ ol.copyTo(channel);
+ } finally {
+ channel.close();
+ }
+ }
+ // The entry needs to correspond to the on-disk filesize. If the content
+ // was filtered (either by autocrlf handling or smudge filters) ask the
+ // filesystem again for the length. Otherwise the objectloader knows the
+ // size
+ if (opt.getAutoCRLF() == AutoCRLF.TRUE || smudgeFilterCommand != null) {
+ entry.setLength(tmpFile.length());
+ } else {
+ entry.setLength(ol.getSize());
}
- entry.setLength(opt.getAutoCRLF() == AutoCRLF.TRUE ? //
- tmpFile.length() // AutoCRLF wants on-disk-size
- : (int) ol.getSize());
if (opt.isFileMode() && fs.supportsExecute()) {
if (FileMode.EXECUTABLE_FILE.equals(entry.getRawMode())) {
@@ -1259,15 +1317,24 @@ public class DirCacheCheckout {
}
}
try {
- FileUtils.rename(tmpFile, f);
+ if (deleteRecursive && f.isDirectory()) {
+ FileUtils.delete(f, FileUtils.RECURSIVE);
+ }
+ FileUtils.rename(tmpFile, f, StandardCopyOption.ATOMIC_MOVE);
} catch (IOException e) {
- throw new IOException(MessageFormat.format(
- JGitText.get().renameFileFailed, tmpFile.getPath(),
- f.getPath()));
+ throw new IOException(
+ MessageFormat.format(JGitText.get().renameFileFailed,
+ tmpFile.getPath(), f.getPath()),
+ e);
+ } finally {
+ if (tmpFile.exists()) {
+ FileUtils.delete(tmpFile);
+ }
}
entry.setLastModified(f.lastModified());
}
+ @SuppressWarnings("deprecation")
private static void checkValidPath(CanonicalTreeParser t)
throws InvalidPathException {
ObjectChecker chk = new ObjectChecker()
@@ -1277,26 +1344,6 @@ public class DirCacheCheckout {
checkValidPathSegment(chk, i);
}
- /**
- * Check if path is a valid path for a checked out file name or ref name.
- *
- * @param path
- * @throws InvalidPathException
- * if the path is invalid
- * @since 3.3
- * @deprecated Use {@link SystemReader#checkPath(String)}.
- */
- @Deprecated
- public static void checkValidPath(String path) throws InvalidPathException {
- try {
- SystemReader.getInstance().checkPath(path);
- } catch (CorruptObjectException e) {
- InvalidPathException p = new InvalidPathException(path);
- p.initCause(e);
- throw p;
- }
- }
-
private static void checkValidPathSegment(ObjectChecker chk,
CanonicalTreeParser t) throws InvalidPathException {
try {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java
index 13885d3..c987c96 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java
@@ -44,6 +44,10 @@
package org.eclipse.jgit.dircache;
+import static org.eclipse.jgit.dircache.DirCache.cmp;
+import static org.eclipse.jgit.dircache.DirCacheTree.peq;
+import static org.eclipse.jgit.lib.FileMode.TYPE_TREE;
+
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
@@ -53,6 +57,7 @@ import java.util.List;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.util.Paths;
/**
* Updates a {@link DirCache} by supplying discrete edit commands.
@@ -72,11 +77,12 @@ public class DirCacheEditor extends BaseDirCacheEditor {
public int compare(final PathEdit o1, final PathEdit o2) {
final byte[] a = o1.path;
final byte[] b = o2.path;
- return DirCache.cmp(a, a.length, b, b.length);
+ return cmp(a, a.length, b, b.length);
}
};
private final List<PathEdit> edits;
+ private int editIdx;
/**
* Construct a new editor.
@@ -126,35 +132,44 @@ public class DirCacheEditor extends BaseDirCacheEditor {
private void applyEdits() {
Collections.sort(edits, EDIT_CMP);
+ editIdx = 0;
final int maxIdx = cache.getEntryCount();
int lastIdx = 0;
- for (final PathEdit e : edits) {
- int eIdx = cache.findEntry(e.path, e.path.length);
+ while (editIdx < edits.size()) {
+ PathEdit e = edits.get(editIdx++);
+ int eIdx = cache.findEntry(lastIdx, e.path, e.path.length);
final boolean missing = eIdx < 0;
if (eIdx < 0)
eIdx = -(eIdx + 1);
final int cnt = Math.min(eIdx, maxIdx) - lastIdx;
if (cnt > 0)
fastKeep(lastIdx, cnt);
- lastIdx = missing ? eIdx : cache.nextEntry(eIdx);
- if (e instanceof DeletePath)
+ if (e instanceof DeletePath) {
+ lastIdx = missing ? eIdx : cache.nextEntry(eIdx);
continue;
+ }
if (e instanceof DeleteTree) {
lastIdx = cache.nextEntry(e.path, e.path.length, eIdx);
continue;
}
if (missing) {
- final DirCacheEntry ent = new DirCacheEntry(e.path);
+ DirCacheEntry ent = new DirCacheEntry(e.path);
e.apply(ent);
- if (ent.getRawMode() == 0)
- throw new IllegalArgumentException(MessageFormat.format(JGitText.get().fileModeNotSetForPath
- , ent.getPathString()));
+ if (ent.getRawMode() == 0) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().fileModeNotSetForPath,
+ ent.getPathString()));
+ }
+ lastIdx = e.replace
+ ? deleteOverlappingSubtree(ent, eIdx)
+ : eIdx;
fastAdd(ent);
} else {
// Apply to all entries of the current path (different stages)
+ lastIdx = cache.nextEntry(eIdx);
for (int i = eIdx; i < lastIdx; i++) {
final DirCacheEntry ent = cache.getEntry(i);
e.apply(ent);
@@ -168,6 +183,102 @@ public class DirCacheEditor extends BaseDirCacheEditor {
fastKeep(lastIdx, cnt);
}
+ private int deleteOverlappingSubtree(DirCacheEntry ent, int eIdx) {
+ byte[] entPath = ent.path;
+ int entLen = entPath.length;
+
+ // Delete any file that was previously processed and overlaps
+ // the parent directory for the new entry. Since the editor
+ // always processes entries in path order, binary search back
+ // for the overlap for each parent directory.
+ for (int p = pdir(entPath, entLen); p > 0; p = pdir(entPath, p)) {
+ int i = findEntry(entPath, p);
+ if (i >= 0) {
+ // A file does overlap, delete the file from the array.
+ // No other parents can have overlaps as the file should
+ // have taken care of that itself.
+ int n = --entryCnt - i;
+ System.arraycopy(entries, i + 1, entries, i, n);
+ break;
+ }
+
+ // If at least one other entry already exists in this parent
+ // directory there is no need to continue searching up the tree.
+ i = -(i + 1);
+ if (i < entryCnt && inDir(entries[i], entPath, p)) {
+ break;
+ }
+ }
+
+ int maxEnt = cache.getEntryCount();
+ if (eIdx >= maxEnt) {
+ return maxEnt;
+ }
+
+ DirCacheEntry next = cache.getEntry(eIdx);
+ if (Paths.compare(next.path, 0, next.path.length, 0,
+ entPath, 0, entLen, TYPE_TREE) < 0) {
+ // Next DirCacheEntry sorts before new entry as tree. Defer a
+ // DeleteTree command to delete any entries if they exist. This
+ // case only happens for A, A.c, A/c type of conflicts (rare).
+ insertEdit(new DeleteTree(entPath));
+ return eIdx;
+ }
+
+ // Next entry may be contained by the entry-as-tree, skip if so.
+ while (eIdx < maxEnt && inDir(cache.getEntry(eIdx), entPath, entLen)) {
+ eIdx++;
+ }
+ return eIdx;
+ }
+
+ private int findEntry(byte[] p, int pLen) {
+ int low = 0;
+ int high = entryCnt;
+ while (low < high) {
+ int mid = (low + high) >>> 1;
+ int cmp = cmp(p, pLen, entries[mid]);
+ if (cmp < 0) {
+ high = mid;
+ } else if (cmp == 0) {
+ while (mid > 0 && cmp(p, pLen, entries[mid - 1]) == 0) {
+ mid--;
+ }
+ return mid;
+ } else {
+ low = mid + 1;
+ }
+ }
+ return -(low + 1);
+ }
+
+ private void insertEdit(DeleteTree d) {
+ for (int i = editIdx; i < edits.size(); i++) {
+ int cmp = EDIT_CMP.compare(d, edits.get(i));
+ if (cmp < 0) {
+ edits.add(i, d);
+ return;
+ } else if (cmp == 0) {
+ return;
+ }
+ }
+ edits.add(d);
+ }
+
+ private static boolean inDir(DirCacheEntry e, byte[] path, int pLen) {
+ return e.path.length > pLen && e.path[pLen] == '/'
+ && peq(path, e.path, pLen);
+ }
+
+ private static int pdir(byte[] path, int e) {
+ for (e--; e > 0; e--) {
+ if (path[e] == '/') {
+ return e;
+ }
+ }
+ return 0;
+ }
+
/**
* Any index record update.
* <p>
@@ -179,6 +290,7 @@ public class DirCacheEditor extends BaseDirCacheEditor {
*/
public abstract static class PathEdit {
final byte[] path;
+ boolean replace = true;
/**
* Create a new update command by path name.
@@ -190,6 +302,10 @@ public class DirCacheEditor extends BaseDirCacheEditor {
path = Constants.encode(entryPath);
}
+ PathEdit(byte[] path) {
+ this.path = path;
+ }
+
/**
* Create a new update command for an existing entry instance.
*
@@ -202,6 +318,22 @@ public class DirCacheEditor extends BaseDirCacheEditor {
}
/**
+ * Configure if a file can replace a directory (or vice versa).
+ * <p>
+ * Default is {@code true} as this is usually the desired behavior.
+ *
+ * @param ok
+ * if true a file can replace a directory, or a directory can
+ * replace a file.
+ * @return {@code this}
+ * @since 4.2
+ */
+ public PathEdit setReplace(boolean ok) {
+ replace = ok;
+ return this;
+ }
+
+ /**
* Apply the update to a single cache entry matching the path.
* <p>
* After apply is invoked the entry is added to the output table, and
@@ -212,6 +344,12 @@ public class DirCacheEditor extends BaseDirCacheEditor {
* the path is a new path in the index.
*/
public abstract void apply(DirCacheEntry ent);
+
+ @Override
+ public String toString() {
+ String p = DirCacheEntry.toString(path);
+ return getClass().getSimpleName() + '[' + p + ']';
+ }
}
/**
@@ -272,10 +410,26 @@ public class DirCacheEditor extends BaseDirCacheEditor {
* only the subtree's contents are matched by the command.
* The special case "" (not "/"!) deletes all entries.
*/
- public DeleteTree(final String entryPath) {
- super(
- (entryPath.endsWith("/") || entryPath.length() == 0) ? entryPath //$NON-NLS-1$
- : entryPath + "/"); //$NON-NLS-1$
+ public DeleteTree(String entryPath) {
+ super(entryPath.isEmpty()
+ || entryPath.charAt(entryPath.length() - 1) == '/'
+ ? entryPath
+ : entryPath + '/');
+ }
+
+ DeleteTree(byte[] path) {
+ super(appendSlash(path));
+ }
+
+ private static byte[] appendSlash(byte[] path) {
+ int n = path.length;
+ if (n > 0 && path[n - 1] != '/') {
+ byte[] r = new byte[n + 1];
+ System.arraycopy(path, 0, r, 0, n);
+ r[n] = '/';
+ return r;
+ }
+ return path;
}
public void apply(final DirCacheEntry ent) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
index eef2e6d..4ebf2e0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
@@ -65,6 +65,7 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.MutableInteger;
import org.eclipse.jgit.util.NB;
+import org.eclipse.jgit.util.SystemReader;
/**
* A single file (or stage of a file) in a {@link DirCache}.
@@ -191,7 +192,7 @@ public class DirCacheEntry {
}
try {
- DirCacheCheckout.checkValidPath(toString(path));
+ checkPath(path);
} catch (InvalidPathException e) {
CorruptObjectException p =
new CorruptObjectException(e.getMessage());
@@ -263,7 +264,7 @@ public class DirCacheEntry {
/**
* Create an empty entry at the specified stage.
*
- * @param newPath
+ * @param path
* name of the cache entry, in the standard encoding.
* @param stage
* the stage index of the new entry.
@@ -274,16 +275,16 @@ public class DirCacheEntry {
* range 0..3, inclusive.
*/
@SuppressWarnings("boxing")
- public DirCacheEntry(final byte[] newPath, final int stage) {
- DirCacheCheckout.checkValidPath(toString(newPath));
+ public DirCacheEntry(byte[] path, final int stage) {
+ checkPath(path);
if (stage < 0 || 3 < stage)
throw new IllegalArgumentException(MessageFormat.format(
JGitText.get().invalidStageForPath,
- stage, toString(newPath)));
+ stage, toString(path)));
info = new byte[INFO_LEN];
infoOffset = 0;
- path = newPath;
+ this.path = path;
int flags = ((stage & 0x3) << 12);
if (path.length < NAME_MASK)
@@ -293,6 +294,23 @@ public class DirCacheEntry {
NB.encodeInt16(info, infoOffset + P_FLAGS, flags);
}
+ /**
+ * Duplicate DirCacheEntry with same path and copied info.
+ * <p>
+ * The same path buffer is reused (avoiding copying), however a new info
+ * buffer is created and its contents are copied.
+ *
+ * @param src
+ * entry to clone.
+ * @since 4.2
+ */
+ public DirCacheEntry(DirCacheEntry src) {
+ path = src.path;
+ info = new byte[INFO_LEN];
+ infoOffset = 0;
+ System.arraycopy(src.info, src.infoOffset, info, 0, INFO_LEN);
+ }
+
void write(final OutputStream os) throws IOException {
final int len = isExtended() ? INFO_LEN_EXTENDED : INFO_LEN;
final int pathLen = path.length;
@@ -498,12 +516,16 @@ public class DirCacheEntry {
switch (mode.getBits() & FileMode.TYPE_MASK) {
case FileMode.TYPE_MISSING:
case FileMode.TYPE_TREE:
- throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidModeForPath
- , mode, getPathString()));
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().invalidModeForPath, mode, getPathString()));
}
NB.encodeInt32(info, infoOffset + P_MODE, mode.getBits());
}
+ void setFileMode(int mode) {
+ NB.encodeInt32(info, infoOffset + P_MODE, mode);
+ }
+
/**
* Get the cached creation time of this file, in milliseconds.
*
@@ -730,7 +752,17 @@ public class DirCacheEntry {
return 0;
}
- private static String toString(final byte[] path) {
+ private static void checkPath(byte[] path) {
+ try {
+ SystemReader.getInstance().checkPath(path);
+ } catch (CorruptObjectException e) {
+ InvalidPathException p = new InvalidPathException(toString(path));
+ p.initCause(e);
+ throw p;
+ }
+ }
+
+ static String toString(final byte[] path) {
return Constants.CHARSET.decode(ByteBuffer.wrap(path)).toString();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java
index 354a074..ad93f72 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java
@@ -103,9 +103,6 @@ public class DirCacheIterator extends AbstractTreeIterator {
/** The subtree containing {@link #currentEntry} if this is first entry. */
protected DirCacheTree currentSubtree;
- /** Holds an {@link AttributesNode} for the current entry */
- private AttributesNode attributesNode;
-
/**
* Create a new iterator for an already loaded DirCache instance.
* <p>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java
index 30932e8..0cbb83d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java
@@ -103,7 +103,7 @@ public class DirCacheTree {
private DirCacheTree parent;
/** Name of this tree within its parent. */
- private byte[] encodedName;
+ byte[] encodedName;
/** Number of {@link DirCacheEntry} records that belong to this tree. */
private int entrySpan;
@@ -399,7 +399,7 @@ public class DirCacheTree {
for (int eOff = 0; eOff < eLen && aOff < aLen; eOff++, aOff++)
if (e[eOff] != a[aOff])
return false;
- if (aOff == aLen)
+ if (aOff >= aLen)
return false;
return a[aOff] == '/';
}
@@ -478,6 +478,7 @@ public class DirCacheTree {
// The entry is contained in this subtree.
//
+ assert(st != null);
st.validate(cache, cCnt, cIdx, pathOff + st.nameLength() + 1);
cIdx += st.entrySpan;
entrySpan += st.entrySpan;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/CorruptObjectException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/CorruptObjectException.java
index c6ea093..e4db40b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/CorruptObjectException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/CorruptObjectException.java
@@ -49,8 +49,10 @@ package org.eclipse.jgit.errors;
import java.io.IOException;
import java.text.MessageFormat;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectChecker;
import org.eclipse.jgit.lib.ObjectId;
/**
@@ -59,6 +61,26 @@ import org.eclipse.jgit.lib.ObjectId;
public class CorruptObjectException extends IOException {
private static final long serialVersionUID = 1L;
+ private ObjectChecker.ErrorType errorType;
+
+ /**
+ * Report a specific error condition discovered in an object.
+ *
+ * @param type
+ * type of error
+ * @param id
+ * identity of the bad object
+ * @param why
+ * description of the error.
+ * @since 4.2
+ */
+ public CorruptObjectException(ObjectChecker.ErrorType type, AnyObjectId id,
+ String why) {
+ super(MessageFormat.format(JGitText.get().objectIsCorrupt3,
+ type.getMessageId(), id.name(), why));
+ this.errorType = type;
+ }
+
/**
* Construct a CorruptObjectException for reporting a problem specified
* object id
@@ -66,8 +88,8 @@ public class CorruptObjectException extends IOException {
* @param id
* @param why
*/
- public CorruptObjectException(final AnyObjectId id, final String why) {
- this(id.toObjectId(), why);
+ public CorruptObjectException(AnyObjectId id, String why) {
+ super(MessageFormat.format(JGitText.get().objectIsCorrupt, id.name(), why));
}
/**
@@ -77,7 +99,7 @@ public class CorruptObjectException extends IOException {
* @param id
* @param why
*/
- public CorruptObjectException(final ObjectId id, final String why) {
+ public CorruptObjectException(ObjectId id, String why) {
super(MessageFormat.format(JGitText.get().objectIsCorrupt, id.name(), why));
}
@@ -87,7 +109,7 @@ public class CorruptObjectException extends IOException {
*
* @param why
*/
- public CorruptObjectException(final String why) {
+ public CorruptObjectException(String why) {
super(why);
}
@@ -105,4 +127,15 @@ public class CorruptObjectException extends IOException {
super(why);
initCause(cause);
}
+
+ /**
+ * Specific error condition identified by {@link ObjectChecker}.
+ *
+ * @return error condition or null.
+ * @since 4.2
+ */
+ @Nullable
+ public ObjectChecker.ErrorType getErrorType() {
+ return errorType;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackMayNotContinueException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/DiffInterruptedException.java
similarity index 76%
copy from org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackMayNotContinueException.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/errors/DiffInterruptedException.java
index 5392a01..5f9ce35 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackMayNotContinueException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/DiffInterruptedException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, Google Inc.
+ * Copyright (C) 2015 Ericsson
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -41,28 +41,36 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.transport;
+package org.eclipse.jgit.errors;
/**
- * Indicates UploadPack may not continue execution.
+ * Thrown when a thread executing a diff is interrupted
*
- * @deprecated use {@link ServiceMayNotContinueException} instead.
+ * @see org.eclipse.jgit.diff.MyersDiff
+ * @since 4.0
*/
- at Deprecated
-public class UploadPackMayNotContinueException extends ServiceMayNotContinueException {
+public class DiffInterruptedException extends RuntimeException {
private static final long serialVersionUID = 1L;
- /** Initialize with no message. */
- public UploadPackMayNotContinueException() {
- // Do not set a message.
+ /**
+ * @param message
+ * @param cause
+ * @since 4.1
+ */
+ public DiffInterruptedException(String message, Throwable cause) {
+ super(message, cause);
}
/**
- * @param msg
- * a message explaining why it cannot continue. This message may
- * be shown to an end-user.
+ * @param message
+ * @since 4.1
*/
- public UploadPackMayNotContinueException(String msg) {
- super(msg);
+ public DiffInterruptedException(String message) {
+ super(message);
+ }
+
+ /** Indicates that the thread computing a diff was interrupted. */
+ public DiffInterruptedException() {
+ super();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/TooLargePackException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/DirCacheNameConflictException.java
similarity index 72%
copy from org.eclipse.jgit/src/org/eclipse/jgit/errors/TooLargePackException.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/errors/DirCacheNameConflictException.java
index 5cf0f80..5f67e34 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/TooLargePackException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/DirCacheNameConflictException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014, Sasa Zivkov <sasa.zivkov at sap.com>, SAP AG
+ * Copyright (C) 2015, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -43,27 +43,38 @@
package org.eclipse.jgit.errors;
-import java.io.IOException;
-import java.text.MessageFormat;
-
-import org.eclipse.jgit.internal.JGitText;
-
/**
- * Thrown when a pack exceeds a given size limit
+ * Thrown by DirCache code when entries overlap in impossible way.
*
- * @since 3.3
+ * @since 4.2
*/
-public class TooLargePackException extends IOException {
+public class DirCacheNameConflictException extends IllegalStateException {
private static final long serialVersionUID = 1L;
+ private final String path1;
+ private final String path2;
+
/**
- * Construct a too large pack exception.
+ * Construct an exception for a specific path.
*
- * @param packSizeLimit
- * the pack size limit (in bytes) that was exceeded
+ * @param path1
+ * one path that conflicts.
+ * @param path2
+ * another path that conflicts.
*/
- public TooLargePackException(long packSizeLimit) {
- super(MessageFormat.format(JGitText.get().receivePackTooLarge,
- Long.valueOf(packSizeLimit)));
+ public DirCacheNameConflictException(String path1, String path2) {
+ super(path1 + ' ' + path2);
+ this.path1 = path1;
+ this.path2 = path2;
+ }
+
+ /** @return one of the paths that has a conflict. */
+ public String getPath1() {
+ return path1;
+ }
+
+ /** @return another path that has a conflict. */
+ public String getPath2() {
+ return path2;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/UnmergedPathsException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/IndexReadException.java
similarity index 70%
copy from org.eclipse.jgit/src/org/eclipse/jgit/api/errors/UnmergedPathsException.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/errors/IndexReadException.java
index 0990040..70f650d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/UnmergedPathsException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/IndexReadException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010,Mathias Kinzler <mathias.kinzler at sap.com> and
+ * Copyright (C) 2015, Christian Halstrick <christian.halstrick at sap.com> and
* other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available under the
@@ -35,30 +35,48 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.api.errors;
+package org.eclipse.jgit.errors;
+
+import java.io.IOException;
import org.eclipse.jgit.internal.JGitText;
/**
- * Thrown when branch deletion fails due to unmerged data
+ * Cannot read the index. This is a serious error that users need to be made
+ * aware of.
+ *
+ * @since 4.2
*/
-public class UnmergedPathsException extends GitAPIException {
+public class IndexReadException extends IOException {
private static final long serialVersionUID = 1L;
/**
- * The default constructor with a default message
+ * Constructs an IndexReadException with the default message.
+ */
+ public IndexReadException() {
+ super(JGitText.get().indexWriteException);
+ }
+
+ /**
+ * Constructs an IndexReadException with the specified detail message.
+ *
+ * @param s
+ * message
*/
- public UnmergedPathsException() {
- this(null);
+ public IndexReadException(final String s) {
+ super(s);
}
/**
- * The default constructor with a default message
+ * Constructs an IndexReadException with the specified detail message.
*
+ * @param s
+ * message
* @param cause
- * original exception
+ * root cause exception
*/
- public UnmergedPathsException(Throwable cause) {
- super(JGitText.get().unmergedPaths, cause);
+ public IndexReadException(final String s, final Throwable cause) {
+ super(s);
+ initCause(cause);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/InvalidObjectIdException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/InvalidObjectIdException.java
index b545312..390545f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/InvalidObjectIdException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/InvalidObjectIdException.java
@@ -45,7 +45,8 @@
package org.eclipse.jgit.errors;
-import java.io.UnsupportedEncodingException;
+import static java.nio.charset.StandardCharsets.US_ASCII;
+
import java.text.MessageFormat;
import org.eclipse.jgit.internal.JGitText;
@@ -64,16 +65,25 @@ public class InvalidObjectIdException extends IllegalArgumentException {
* @param length of the sequence of invalid bytes.
*/
public InvalidObjectIdException(byte[] bytes, int offset, int length) {
- super(MessageFormat.format(JGitText.get().invalidId, asAscii(bytes, offset, length)));
+ super(msg(bytes, offset, length));
+ }
+
+ /**
+ * @param id the invalid id.
+ *
+ * @since 4.1
+ */
+ public InvalidObjectIdException(String id) {
+ super(MessageFormat.format(JGitText.get().invalidId, id));
}
- private static String asAscii(byte[] bytes, int offset, int length) {
+ private static String msg(byte[] bytes, int offset, int length) {
try {
- return ": " + new String(bytes, offset, length, "US-ASCII"); //$NON-NLS-1$ //$NON-NLS-2$
- } catch (UnsupportedEncodingException e2) {
- return ""; //$NON-NLS-1$
- } catch (StringIndexOutOfBoundsException e2) {
- return ""; //$NON-NLS-1$
+ return MessageFormat.format(
+ JGitText.get().invalidId,
+ new String(bytes, offset, length, US_ASCII));
+ } catch (StringIndexOutOfBoundsException e) {
+ return JGitText.get().invalidId0;
}
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/LockFailedException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/LockFailedException.java
index 18aa9d9..0142e17 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/LockFailedException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/LockFailedException.java
@@ -57,6 +57,20 @@ public class LockFailedException extends IOException {
private File file;
/**
+ * @param file
+ * file that could not be locked
+ * @param message
+ * exception message
+ * @param cause
+ * cause, for later retrieval by {@link Throwable#getCause()}
+ * @since 4.1
+ */
+ public LockFailedException(File file, String message, Throwable cause) {
+ super(message, cause);
+ this.file = file;
+ }
+
+ /**
* Construct a CannotLockException for the given file and message
*
* @param file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackProtocolException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackProtocolException.java
index 5503bd1..44bc164 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackProtocolException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/PackProtocolException.java
@@ -60,7 +60,7 @@ public class PackProtocolException extends TransportException {
* @param uri
* URI used for transport
* @param s
- * message
+ * message, which may be shown to an end-user.
*/
public PackProtocolException(final URIish uri, final String s) {
super(uri + ": " + s); //$NON-NLS-1$
@@ -73,7 +73,7 @@ public class PackProtocolException extends TransportException {
* @param uri
* URI used for transport
* @param s
- * message
+ * message, which may be shown to an end-user.
* @param cause
* root cause exception
*/
@@ -86,7 +86,7 @@ public class PackProtocolException extends TransportException {
* Constructs an PackProtocolException with the specified detail message.
*
* @param s
- * message
+ * message, which may be shown to an end-user.
*/
public PackProtocolException(final String s) {
super(s);
@@ -96,7 +96,7 @@ public class PackProtocolException extends TransportException {
* Constructs an PackProtocolException with the specified detail message.
*
* @param s
- * message
+ * message, which may be shown to an end-user.
* @param cause
* root cause exception
*/
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/TooLargePackException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/TooLargePackException.java
index 5cf0f80..d547985 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/TooLargePackException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/TooLargePackException.java
@@ -43,17 +43,17 @@
package org.eclipse.jgit.errors;
-import java.io.IOException;
import java.text.MessageFormat;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.transport.URIish;
/**
* Thrown when a pack exceeds a given size limit
*
* @since 3.3
*/
-public class TooLargePackException extends IOException {
+public class TooLargePackException extends TransportException {
private static final long serialVersionUID = 1L;
/**
@@ -66,4 +66,17 @@ public class TooLargePackException extends IOException {
super(MessageFormat.format(JGitText.get().receivePackTooLarge,
Long.valueOf(packSizeLimit)));
}
+
+ /**
+ * Construct a too large pack exception.
+ *
+ * @param uri
+ * URI used for transport
+ * @param s
+ * message
+ * @since 4.0
+ */
+ public TooLargePackException(URIish uri, String s) {
+ super(uri.setPass(null) + ": " + s); //$NON-NLS-1$
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/GroupHead.java b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/GroupHead.java
index 0243bdc..8af5228 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/GroupHead.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/fnmatch/GroupHead.java
@@ -114,9 +114,9 @@ final class GroupHead extends AbstractHead {
characterClasses.add(LetterPattern.INSTANCE);
characterClasses.add(DigitPattern.INSTANCE);
} else {
- final String message = String.format(MessageFormat.format(
+ final String message = MessageFormat.format(
JGitText.get().characterClassIsNotSupported,
- characterClass));
+ characterClass);
throw new InvalidPatternException(message, wholePattern);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
new file mode 100644
index 0000000..7eb9550
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.gitrepo;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.gitrepo.RepoProject.CopyFile;
+import org.eclipse.jgit.gitrepo.internal.RepoText;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.Repository;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+/**
+ * Repo XML manifest parser.
+ *
+ * @see <a href="https://code.google.com/p/git-repo/">git-repo project page</a>
+ * @since 4.0
+ */
+public class ManifestParser extends DefaultHandler {
+ private final String filename;
+ private final String baseUrl;
+ private final String defaultBranch;
+ private final Repository rootRepo;
+ private final Map<String, String> remotes;
+ private final Set<String> plusGroups;
+ private final Set<String> minusGroups;
+ private final List<RepoProject> projects;
+ private final List<RepoProject> filteredProjects;
+ private final IncludedFileReader includedReader;
+
+ private String defaultRemote;
+ private String defaultRevision;
+ private int xmlInRead;
+ private RepoProject currentProject;
+
+ /**
+ * A callback to read included xml files.
+ */
+ public interface IncludedFileReader {
+ /**
+ * Read a file from the same base dir of the manifest xml file.
+ *
+ * @param path
+ * The relative path to the file to read
+ * @return the {@code InputStream} of the file.
+ * @throws GitAPIException
+ * @throws IOException
+ */
+ public InputStream readIncludeFile(String path)
+ throws GitAPIException, IOException;
+ }
+
+ /**
+ * @param includedReader
+ * @param filename
+ * @param defaultBranch
+ * @param baseUrl
+ * @param groups
+ * @param rootRepo
+ */
+ public ManifestParser(IncludedFileReader includedReader, String filename,
+ String defaultBranch, String baseUrl, String groups,
+ Repository rootRepo) {
+ this.includedReader = includedReader;
+ this.filename = filename;
+ this.defaultBranch = defaultBranch;
+ this.rootRepo = rootRepo;
+
+ // Strip trailing /s to match repo behavior.
+ int lastIndex = baseUrl.length() - 1;
+ while (lastIndex >= 0 && baseUrl.charAt(lastIndex) == '/')
+ lastIndex--;
+ this.baseUrl = baseUrl.substring(0, lastIndex + 1);
+
+ plusGroups = new HashSet<String>();
+ minusGroups = new HashSet<String>();
+ if (groups == null || groups.length() == 0
+ || groups.equals("default")) { //$NON-NLS-1$
+ // default means "all,-notdefault"
+ minusGroups.add("notdefault"); //$NON-NLS-1$
+ } else {
+ for (String group : groups.split(",")) { //$NON-NLS-1$
+ if (group.startsWith("-")) //$NON-NLS-1$
+ minusGroups.add(group.substring(1));
+ else
+ plusGroups.add(group);
+ }
+ }
+
+ remotes = new HashMap<String, String>();
+ projects = new ArrayList<RepoProject>();
+ filteredProjects = new ArrayList<RepoProject>();
+ }
+
+ /**
+ * Read the xml file.
+ *
+ * @param inputStream
+ * @throws IOException
+ */
+ public void read(InputStream inputStream) throws IOException {
+ xmlInRead++;
+ final XMLReader xr;
+ try {
+ xr = XMLReaderFactory.createXMLReader();
+ } catch (SAXException e) {
+ throw new IOException(JGitText.get().noXMLParserAvailable);
+ }
+ xr.setContentHandler(this);
+ try {
+ xr.parse(new InputSource(inputStream));
+ } catch (SAXException e) {
+ IOException error = new IOException(
+ RepoText.get().errorParsingManifestFile);
+ error.initCause(e);
+ throw error;
+ }
+ }
+
+ @Override
+ public void startElement(
+ String uri,
+ String localName,
+ String qName,
+ Attributes attributes) throws SAXException {
+ if ("project".equals(qName)) { //$NON-NLS-1$
+ if (attributes.getValue("name") == null) { //$NON-NLS-1$
+ throw new SAXException(RepoText.get().invalidManifest);
+ }
+ currentProject = new RepoProject(
+ attributes.getValue("name"), //$NON-NLS-1$
+ attributes.getValue("path"), //$NON-NLS-1$
+ attributes.getValue("revision"), //$NON-NLS-1$
+ attributes.getValue("remote"), //$NON-NLS-1$
+ attributes.getValue("groups")); //$NON-NLS-1$
+ } else if ("remote".equals(qName)) { //$NON-NLS-1$
+ String alias = attributes.getValue("alias"); //$NON-NLS-1$
+ String fetch = attributes.getValue("fetch"); //$NON-NLS-1$
+ remotes.put(attributes.getValue("name"), fetch); //$NON-NLS-1$
+ if (alias != null)
+ remotes.put(alias, fetch);
+ } else if ("default".equals(qName)) { //$NON-NLS-1$
+ defaultRemote = attributes.getValue("remote"); //$NON-NLS-1$
+ defaultRevision = attributes.getValue("revision"); //$NON-NLS-1$
+ if (defaultRevision == null)
+ defaultRevision = defaultBranch;
+ } else if ("copyfile".equals(qName)) { //$NON-NLS-1$
+ if (currentProject == null)
+ throw new SAXException(RepoText.get().invalidManifest);
+ currentProject.addCopyFile(new CopyFile(
+ rootRepo,
+ currentProject.getPath(),
+ attributes.getValue("src"), //$NON-NLS-1$
+ attributes.getValue("dest"))); //$NON-NLS-1$
+ } else if ("include".equals(qName)) { //$NON-NLS-1$
+ String name = attributes.getValue("name"); //$NON-NLS-1$
+ InputStream is = null;
+ if (includedReader != null) {
+ try {
+ is = includedReader.readIncludeFile(name);
+ } catch (Exception e) {
+ throw new SAXException(MessageFormat.format(
+ RepoText.get().errorIncludeFile, name), e);
+ }
+ } else if (filename != null) {
+ int index = filename.lastIndexOf('/');
+ String path = filename.substring(0, index + 1) + name;
+ try {
+ is = new FileInputStream(path);
+ } catch (IOException e) {
+ throw new SAXException(MessageFormat.format(
+ RepoText.get().errorIncludeFile, path), e);
+ }
+ }
+ if (is == null) {
+ throw new SAXException(
+ RepoText.get().errorIncludeNotImplemented);
+ }
+ try {
+ read(is);
+ } catch (IOException e) {
+ throw new SAXException(e);
+ }
+ }
+ }
+
+ @Override
+ public void endElement(
+ String uri,
+ String localName,
+ String qName) throws SAXException {
+ if ("project".equals(qName)) { //$NON-NLS-1$
+ projects.add(currentProject);
+ currentProject = null;
+ }
+ }
+
+ @Override
+ public void endDocument() throws SAXException {
+ xmlInRead--;
+ if (xmlInRead != 0)
+ return;
+
+ // Only do the following after we finished reading everything.
+ Map<String, String> remoteUrls = new HashMap<String, String>();
+ URI baseUri;
+ try {
+ baseUri = new URI(baseUrl);
+ } catch (URISyntaxException e) {
+ throw new SAXException(e);
+ }
+ for (RepoProject proj : projects) {
+ String remote = proj.getRemote();
+ if (remote == null) {
+ if (defaultRemote == null) {
+ if (filename != null)
+ throw new SAXException(MessageFormat.format(
+ RepoText.get().errorNoDefaultFilename,
+ filename));
+ else
+ throw new SAXException(
+ RepoText.get().errorNoDefault);
+ }
+ remote = defaultRemote;
+ }
+ String remoteUrl = remoteUrls.get(remote);
+ if (remoteUrl == null) {
+ remoteUrl = baseUri.resolve(remotes.get(remote)).toString();
+ if (!remoteUrl.endsWith("/")) //$NON-NLS-1$
+ remoteUrl = remoteUrl + "/"; //$NON-NLS-1$
+ remoteUrls.put(remote, remoteUrl);
+ }
+ proj.setUrl(remoteUrl + proj.getName())
+ .setDefaultRevision(defaultRevision);
+ }
+
+ filteredProjects.addAll(projects);
+ removeNotInGroup();
+ removeOverlaps();
+ }
+
+ /**
+ * Getter for projects.
+ *
+ * @return projects list reference, never null
+ */
+ public List<RepoProject> getProjects() {
+ return projects;
+ }
+
+ /**
+ * Getter for filterdProjects.
+ *
+ * @return filtered projects list reference, never null
+ */
+ public List<RepoProject> getFilteredProjects() {
+ return filteredProjects;
+ }
+
+ /** Remove projects that are not in our desired groups. */
+ void removeNotInGroup() {
+ Iterator<RepoProject> iter = filteredProjects.iterator();
+ while (iter.hasNext())
+ if (!inGroups(iter.next()))
+ iter.remove();
+ }
+
+ /** Remove projects that sits in a subdirectory of any other project. */
+ void removeOverlaps() {
+ Collections.sort(filteredProjects);
+ Iterator<RepoProject> iter = filteredProjects.iterator();
+ if (!iter.hasNext())
+ return;
+ RepoProject last = iter.next();
+ while (iter.hasNext()) {
+ RepoProject p = iter.next();
+ if (last.isAncestorOf(p))
+ iter.remove();
+ else
+ last = p;
+ }
+ removeNestedCopyfiles();
+ }
+
+ /** Remove copyfiles that sit in a subdirectory of any other project. */
+ void removeNestedCopyfiles() {
+ for (RepoProject proj : filteredProjects) {
+ List<CopyFile> copyfiles = new ArrayList<>(proj.getCopyFiles());
+ proj.clearCopyFiles();
+ for (CopyFile copyfile : copyfiles) {
+ if (!isNestedCopyfile(copyfile)) {
+ proj.addCopyFile(copyfile);
+ }
+ }
+ }
+ }
+
+ boolean inGroups(RepoProject proj) {
+ for (String group : minusGroups) {
+ if (proj.inGroup(group)) {
+ // minus groups have highest priority.
+ return false;
+ }
+ }
+ if (plusGroups.isEmpty() || plusGroups.contains("all")) { //$NON-NLS-1$
+ // empty plus groups means "all"
+ return true;
+ }
+ for (String group : plusGroups) {
+ if (proj.inGroup(group))
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isNestedCopyfile(CopyFile copyfile) {
+ if (copyfile.dest.indexOf('/') == -1) {
+ // If the copyfile is at root level then it won't be nested.
+ return false;
+ }
+ for (RepoProject proj : filteredProjects) {
+ if (proj.getPath().compareTo(copyfile.dest) > 0) {
+ // Early return as remaining projects can't be ancestor of this
+ // copyfile config (filteredProjects is sorted).
+ return false;
+ }
+ if (proj.isAncestorOf(copyfile.dest)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
index a4b444e..ff9f233 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
@@ -42,24 +42,17 @@
*/
package org.eclipse.jgit.gitrepo;
+import static org.eclipse.jgit.lib.Constants.DEFAULT_REMOTE_NAME;
+import static org.eclipse.jgit.lib.Constants.R_REMOTES;
+
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.nio.channels.FileChannel;
import java.text.MessageFormat;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.GitCommand;
@@ -70,6 +63,8 @@ import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.gitrepo.ManifestParser.IncludedFileReader;
+import org.eclipse.jgit.gitrepo.RepoProject.CopyFile;
import org.eclipse.jgit.gitrepo.internal.RepoText;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.CommitBuilder;
@@ -89,12 +84,6 @@ import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.util.FileUtils;
-import org.xml.sax.Attributes;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.DefaultHandler;
-import org.xml.sax.helpers.XMLReaderFactory;
/**
* A class used to execute a repo command.
@@ -119,12 +108,14 @@ public class RepoCommand extends GitCommand<RevCommit> {
private String uri;
private String groups;
private String branch;
+ private String targetBranch = Constants.HEAD;
+ private boolean recordRemoteBranch = false;
private PersonIdent author;
private RemoteReader callback;
private InputStream inputStream;
private IncludedFileReader includedReader;
- private List<Project> bareProjects;
+ private List<RepoProject> bareProjects;
private Git git;
private ProgressMonitor monitor;
@@ -214,353 +205,13 @@ public class RepoCommand extends GitCommand<RevCommit> {
*/
protected byte[] readFileFromRepo(Repository repo,
String ref, String path) throws GitAPIException, IOException {
- ObjectReader reader = repo.newObjectReader();
- byte[] result;
- try {
+ try (ObjectReader reader = repo.newObjectReader()) {
ObjectId oid = repo.resolve(ref + ":" + path); //$NON-NLS-1$
- result = reader.open(oid).getBytes(Integer.MAX_VALUE);
- } finally {
- reader.release();
- }
- return result;
- }
- }
-
- /**
- * A callback to read included xml files.
- *
- * @since 3.5
- */
- public interface IncludedFileReader {
- /**
- * Read a file from the same base dir of the manifest xml file.
- *
- * @param path
- * The relative path to the file to read
- * @return the {@code InputStream} of the file.
- * @throws GitAPIException
- * @throws IOException
- */
- public InputStream readIncludeFile(String path)
- throws GitAPIException, IOException;
- }
-
- private static class CopyFile {
- final Repository repo;
- final String path;
- final String src;
- final String dest;
-
- CopyFile(Repository repo, String path, String src, String dest) {
- this.repo = repo;
- this.path = path;
- this.src = src;
- this.dest = dest;
- }
-
- void copy() throws IOException {
- File srcFile = new File(repo.getWorkTree(),
- path + "/" + src); //$NON-NLS-1$
- File destFile = new File(repo.getWorkTree(), dest);
- FileInputStream input = new FileInputStream(srcFile);
- try {
- FileOutputStream output = new FileOutputStream(destFile);
- try {
- FileChannel channel = input.getChannel();
- output.getChannel().transferFrom(channel, 0, channel.size());
- } finally {
- output.close();
- }
- } finally {
- input.close();
+ return reader.open(oid).getBytes(Integer.MAX_VALUE);
}
}
}
- private static class Project implements Comparable<Project> {
- final String name;
- final String path;
- final String revision;
- final String remote;
- final Set<String> groups;
- final List<CopyFile> copyfiles;
-
- Project(String name, String path, String revision,
- String remote, String groups) {
- this.name = name;
- if (path != null)
- this.path = path;
- else
- this.path = name;
- this.revision = revision;
- this.remote = remote;
- this.groups = new HashSet<String>();
- if (groups != null && groups.length() > 0)
- this.groups.addAll(Arrays.asList(groups.split(","))); //$NON-NLS-1$
- copyfiles = new ArrayList<CopyFile>();
- }
-
- void addCopyFile(CopyFile copyfile) {
- copyfiles.add(copyfile);
- }
-
- String getPathWithSlash() {
- if (path.endsWith("/")) //$NON-NLS-1$
- return path;
- else
- return path + "/"; //$NON-NLS-1$
- }
-
- boolean isAncestorOf(Project that) {
- return that.getPathWithSlash().startsWith(this.getPathWithSlash());
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof Project) {
- Project that = (Project) o;
- return this.getPathWithSlash().equals(that.getPathWithSlash());
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return this.getPathWithSlash().hashCode();
- }
-
- public int compareTo(Project that) {
- return this.getPathWithSlash().compareTo(that.getPathWithSlash());
- }
- }
-
- private static class XmlManifest extends DefaultHandler {
- private final RepoCommand command;
- private final String filename;
- private final String baseUrl;
- private final Map<String, String> remotes;
- private final Set<String> plusGroups;
- private final Set<String> minusGroups;
- private List<Project> projects;
- private String defaultRemote;
- private String defaultRevision;
- private IncludedFileReader includedReader;
- private int xmlInRead;
- private Project currentProject;
-
- XmlManifest(RepoCommand command, IncludedFileReader includedReader,
- String filename, String baseUrl, String groups) {
- this.command = command;
- this.includedReader = includedReader;
- this.filename = filename;
-
- // Strip trailing /s to match repo behavior.
- int lastIndex = baseUrl.length() - 1;
- while (lastIndex >= 0 && baseUrl.charAt(lastIndex) == '/')
- lastIndex--;
- this.baseUrl = baseUrl.substring(0, lastIndex + 1);
-
- remotes = new HashMap<String, String>();
- projects = new ArrayList<Project>();
- plusGroups = new HashSet<String>();
- minusGroups = new HashSet<String>();
- if (groups == null || groups.length() == 0 || groups.equals("default")) { //$NON-NLS-1$
- // default means "all,-notdefault"
- minusGroups.add("notdefault"); //$NON-NLS-1$
- } else {
- for (String group : groups.split(",")) { //$NON-NLS-1$
- if (group.startsWith("-")) //$NON-NLS-1$
- minusGroups.add(group.substring(1));
- else
- plusGroups.add(group);
- }
- }
- }
-
- void read(InputStream inputStream) throws IOException {
- xmlInRead++;
- final XMLReader xr;
- try {
- xr = XMLReaderFactory.createXMLReader();
- } catch (SAXException e) {
- throw new IOException(JGitText.get().noXMLParserAvailable);
- }
- xr.setContentHandler(this);
- try {
- xr.parse(new InputSource(inputStream));
- } catch (SAXException e) {
- IOException error = new IOException(
- RepoText.get().errorParsingManifestFile);
- error.initCause(e);
- throw error;
- }
- }
-
- @Override
- public void startElement(
- String uri,
- String localName,
- String qName,
- Attributes attributes) throws SAXException {
- if ("project".equals(qName)) { //$NON-NLS-1$
- currentProject = new Project(
- attributes.getValue("name"), //$NON-NLS-1$
- attributes.getValue("path"), //$NON-NLS-1$
- attributes.getValue("revision"), //$NON-NLS-1$
- attributes.getValue("remote"), //$NON-NLS-1$
- attributes.getValue("groups")); //$NON-NLS-1$
- } else if ("remote".equals(qName)) { //$NON-NLS-1$
- String alias = attributes.getValue("alias"); //$NON-NLS-1$
- String fetch = attributes.getValue("fetch"); //$NON-NLS-1$
- remotes.put(attributes.getValue("name"), fetch); //$NON-NLS-1$
- if (alias != null)
- remotes.put(alias, fetch);
- } else if ("default".equals(qName)) { //$NON-NLS-1$
- defaultRemote = attributes.getValue("remote"); //$NON-NLS-1$
- defaultRevision = attributes.getValue("revision"); //$NON-NLS-1$
- if (defaultRevision == null)
- defaultRevision = command.branch;
- } else if ("copyfile".equals(qName)) { //$NON-NLS-1$
- if (currentProject == null)
- throw new SAXException(RepoText.get().invalidManifest);
- currentProject.addCopyFile(new CopyFile(
- command.repo,
- currentProject.path,
- attributes.getValue("src"), //$NON-NLS-1$
- attributes.getValue("dest"))); //$NON-NLS-1$
- } else if ("include".equals(qName)) { //$NON-NLS-1$
- String name = attributes.getValue("name"); //$NON-NLS-1$
- InputStream is = null;
- if (includedReader != null) {
- try {
- is = includedReader.readIncludeFile(name);
- } catch (Exception e) {
- throw new SAXException(MessageFormat.format(
- RepoText.get().errorIncludeFile, name), e);
- }
- } else if (filename != null) {
- int index = filename.lastIndexOf('/');
- String path = filename.substring(0, index + 1) + name;
- try {
- is = new FileInputStream(path);
- } catch (IOException e) {
- throw new SAXException(MessageFormat.format(
- RepoText.get().errorIncludeFile, path), e);
- }
- }
- if (is == null) {
- throw new SAXException(
- RepoText.get().errorIncludeNotImplemented);
- }
- try {
- read(is);
- } catch (IOException e) {
- throw new SAXException(e);
- }
- }
- }
-
- @Override
- public void endElement(
- String uri,
- String localName,
- String qName) throws SAXException {
- if ("project".equals(qName)) { //$NON-NLS-1$
- projects.add(currentProject);
- currentProject = null;
- }
- }
-
- @Override
- public void endDocument() throws SAXException {
- xmlInRead--;
- if (xmlInRead != 0)
- return;
-
- // Only do the following after we finished reading everything.
- removeNotInGroup();
- removeOverlaps();
-
- Map<String, String> remoteUrls = new HashMap<String, String>();
- URI baseUri;
- try {
- baseUri = new URI(baseUrl);
- } catch (URISyntaxException e) {
- throw new SAXException(e);
- }
- for (Project proj : projects) {
- String remote = proj.remote;
- if (remote == null) {
- if (defaultRemote == null) {
- if (filename != null)
- throw new SAXException(MessageFormat.format(
- RepoText.get().errorNoDefaultFilename,
- filename));
- else
- throw new SAXException(
- RepoText.get().errorNoDefault);
- }
- remote = defaultRemote;
- }
- String remoteUrl = remoteUrls.get(remote);
- if (remoteUrl == null) {
- remoteUrl = baseUri.resolve(remotes.get(remote)).toString();
- if (!remoteUrl.endsWith("/")) //$NON-NLS-1$
- remoteUrl = remoteUrl + "/"; //$NON-NLS-1$
- remoteUrls.put(remote, remoteUrl);
- }
-
- command.addSubmodule(remoteUrl + proj.name,
- proj.path,
- proj.revision == null
- ? defaultRevision : proj.revision,
- proj.copyfiles);
- }
- }
-
- /** Remove projects that are not in our desired groups. */
- void removeNotInGroup() {
- Iterator<Project> iter = projects.iterator();
- while (iter.hasNext())
- if (!inGroups(iter.next()))
- iter.remove();
- }
-
- /** Remove projects that sits in a subdirectory of any other project. */
- void removeOverlaps() {
- Collections.sort(projects);
- Iterator<Project> iter = projects.iterator();
- if (!iter.hasNext())
- return;
- Project last = iter.next();
- while (iter.hasNext()) {
- Project p = iter.next();
- if (last.isAncestorOf(p))
- iter.remove();
- else
- last = p;
- }
- }
-
- boolean inGroups(Project proj) {
- for (String group : minusGroups) {
- if (proj.groups.contains(group)) {
- // minus groups have highest priority.
- return false;
- }
- }
- if (plusGroups.isEmpty() || plusGroups.contains("all")) { //$NON-NLS-1$
- // empty plus groups means "all"
- return true;
- }
- for (String group : plusGroups) {
- if (proj.groups.contains(group))
- return true;
- }
- return false;
- }
- }
-
@SuppressWarnings("serial")
private static class ManifestErrorException extends GitAPIException {
ManifestErrorException(Throwable cause) {
@@ -578,27 +229,27 @@ public class RepoCommand extends GitCommand<RevCommit> {
/**
* @param repo
*/
- public RepoCommand(final Repository repo) {
+ public RepoCommand(Repository repo) {
super(repo);
}
/**
* Set path to the manifest XML file.
- *
+ * <p>
* Calling {@link #setInputStream} will ignore the path set here.
*
* @param path
* (with <code>/</code> as separator)
* @return this command
*/
- public RepoCommand setPath(final String path) {
+ public RepoCommand setPath(String path) {
this.path = path;
return this;
}
/**
* Set the input stream to the manifest XML.
- *
+ * <p>
* Setting inputStream will ignore the path set. It will be closed in
* {@link #call}.
*
@@ -606,7 +257,7 @@ public class RepoCommand extends GitCommand<RevCommit> {
* @return this command
* @since 3.5
*/
- public RepoCommand setInputStream(final InputStream inputStream) {
+ public RepoCommand setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
return this;
}
@@ -617,7 +268,7 @@ public class RepoCommand extends GitCommand<RevCommit> {
* @param uri
* @return this command
*/
- public RepoCommand setURI(final String uri) {
+ public RepoCommand setURI(String uri) {
this.uri = uri;
return this;
}
@@ -628,14 +279,14 @@ public class RepoCommand extends GitCommand<RevCommit> {
* @param groups groups separated by comma, examples: default|all|G1,-G2,-G3
* @return this command
*/
- public RepoCommand setGroups(final String groups) {
+ public RepoCommand setGroups(String groups) {
this.groups = groups;
return this;
}
/**
* Set default branch.
- *
+ * <p>
* This is generally the name of the branch the manifest file was in. If
* there's no default revision (branch) specified in manifest and no
* revision specified in project, this branch will be used.
@@ -643,12 +294,54 @@ public class RepoCommand extends GitCommand<RevCommit> {
* @param branch
* @return this command
*/
- public RepoCommand setBranch(final String branch) {
+ public RepoCommand setBranch(String branch) {
this.branch = branch;
return this;
}
/**
+ * Set target branch.
+ * <p>
+ * This is the target branch of the super project to be updated. If not set,
+ * default is HEAD.
+ * <p>
+ * For non-bare repositories, HEAD will always be used and this will be
+ * ignored.
+ *
+ * @param branch
+ * @return this command
+ * @since 4.1
+ */
+ public RepoCommand setTargetBranch(String branch) {
+ this.targetBranch = Constants.R_HEADS + branch;
+ return this;
+ }
+
+ /**
+ * Set whether the branch name should be recorded in .gitmodules
+ * <p>
+ * Submodule entries in .gitmodules can include a "branch" field
+ * to indicate what remote branch each submodule tracks.
+ * <p>
+ * That field is used by "git submodule update --remote" to update
+ * to the tip of the tracked branch when asked and by Gerrit to
+ * update the superproject when a change on that branch is merged.
+ * <p>
+ * Subprojects that request a specific commit or tag will not have
+ * a branch name recorded.
+ * <p>
+ * Not implemented for non-bare repositories.
+ *
+ * @param enable Whether to record the branch name
+ * @return this command
+ * @since 4.2
+ */
+ public RepoCommand setRecordRemoteBranch(boolean enable) {
+ this.recordRemoteBranch = enable;
+ return this;
+ }
+
+ /**
* The progress monitor associated with the clone operation. By default,
* this is set to <code>NullProgressMonitor</code>
*
@@ -663,7 +356,7 @@ public class RepoCommand extends GitCommand<RevCommit> {
/**
* Set the author/committer for the bare repository commit.
- *
+ * <p>
* For non-bare repositories, the current user will be used and this will be
* ignored.
*
@@ -693,7 +386,7 @@ public class RepoCommand extends GitCommand<RevCommit> {
*
* @param reader
* @return this command
- * @since 3.5
+ * @since 4.0
*/
public RepoCommand setIncludedFileReader(IncludedFileReader reader) {
this.includedReader = reader;
@@ -720,7 +413,7 @@ public class RepoCommand extends GitCommand<RevCommit> {
}
if (repo.isBare()) {
- bareProjects = new ArrayList<Project>();
+ bareProjects = new ArrayList<RepoProject>();
if (author == null)
author = new PersonIdent(repo);
if (callback == null)
@@ -728,11 +421,17 @@ public class RepoCommand extends GitCommand<RevCommit> {
} else
git = new Git(repo);
- XmlManifest manifest = new XmlManifest(
- this, includedReader, path, uri, groups);
+ ManifestParser parser = new ManifestParser(
+ includedReader, path, branch, uri, groups, repo);
try {
- manifest.read(inputStream);
- } catch (IOException e) {
+ parser.read(inputStream);
+ for (RepoProject proj : parser.getFilteredProjects()) {
+ addSubmodule(proj.getUrl(),
+ proj.getPath(),
+ proj.getRevision(),
+ proj.getCopyFiles());
+ }
+ } catch (GitAPIException | IOException e) {
throw new ManifestErrorException(e);
}
} finally {
@@ -748,21 +447,24 @@ public class RepoCommand extends GitCommand<RevCommit> {
DirCache index = DirCache.newInCore();
DirCacheBuilder builder = index.builder();
ObjectInserter inserter = repo.newObjectInserter();
- RevWalk rw = new RevWalk(repo);
- try {
+ try (RevWalk rw = new RevWalk(repo)) {
Config cfg = new Config();
- for (Project proj : bareProjects) {
- String name = proj.path;
- String nameUri = proj.name;
+ for (RepoProject proj : bareProjects) {
+ String name = proj.getPath();
+ String nameUri = proj.getName();
cfg.setString("submodule", name, "path", name); //$NON-NLS-1$ //$NON-NLS-2$
cfg.setString("submodule", name, "url", nameUri); //$NON-NLS-1$ //$NON-NLS-2$
// create gitlink
DirCacheEntry dcEntry = new DirCacheEntry(name);
ObjectId objectId;
- if (ObjectId.isId(proj.revision))
- objectId = ObjectId.fromString(proj.revision);
- else {
- objectId = callback.sha1(nameUri, proj.revision);
+ if (ObjectId.isId(proj.getRevision())) {
+ objectId = ObjectId.fromString(proj.getRevision());
+ } else {
+ objectId = callback.sha1(nameUri, proj.getRevision());
+ if (recordRemoteBranch)
+ // can be branch or tag
+ cfg.setString("submodule", name, "branch", //$NON-NLS-1$ //$NON-NLS-2$
+ proj.getRevision());
}
if (objectId == null)
throw new RemoteUnavailableException(nameUri);
@@ -770,9 +472,9 @@ public class RepoCommand extends GitCommand<RevCommit> {
dcEntry.setFileMode(FileMode.GITLINK);
builder.add(dcEntry);
- for (CopyFile copyfile : proj.copyfiles) {
+ for (CopyFile copyfile : proj.getCopyFiles()) {
byte[] src = callback.readFile(
- nameUri, proj.revision, copyfile.src);
+ nameUri, proj.getRevision(), copyfile.src);
objectId = inserter.insert(Constants.OBJ_BLOB, src);
dcEntry = new DirCacheEntry(copyfile.dest);
dcEntry.setObjectId(objectId);
@@ -794,7 +496,7 @@ public class RepoCommand extends GitCommand<RevCommit> {
ObjectId treeId = index.writeTree(inserter);
// Create a Commit object, populate it and write it
- ObjectId headId = repo.resolve(Constants.HEAD + "^{commit}"); //$NON-NLS-1$
+ ObjectId headId = repo.resolve(targetBranch + "^{commit}"); //$NON-NLS-1$
CommitBuilder commit = new CommitBuilder();
commit.setTreeId(treeId);
if (headId != null)
@@ -806,7 +508,7 @@ public class RepoCommand extends GitCommand<RevCommit> {
ObjectId commitId = inserter.insert(commit);
inserter.flush();
- RefUpdate ru = repo.updateRef(Constants.HEAD);
+ RefUpdate ru = repo.updateRef(targetBranch);
ru.setNewObjectId(commitId);
ru.setExpectedOldObjectId(headId != null ? headId : ObjectId.zeroId());
Result rc = ru.update(rw);
@@ -820,19 +522,19 @@ public class RepoCommand extends GitCommand<RevCommit> {
case REJECTED:
case LOCK_FAILURE:
throw new ConcurrentRefUpdateException(
- JGitText.get().couldNotLockHEAD, ru.getRef(),
+ MessageFormat.format(
+ JGitText.get().cannotLock, targetBranch),
+ ru.getRef(),
rc);
default:
throw new JGitInternalException(MessageFormat.format(
JGitText.get().updatingRefFailed,
- Constants.HEAD, commitId.name(), rc));
+ targetBranch, commitId.name(), rc));
}
return rw.parseCommit(commitId);
} catch (IOException e) {
throw new ManifestErrorException(e);
- } finally {
- rw.release();
}
} else {
return git
@@ -843,10 +545,10 @@ public class RepoCommand extends GitCommand<RevCommit> {
}
private void addSubmodule(String url, String name, String revision,
- List<CopyFile> copyfiles) throws SAXException {
+ List<CopyFile> copyfiles) throws GitAPIException, IOException {
if (repo.isBare()) {
- Project proj = new Project(url, name, revision, null, null);
- proj.copyfiles.addAll(copyfiles);
+ RepoProject proj = new RepoProject(url, name, revision, null, null);
+ proj.addCopyFiles(copyfiles);
bareProjects.add(proj);
} else {
SubmoduleAddCommand add = git
@@ -856,22 +558,18 @@ public class RepoCommand extends GitCommand<RevCommit> {
if (monitor != null)
add.setProgressMonitor(monitor);
- try {
- Repository subRepo = add.call();
- if (revision != null) {
- Git sub = new Git(subRepo);
- sub.checkout().setName(findRef(revision, subRepo)).call();
- subRepo.close();
- git.add().addFilepattern(name).call();
+ Repository subRepo = add.call();
+ if (revision != null) {
+ try (Git sub = new Git(subRepo)) {
+ sub.checkout().setName(findRef(revision, subRepo))
+ .call();
}
- for (CopyFile copyfile : copyfiles) {
- copyfile.copy();
- git.add().addFilepattern(copyfile.dest).call();
- }
- } catch (GitAPIException e) {
- throw new SAXException(e);
- } catch (IOException e) {
- throw new SAXException(e);
+ subRepo.close();
+ git.add().addFilepattern(name).call();
+ }
+ for (CopyFile copyfile : copyfiles) {
+ copyfile.copy();
+ git.add().addFilepattern(copyfile.dest).call();
}
}
}
@@ -879,7 +577,7 @@ public class RepoCommand extends GitCommand<RevCommit> {
private static String findRef(String ref, Repository repo)
throws IOException {
if (!ObjectId.isId(ref)) {
- Ref r = repo.getRef(Constants.DEFAULT_REMOTE_NAME + "/" + ref); //$NON-NLS-1$
+ Ref r = repo.exactRef(R_REMOTES + DEFAULT_REMOTE_NAME + "/" + ref); //$NON-NLS-1$
if (r != null)
return r.getName();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
new file mode 100644
index 0000000..915066d
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.gitrepo;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * The representation of a repo sub project.
+ *
+ * @see <a href="https://code.google.com/p/git-repo/">git-repo project page</a>
+ * @since 4.0
+ */
+public class RepoProject implements Comparable<RepoProject> {
+ private final String name;
+ private final String path;
+ private final String revision;
+ private final String remote;
+ private final Set<String> groups;
+ private final List<CopyFile> copyfiles;
+ private String url;
+ private String defaultRevision;
+
+ /**
+ * The representation of a copy file configuration.
+ */
+ public static class CopyFile {
+ final Repository repo;
+ final String path;
+ final String src;
+ final String dest;
+
+ /**
+ * @param repo
+ * the super project.
+ * @param path
+ * the path of the project containing this copyfile config.
+ * @param src
+ * the source path relative to the sub repo.
+ * @param dest
+ * the destination path relative to the super project.
+ */
+ public CopyFile(Repository repo, String path, String src, String dest) {
+ this.repo = repo;
+ this.path = path;
+ this.src = src;
+ this.dest = dest;
+ }
+
+ /**
+ * Do the copy file action.
+ *
+ * @throws IOException
+ */
+ public void copy() throws IOException {
+ File srcFile = new File(repo.getWorkTree(),
+ path + "/" + src); //$NON-NLS-1$
+ File destFile = new File(repo.getWorkTree(), dest);
+ FileInputStream input = new FileInputStream(srcFile);
+ try {
+ FileOutputStream output = new FileOutputStream(destFile);
+ try {
+ FileChannel channel = input.getChannel();
+ output.getChannel().transferFrom(
+ channel, 0, channel.size());
+ } finally {
+ output.close();
+ }
+ } finally {
+ input.close();
+ }
+ }
+ }
+
+ /**
+ * @param name
+ * the relative path to the {@code remote}
+ * @param path
+ * the relative path to the super project
+ * @param revision
+ * a SHA-1 or branch name or tag name
+ * @param remote
+ * name of the remote definition
+ * @param groups
+ * comma separated group list
+ */
+ public RepoProject(String name, String path, String revision,
+ String remote, String groups) {
+ if (name == null) {
+ throw new NullPointerException();
+ }
+ this.name = name;
+ if (path != null)
+ this.path = path;
+ else
+ this.path = name;
+ this.revision = revision;
+ this.remote = remote;
+ this.groups = new HashSet<String>();
+ if (groups != null && groups.length() > 0)
+ this.groups.addAll(Arrays.asList(groups.split(","))); //$NON-NLS-1$
+ copyfiles = new ArrayList<CopyFile>();
+ }
+
+ /**
+ * Set the url of the sub repo.
+ *
+ * @param url
+ * @return this for chaining.
+ */
+ public RepoProject setUrl(String url) {
+ this.url = url;
+ return this;
+ }
+
+ /**
+ * Set the default revision for the sub repo.
+ *
+ * @param defaultRevision
+ * @return this for chaining.
+ */
+ public RepoProject setDefaultRevision(String defaultRevision) {
+ this.defaultRevision = defaultRevision;
+ return this;
+ }
+
+ /**
+ * Get the name (relative path to the {@code remote}) of this sub repo.
+ *
+ * @return {@code name}
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the path (relative path to the super project) of this sub repo.
+ *
+ * @return {@code path}
+ */
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * Get the revision of the sub repo.
+ *
+ * @return {@code revision} if set, or {@code defaultRevision}.
+ */
+ public String getRevision() {
+ return revision == null ? defaultRevision : revision;
+ }
+
+ /**
+ * Getter for the copyfile configurations.
+ *
+ * @return Immutable copy of {@code copyfiles}
+ */
+ public List<CopyFile> getCopyFiles() {
+ return Collections.unmodifiableList(copyfiles);
+ }
+
+ /**
+ * Get the url of the sub repo.
+ *
+ * @return {@code url}
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * Get the name of the remote definition of the sub repo.
+ *
+ * @return {@code remote}
+ */
+ public String getRemote() {
+ return remote;
+ }
+
+ /**
+ * Test whether this sub repo belongs to a specified group.
+ *
+ * @param group
+ * @return true if {@code group} is present.
+ */
+ public boolean inGroup(String group) {
+ return groups.contains(group);
+ }
+
+ /**
+ * Add a copy file configuration.
+ *
+ * @param copyfile
+ */
+ public void addCopyFile(CopyFile copyfile) {
+ copyfiles.add(copyfile);
+ }
+
+ /**
+ * Add a bunch of copyfile configurations.
+ *
+ * @param copyfiles
+ */
+ public void addCopyFiles(Collection<CopyFile> copyfiles) {
+ this.copyfiles.addAll(copyfiles);
+ }
+
+ /**
+ * Clear all the copyfiles.
+ *
+ * @since 4.2
+ */
+ public void clearCopyFiles() {
+ this.copyfiles.clear();
+ }
+
+ private String getPathWithSlash() {
+ if (path.endsWith("/")) //$NON-NLS-1$
+ return path;
+ else
+ return path + "/"; //$NON-NLS-1$
+ }
+
+ /**
+ * Check if this sub repo is the ancestor of given sub repo.
+ *
+ * @param that
+ * non null
+ * @return true if this sub repo is the ancestor of given sub repo.
+ */
+ public boolean isAncestorOf(RepoProject that) {
+ return isAncestorOf(that.getPathWithSlash());
+ }
+
+ /**
+ * Check if this sub repo is an ancestor of the given path.
+ *
+ * @param path
+ * path to be checked to see if it is within this repository
+ * @return true if this sub repo is an ancestor of the given path.
+ * @since 4.2
+ */
+ public boolean isAncestorOf(String path) {
+ return path.startsWith(getPathWithSlash());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof RepoProject) {
+ RepoProject that = (RepoProject) o;
+ return this.getPathWithSlash().equals(that.getPathWithSlash());
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.getPathWithSlash().hashCode();
+ }
+
+ @Override
+ public int compareTo(RepoProject that) {
+ return this.getPathWithSlash().compareTo(that.getPathWithSlash());
+ }
+}
+
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/CommitMsgHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/CommitMsgHook.java
new file mode 100644
index 0000000..fa17075
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/CommitMsgHook.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2015 Obeo.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.hooks;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import org.eclipse.jgit.api.errors.AbortedByHookException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * The <code>commit-msg</code> hook implementation. This hook is run before the
+ * commit and can reject the commit. It passes one argument to the hook script,
+ * which is the path to the COMMIT_MSG file, relative to the repository
+ * workTree.
+ *
+ * @since 4.0
+ */
+public class CommitMsgHook extends GitHook<String> {
+
+ /**
+ * Constant indicating the name of the commit-smg hook.
+ */
+ public static final String NAME = "commit-msg"; //$NON-NLS-1$
+
+ /**
+ * The commit message.
+ */
+ private String commitMessage;
+
+ /**
+ * @param repo
+ * The repository
+ * @param outputStream
+ * The output stream the hook must use. {@code null} is allowed,
+ * in which case the hook will use {@code System.out}.
+ */
+ protected CommitMsgHook(Repository repo, PrintStream outputStream) {
+ super(repo, outputStream);
+ }
+
+ @Override
+ public String call() throws IOException, AbortedByHookException {
+ if (commitMessage == null) {
+ throw new IllegalStateException();
+ }
+ if (canRun()) {
+ getRepository().writeCommitEditMsg(commitMessage);
+ doRun();
+ commitMessage = getRepository().readCommitEditMsg();
+ }
+ return commitMessage;
+ }
+
+ /**
+ * @return {@code true} if and only if the path to the message commit file
+ * is not null (which would happen in a bare repository) and the
+ * commit message is also not null.
+ */
+ private boolean canRun() {
+ return getCommitEditMessageFilePath() != null && commitMessage != null;
+ }
+
+ @Override
+ public String getHookName() {
+ return NAME;
+ }
+
+ /**
+ * This hook receives one parameter, which is the path to the file holding
+ * the current commit-msg, relative to the repository's work tree.
+ */
+ @Override
+ protected String[] getParameters() {
+ return new String[] { getCommitEditMessageFilePath() };
+ }
+
+ /**
+ * @return The path to the commit edit message file relative to the
+ * repository's work tree, or null if the repository is bare.
+ */
+ private String getCommitEditMessageFilePath() {
+ File gitDir = getRepository().getDirectory();
+ if (gitDir == null) {
+ return null;
+ }
+ return Repository.stripWorkDir(getRepository().getWorkTree(), new File(
+ gitDir, Constants.COMMIT_EDITMSG));
+ }
+
+ /**
+ * It is mandatory to call this method with a non-null value before actually
+ * calling the hook.
+ *
+ * @param commitMessage
+ * The commit message before the hook has run.
+ * @return {@code this} for convenience.
+ */
+ public CommitMsgHook setCommitMessage(String commitMessage) {
+ this.commitMessage = commitMessage;
+ return this;
+ }
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
new file mode 100644
index 0000000..ad2eeb0
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2015 Obeo.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.hooks;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.concurrent.Callable;
+
+import org.eclipse.jgit.api.errors.AbortedByHookException;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.ProcessResult;
+
+/**
+ * Git can fire off custom scripts when certain important actions occur. These
+ * custom scripts are called "hooks". There are two groups of hooks: client-side
+ * (that run on local operations such as committing and merging), and
+ * server-side (that run on network operations such as receiving pushed
+ * commits). This is the abstract super-class of the different hook
+ * implementations in JGit.
+ *
+ * @param <T>
+ * the return type which is expected from {@link #call()}
+ * @see <a href="http://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks">Git
+ * Hooks on the git-scm official site</a>
+ * @since 4.0
+ */
+abstract class GitHook<T> implements Callable<T> {
+
+ private final Repository repo;
+
+ /**
+ * The output stream to be used by the hook.
+ */
+ protected final PrintStream outputStream;
+
+ /**
+ * @param repo
+ * @param outputStream
+ * The output stream the hook must use. {@code null} is allowed,
+ * in which case the hook will use {@code System.out}.
+ */
+ protected GitHook(Repository repo, PrintStream outputStream) {
+ this.repo = repo;
+ this.outputStream = outputStream;
+ }
+
+ /**
+ * Run the hook.
+ *
+ * @throws IOException
+ * if IO goes wrong.
+ * @throws AbortedByHookException
+ * If the hook has been run and a returned an exit code
+ * different from zero.
+ */
+ public abstract T call() throws IOException, AbortedByHookException;
+
+ /**
+ * @return The name of the hook, which must not be {@code null}.
+ */
+ public abstract String getHookName();
+
+ /**
+ * @return The repository.
+ */
+ protected Repository getRepository() {
+ return repo;
+ }
+
+ /**
+ * Override this method when needed to provide relevant parameters to the
+ * underlying hook script. The default implementation returns an empty
+ * array.
+ *
+ * @return The parameters the hook receives.
+ */
+ protected String[] getParameters() {
+ return new String[0];
+ }
+
+ /**
+ * Override to provide relevant arguments via stdin to the underlying hook
+ * script. The default implementation returns {@code null}.
+ *
+ * @return The parameters the hook receives.
+ */
+ protected String getStdinArgs() {
+ return null;
+ }
+
+ /**
+ * @return The output stream the hook must use. Never {@code null},
+ * {@code System.out} is returned by default.
+ */
+ protected PrintStream getOutputStream() {
+ return outputStream == null ? System.out : outputStream;
+ }
+
+ /**
+ * Runs the hook, without performing any validity checks.
+ *
+ * @throws AbortedByHookException
+ * If the underlying hook script exited with non-zero.
+ */
+ protected void doRun() throws AbortedByHookException {
+ final ByteArrayOutputStream errorByteArray = new ByteArrayOutputStream();
+ final PrintStream hookErrRedirect = new PrintStream(errorByteArray);
+ ProcessResult result = FS.DETECTED.runHookIfPresent(getRepository(),
+ getHookName(), getParameters(), getOutputStream(),
+ hookErrRedirect, getStdinArgs());
+ if (result.isExecutedWithError()) {
+ throw new AbortedByHookException(errorByteArray.toString(),
+ getHookName(), result.getExitCode());
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyRecursive.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/Hooks.java
similarity index 63%
copy from org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyRecursive.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/hooks/Hooks.java
index 58c1ed2..6f7a21a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyRecursive.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/Hooks.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012, Research In Motion Limited
+ * Copyright (C) 2015 Obeo.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -40,30 +40,49 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+package org.eclipse.jgit.hooks;
-package org.eclipse.jgit.merge;
+import java.io.PrintStream;
import org.eclipse.jgit.lib.Repository;
/**
- * A three-way merge strategy performing a content-merge if necessary
+ * Factory class for instantiating supported hooks.
*
- * @since 3.0
+ * @since 4.0
*/
-public class StrategyRecursive extends StrategyResolve {
+public class Hooks {
- @Override
- public ThreeWayMerger newMerger(Repository db) {
- return new RecursiveMerger(db, false);
+ /**
+ * @param repo
+ * @param outputStream
+ * The output stream, or {@code null} to use {@code System.out}
+ * @return The pre-commit hook for the given repository.
+ */
+ public static PreCommitHook preCommit(Repository repo,
+ PrintStream outputStream) {
+ return new PreCommitHook(repo, outputStream);
}
- @Override
- public ThreeWayMerger newMerger(Repository db, boolean inCore) {
- return new RecursiveMerger(db, inCore);
+ /**
+ * @param repo
+ * @param outputStream
+ * The output stream, or {@code null} to use {@code System.out}
+ * @return The commit-msg hook for the given repository.
+ */
+ public static CommitMsgHook commitMsg(Repository repo,
+ PrintStream outputStream) {
+ return new CommitMsgHook(repo, outputStream);
}
- @Override
- public String getName() {
- return "recursive";
+ /**
+ * @param repo
+ * @param outputStream
+ * The output stream, or {@code null} to use {@code System.out}
+ * @return The pre-push hook for the given repository.
+ * @since 4.2
+ */
+ public static PrePushHook prePush(Repository repo, PrintStream outputStream) {
+ return new PrePushHook(repo, outputStream);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyRecursive.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PreCommitHook.java
similarity index 68%
copy from org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyRecursive.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/hooks/PreCommitHook.java
index 58c1ed2..1ab32e0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyRecursive.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PreCommitHook.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012, Research In Motion Limited
+ * Copyright (C) 2015 Obeo.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -40,30 +40,45 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+package org.eclipse.jgit.hooks;
-package org.eclipse.jgit.merge;
+import java.io.IOException;
+import java.io.PrintStream;
+import org.eclipse.jgit.api.errors.AbortedByHookException;
import org.eclipse.jgit.lib.Repository;
/**
- * A three-way merge strategy performing a content-merge if necessary
+ * The <code>pre-commit</code> hook implementation. This hook is run before the
+ * commit and can reject the commit.
*
- * @since 3.0
+ * @since 4.0
*/
-public class StrategyRecursive extends StrategyResolve {
+public class PreCommitHook extends GitHook<Void> {
- @Override
- public ThreeWayMerger newMerger(Repository db) {
- return new RecursiveMerger(db, false);
+ /** The pre-commit hook name. */
+ public static final String NAME = "pre-commit"; //$NON-NLS-1$
+
+ /**
+ * @param repo
+ * The repository
+ * @param outputStream
+ * The output stream the hook must use. {@code null} is allowed,
+ * in which case the hook will use {@code System.out}.
+ */
+ protected PreCommitHook(Repository repo, PrintStream outputStream) {
+ super(repo, outputStream);
}
@Override
- public ThreeWayMerger newMerger(Repository db, boolean inCore) {
- return new RecursiveMerger(db, inCore);
+ public Void call() throws IOException, AbortedByHookException {
+ doRun();
+ return null;
}
@Override
- public String getName() {
- return "recursive";
+ public String getHookName() {
+ return NAME;
}
+
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java
new file mode 100644
index 0000000..a501fee
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2015 Obeo.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.hooks;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Collection;
+
+import org.eclipse.jgit.api.errors.AbortedByHookException;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.RemoteRefUpdate;
+
+/**
+ * The <code>pre-push</code> hook implementation. The pre-push hook runs during
+ * git push, after the remote refs have been updated but before any objects have
+ * been transferred.
+ *
+ * @since 4.2
+ */
+public class PrePushHook extends GitHook<String> {
+
+ /**
+ * Constant indicating the name of the pre-push hook.
+ */
+ public static final String NAME = "pre-push"; //$NON-NLS-1$
+
+ private String remoteName;
+
+ private String remoteLocation;
+
+ private String refs;
+
+ /**
+ * @param repo
+ * The repository
+ * @param outputStream
+ * The output stream the hook must use. {@code null} is allowed,
+ * in which case the hook will use {@code System.out}.
+ */
+ protected PrePushHook(Repository repo, PrintStream outputStream) {
+ super(repo, outputStream);
+ }
+
+ @Override
+ protected String getStdinArgs() {
+ return refs;
+ }
+
+ @Override
+ public String call() throws IOException, AbortedByHookException {
+ if (canRun()) {
+ doRun();
+ }
+ return ""; //$NON-NLS-1$
+ }
+
+ /**
+ * @return {@code true}
+ */
+ private boolean canRun() {
+ return true;
+ }
+
+ @Override
+ public String getHookName() {
+ return NAME;
+ }
+
+ /**
+ * This hook receives two parameters, which is the name and the location of
+ * the remote repository.
+ */
+ @Override
+ protected String[] getParameters() {
+ if (remoteName == null) {
+ remoteName = remoteLocation;
+ }
+ return new String[] { remoteName, remoteLocation };
+ }
+
+ /**
+ * @param name
+ */
+ public void setRemoteName(String name) {
+ remoteName = name;
+ }
+
+ /**
+ * @param location
+ */
+ public void setRemoteLocation(String location) {
+ remoteLocation = location;
+ }
+
+ /**
+ * @param toRefs
+ */
+ public void setRefs(Collection<RemoteRefUpdate> toRefs) {
+ StringBuilder b = new StringBuilder();
+ boolean first = true;
+ for (RemoteRefUpdate u : toRefs) {
+ if (!first)
+ b.append("\n"); //$NON-NLS-1$
+ else
+ first = false;
+ b.append(u.getSrcRef());
+ b.append(" "); //$NON-NLS-1$
+ b.append(u.getNewObjectId().getName());
+ b.append(" "); //$NON-NLS-1$
+ b.append(u.getRemoteName());
+ b.append(" "); //$NON-NLS-1$
+ ObjectId ooid = u.getExpectedOldObjectId();
+ b.append((ooid == null) ? ObjectId.zeroId().getName() : ooid
+ .getName());
+ }
+ refs = b.toString();
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java
index 2303ffd..e376cbb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java
@@ -47,6 +47,8 @@ import static org.eclipse.jgit.ignore.internal.IMatcher.NO_MATCH;
import org.eclipse.jgit.errors.InvalidPatternException;
import org.eclipse.jgit.ignore.internal.IMatcher;
import org.eclipse.jgit.ignore.internal.PathMatcher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* "Fast" (compared with IgnoreRule) git ignore rule implementation supporting
@@ -57,6 +59,8 @@ import org.eclipse.jgit.ignore.internal.PathMatcher;
* @since 3.6
*/
public class FastIgnoreRule {
+ private final static Logger LOG = LoggerFactory
+ .getLogger(FastIgnoreRule.class);
/**
* Character used as default path separator for ignore entries
@@ -98,24 +102,32 @@ public class FastIgnoreRule {
if (pattern.charAt(0) == '#') {
this.matcher = NO_MATCH;
dirOnly = false;
- } else {
- dirOnly = pattern.charAt(pattern.length() - 1) == PATH_SEPARATOR;
- if (dirOnly) {
- pattern = stripTrailing(pattern, PATH_SEPARATOR);
- if (pattern.length() == 0) {
- this.matcher = NO_MATCH;
- return;
- }
+ return;
+ }
+ if (pattern.charAt(0) == '\\' && pattern.length() > 1) {
+ char next = pattern.charAt(1);
+ if (next == '!' || next == '#') {
+ // remove backslash escaping first special characters
+ pattern = pattern.substring(1);
}
- IMatcher m;
- try {
- m = PathMatcher.createPathMatcher(pattern,
- Character.valueOf(PATH_SEPARATOR), dirOnly);
- } catch (InvalidPatternException e) {
- m = NO_MATCH;
+ }
+ dirOnly = pattern.charAt(pattern.length() - 1) == PATH_SEPARATOR;
+ if (dirOnly) {
+ pattern = stripTrailing(pattern, PATH_SEPARATOR);
+ if (pattern.length() == 0) {
+ this.matcher = NO_MATCH;
+ return;
}
- this.matcher = m;
}
+ IMatcher m;
+ try {
+ m = PathMatcher.createPathMatcher(pattern,
+ Character.valueOf(PATH_SEPARATOR), dirOnly);
+ } catch (InvalidPatternException e) {
+ m = NO_MATCH;
+ LOG.error(e.getMessage(), e);
+ }
+ this.matcher = m;
}
/**
@@ -176,6 +188,14 @@ public class FastIgnoreRule {
return !inverse;
}
+ /**
+ * @return true if the rule never matches (comment line or broken pattern)
+ * @since 4.1
+ */
+ public boolean isEmpty() {
+ return matcher == NO_MATCH;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java
index efaeacd..8b1244e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java
@@ -109,9 +109,12 @@ public class IgnoreNode {
BufferedReader br = asReader(in);
String txt;
while ((txt = br.readLine()) != null) {
- txt = txt.trim();
- if (txt.length() > 0 && !txt.startsWith("#") && !txt.equals("/")) //$NON-NLS-1$ //$NON-NLS-2$
- rules.add(new FastIgnoreRule(txt));
+ if (txt.length() > 0 && !txt.startsWith("#") && !txt.equals("/")) { //$NON-NLS-1$ //$NON-NLS-2$
+ FastIgnoreRule rule = new FastIgnoreRule(txt);
+ if (!rule.isEmpty()) {
+ rules.add(rule);
+ }
+ }
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreRule.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreRule.java
deleted file mode 100644
index f14d1bd..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreRule.java
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Copyright (C) 2010, Red Hat Inc.
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package org.eclipse.jgit.ignore;
-
-import org.eclipse.jgit.errors.InvalidPatternException;
-import org.eclipse.jgit.fnmatch.FileNameMatcher;
-
-/**
- * A single ignore rule corresponding to one line in a .gitignore or ignore
- * file. Parses the ignore pattern
- *
- * Inspiration from: Ferry Huberts
- *
- * @deprecated this rule does not support double star pattern and is slow
- * parsing glob expressions. Consider to use {@link FastIgnoreRule}
- * instead. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=440732
- */
- at Deprecated
-public class IgnoreRule {
- private String pattern;
- private boolean negation;
- private boolean nameOnly;
- private boolean dirOnly;
- private FileNameMatcher matcher;
-
- /**
- * Create a new ignore rule with the given pattern. Assumes that
- * the pattern is already trimmed.
- *
- * @param pattern
- * Base pattern for the ignore rule. This pattern will
- * be parsed to generate rule parameters.
- */
- public IgnoreRule (String pattern) {
- this.pattern = pattern;
- negation = false;
- nameOnly = false;
- dirOnly = false;
- matcher = null;
- setup();
- }
-
- /**
- * Remove leading/trailing characters as needed. Set up
- * rule variables for later matching.
- */
- private void setup() {
- int startIndex = 0;
- int endIndex = pattern.length();
- if (pattern.startsWith("!")) { //$NON-NLS-1$
- startIndex++;
- negation = true;
- }
-
- if (pattern.endsWith("/")) { //$NON-NLS-1$
- endIndex --;
- dirOnly = true;
- }
-
- pattern = pattern.substring(startIndex, endIndex);
- boolean hasSlash = pattern.contains("/"); //$NON-NLS-1$
-
- if (!hasSlash)
- nameOnly = true;
- else if (!pattern.startsWith("/")) { //$NON-NLS-1$
- //Contains "/" but does not start with one
- //Adding / to the start should not interfere with matching
- pattern = "/" + pattern; //$NON-NLS-1$
- }
-
- if (pattern.contains("*") || pattern.contains("?") || pattern.contains("[")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
- try {
- matcher = new FileNameMatcher(pattern, Character.valueOf('/'));
- } catch (InvalidPatternException e) {
- // Ignore pattern exceptions
- }
- }
- }
-
-
- /**
- * @return
- * True if the pattern is just a file name and not a path
- */
- public boolean getNameOnly() {
- return nameOnly;
- }
-
- /**
- *
- * @return
- * True if the pattern should match directories only
- */
- public boolean dirOnly() {
- return dirOnly;
- }
-
- /**
- *
- * @return
- * True if the pattern had a "!" in front of it
- */
- public boolean getNegation() {
- return negation;
- }
-
- /**
- * @return
- * The blob pattern to be used as a matcher
- */
- public String getPattern() {
- return pattern;
- }
-
- /**
- * Returns true if a match was made.
- * <br>
- * This function does NOT return the actual ignore status of the
- * target! Please consult {@link #getResult()} for the ignore status. The actual
- * ignore status may be true or false depending on whether this rule is
- * an ignore rule or a negation rule.
- *
- * @param target
- * Name pattern of the file, relative to the base directory of this rule
- * @param isDirectory
- * Whether the target file is a directory or not
- * @return
- * True if a match was made. This does not necessarily mean that
- * the target is ignored. Call {@link IgnoreRule#getResult() getResult()} for the result.
- */
- public boolean isMatch(String target, boolean isDirectory) {
- if (!target.startsWith("/")) //$NON-NLS-1$
- target = "/" + target; //$NON-NLS-1$
-
- if (matcher == null) {
- if (target.equals(pattern)) {
- //Exact match
- if (dirOnly && !isDirectory)
- //Directory expectations not met
- return false;
- else
- //Directory expectations met
- return true;
- }
-
- /*
- * Add slashes for startsWith check. This avoids matching e.g.
- * "/src/new" to /src/newfile" but allows "/src/new" to match
- * "/src/new/newfile", as is the git standard
- */
- if ((target).startsWith(pattern + "/")) //$NON-NLS-1$
- return true;
-
- if (nameOnly) {
- //Iterate through each sub-name
- final String[] segments = target.split("/"); //$NON-NLS-1$
- for (int idx = 0; idx < segments.length; idx++) {
- final String segmentName = segments[idx];
- // String.split("/") creates empty segment for leading slash
- if (segmentName.length() == 0)
- continue;
- if (segmentName.equals(pattern) &&
- doesMatchDirectoryExpectations(isDirectory, idx, segments.length))
- return true;
- }
- }
-
- } else {
- matcher.reset();
- matcher.append(target);
- if (matcher.isMatch())
- return true;
-
- final String[] segments = target.split("/"); //$NON-NLS-1$
- if (nameOnly) {
- for (int idx = 0; idx < segments.length; idx++) {
- final String segmentName = segments[idx];
- // String.split("/") creates empty segment for leading slash
- if (segmentName.length() == 0)
- continue;
- //Iterate through each sub-directory
- matcher.reset();
- matcher.append(segmentName);
- if (matcher.isMatch() &&
- doesMatchDirectoryExpectations(isDirectory, idx, segments.length))
- return true;
- }
- } else {
- //TODO: This is the slowest operation
- //This matches e.g. "/src/ne?" to "/src/new/file.c"
- matcher.reset();
-
- for (int idx = 0; idx < segments.length; idx++) {
- final String segmentName = segments[idx];
- // String.split("/") creates empty segment for leading slash
- if (segmentName.length() == 0)
- continue;
-
- matcher.append("/" + segmentName); //$NON-NLS-1$
-
- if (matcher.isMatch()
- && doesMatchDirectoryExpectations(isDirectory, idx,
- segments.length))
- return true;
- }
- }
- }
-
- return false;
- }
-
- /**
- * If a call to <code>isMatch(String, boolean)</code> was previously
- * made, this will return whether or not the target was ignored. Otherwise
- * this just indicates whether the rule is non-negation or negation.
- *
- * @return
- * True if the target is to be ignored, false otherwise.
- */
- public boolean getResult() {
- return !negation;
- }
-
- private boolean doesMatchDirectoryExpectations(boolean isDirectory, int segmentIdx, int segmentLength) {
- // The segment we are checking is a directory, expectations are met.
- if (segmentIdx < segmentLength - 1) {
- return true;
- }
-
- // We are checking the last part of the segment for which isDirectory has to be considered.
- return !dirOnly || isDirectory;
- }
-
- @Override
- public String toString() {
- return pattern;
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/LeadingAsteriskMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/LeadingAsteriskMatcher.java
index f1153d9..3d0ad09 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/LeadingAsteriskMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/LeadingAsteriskMatcher.java
@@ -50,7 +50,7 @@ package org.eclipse.jgit.ignore.internal;
public class LeadingAsteriskMatcher extends NameMatcher {
LeadingAsteriskMatcher(String pattern, Character pathSeparator, boolean dirOnly) {
- super(pattern, pathSeparator, dirOnly);
+ super(pattern, pathSeparator, dirOnly, true);
if (subPattern.charAt(0) != '*')
throw new IllegalArgumentException(
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/NameMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/NameMatcher.java
index 6c4c809..8beae83 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/NameMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/NameMatcher.java
@@ -58,9 +58,13 @@ public class NameMatcher extends AbstractMatcher {
final String subPattern;
- NameMatcher(String pattern, Character pathSeparator, boolean dirOnly) {
+ NameMatcher(String pattern, Character pathSeparator, boolean dirOnly,
+ boolean deleteBackslash) {
super(pattern, dirOnly);
slash = getPathSeparator(pathSeparator);
+ if (deleteBackslash) {
+ pattern = Strings.deleteBackslash(pattern);
+ }
beginning = pattern.length() == 0 ? false : pattern.charAt(0) == slash;
if (!beginning)
this.subPattern = pattern;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java
index d3e5f6a..c3f6694 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java
@@ -85,7 +85,8 @@ public class PathMatcher extends AbstractMatcher {
}
private boolean isSimplePathWithSegments(String path) {
- return !isWildCard(path) && count(path, slash, true) > 0;
+ return !isWildCard(path) && path.indexOf('\\') < 0
+ && count(path, slash, true) > 0;
}
static private List<IMatcher> createMatchers(List<String> segments,
@@ -118,7 +119,7 @@ public class PathMatcher extends AbstractMatcher {
public static IMatcher createPathMatcher(String pattern,
Character pathSeparator, boolean dirOnly)
throws InvalidPatternException {
- pattern = pattern.trim();
+ pattern = trim(pattern);
char slash = Strings.getPathSeparator(pathSeparator);
// ignore possible leading and trailing slash
int slashIdx = pattern.indexOf(slash, 1);
@@ -127,6 +128,29 @@ public class PathMatcher extends AbstractMatcher {
return createNameMatcher0(pattern, pathSeparator, dirOnly);
}
+ /**
+ * Trim trailing spaces, unless they are escaped with backslash, see
+ * https://www.kernel.org/pub/software/scm/git/docs/gitignore.html
+ *
+ * @param pattern
+ * non null
+ * @return trimmed pattern
+ */
+ private static String trim(String pattern) {
+ while (pattern.length() > 0
+ && pattern.charAt(pattern.length() - 1) == ' ') {
+ if (pattern.length() > 1
+ && pattern.charAt(pattern.length() - 2) == '\\') {
+ // last space was escaped by backslash: remove backslash and
+ // keep space
+ pattern = pattern.substring(0, pattern.length() - 2) + " "; //$NON-NLS-1$
+ return pattern;
+ }
+ pattern = pattern.substring(0, pattern.length() - 1);
+ }
+ return pattern;
+ }
+
private static IMatcher createNameMatcher0(String segment,
Character pathSeparator, boolean dirOnly)
throws InvalidPatternException {
@@ -144,7 +168,7 @@ public class PathMatcher extends AbstractMatcher {
case COMPLEX:
return new WildCardMatcher(segment, pathSeparator, dirOnly);
default:
- return new NameMatcher(segment, pathSeparator, dirOnly);
+ return new NameMatcher(segment, pathSeparator, dirOnly, true);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java
index cd4d753..e354c71 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java
@@ -44,13 +44,16 @@ package org.eclipse.jgit.ignore.internal;
import static java.lang.Character.isLetter;
+import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
import org.eclipse.jgit.errors.InvalidPatternException;
import org.eclipse.jgit.ignore.FastIgnoreRule;
+import org.eclipse.jgit.internal.JGitText;
/**
* Various {@link String} related utility methods, written mostly to avoid
@@ -138,16 +141,34 @@ public class Strings {
private static boolean isComplexWildcard(String pattern) {
int idx1 = pattern.indexOf('[');
if (idx1 != -1) {
- int idx2 = pattern.indexOf(']');
- if (idx2 > idx1)
- return true;
+ return true;
}
- // required to match escaped backslashes '\\\\'
- if (pattern.indexOf('?') != -1 || pattern.indexOf('\\') != -1)
+ if (pattern.indexOf('?') != -1) {
return true;
+ } else {
+ // check if the backslash escapes one of the glob special characters
+ // if not, backslash is not part of a regex and treated literally
+ int backSlash = pattern.indexOf('\\');
+ if (backSlash >= 0) {
+ int nextIdx = backSlash + 1;
+ if (pattern.length() == nextIdx) {
+ return false;
+ }
+ char nextChar = pattern.charAt(nextIdx);
+ if (escapedByBackslash(nextChar)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
return false;
}
+ private static boolean escapedByBackslash(char nextChar) {
+ return nextChar == '?' || nextChar == '*' || nextChar == '[';
+ }
+
static PatternState checkWildCards(String pattern) {
if (isComplexWildcard(pattern))
return PatternState.COMPLEX;
@@ -226,7 +247,7 @@ public class Strings {
char[] charClass = new char[6];
for (int i = 0; i < pattern.length(); i++) {
- char c = pattern.charAt(i);
+ final char c = pattern.charAt(i);
switch (c) {
case '*':
@@ -236,6 +257,20 @@ public class Strings {
sb.append('.').append(c);
break;
+ case '(': // fall-through
+ case ')': // fall-through
+ case '{': // fall-through
+ case '}': // fall-through
+ case '+': // fall-through
+ case '$': // fall-through
+ case '^': // fall-through
+ case '|':
+ if (seenEscape || in_brackets > 0)
+ sb.append(c);
+ else
+ sb.append('\\').append(c);
+ break;
+
case '.':
if (seenEscape)
sb.append(c);
@@ -273,6 +308,14 @@ public class Strings {
char lookAhead = lookAhead(pattern, i);
if (lookAhead == ']' || lookAhead == '[')
ignoreLastBracket = true;
+ } else {
+ //
+ char lookAhead = lookAhead(pattern, i);
+ if (lookAhead != '\\' && lookAhead != '['
+ && lookAhead != '?' && lookAhead != '*'
+ && lookAhead != ' ' && lookBehind(sb) != '\\') {
+ break;
+ }
}
sb.append(c);
break;
@@ -349,7 +392,16 @@ public class Strings {
if (in_brackets > 0)
throw new InvalidPatternException("Not closed bracket?", pattern); //$NON-NLS-1$
- return Pattern.compile(sb.toString());
+ try {
+ return Pattern.compile(sb.toString());
+ } catch (PatternSyntaxException e) {
+ InvalidPatternException patternException = new InvalidPatternException(
+ MessageFormat.format(JGitText.get().invalidIgnoreRule,
+ pattern),
+ pattern);
+ patternException.initCause(e);
+ throw patternException;
+ }
}
/**
@@ -401,4 +453,30 @@ public class Strings {
return null;
}
+ static String deleteBackslash(String s) {
+ if (s.indexOf('\\') < 0) {
+ return s;
+ }
+ StringBuilder sb = new StringBuilder(s.length());
+ for (int i = 0; i < s.length(); i++) {
+ char ch = s.charAt(i);
+ if (ch == '\\') {
+ if (i + 1 == s.length()) {
+ continue;
+ }
+ char next = s.charAt(i + 1);
+ if (next == '\\') {
+ sb.append(ch);
+ i++;
+ continue;
+ }
+ if (!escapedByBackslash(next)) {
+ continue;
+ }
+ }
+ sb.append(ch);
+ }
+ return sb.toString();
+ }
+
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/TrailingAsteriskMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/TrailingAsteriskMatcher.java
index 4a1c780..b927d27 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/TrailingAsteriskMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/TrailingAsteriskMatcher.java
@@ -50,7 +50,7 @@ package org.eclipse.jgit.ignore.internal;
public class TrailingAsteriskMatcher extends NameMatcher {
TrailingAsteriskMatcher(String pattern, Character pathSeparator, boolean dirOnly) {
- super(pattern, pathSeparator, dirOnly);
+ super(pattern, pathSeparator, dirOnly, true);
if (subPattern.charAt(subPattern.length() - 1) != '*')
throw new IllegalArgumentException(
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildCardMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildCardMatcher.java
index 7d12b0d..8f98152 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildCardMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildCardMatcher.java
@@ -62,7 +62,7 @@ public class WildCardMatcher extends NameMatcher {
WildCardMatcher(String pattern, Character pathSeparator, boolean dirOnly)
throws InvalidPatternException {
- super(pattern, pathSeparator, dirOnly);
+ super(pattern, pathSeparator, dirOnly, false);
p = convertGlob(subPattern);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
index 0300af1..7740a2b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -79,6 +79,7 @@ public class JGitText extends TranslationBundle {
/***/ public String atLeastOnePathIsRequired;
/***/ public String atLeastOnePatternIsRequired;
/***/ public String atLeastTwoFiltersNeeded;
+ /***/ public String atomicPushNotSupported;
/***/ public String authenticationNotSupported;
/***/ public String badBase64InputCharacterAt;
/***/ public String badEntryDelimiter;
@@ -86,6 +87,7 @@ public class JGitText extends TranslationBundle {
/***/ public String badEscape;
/***/ public String badGroupHeader;
/***/ public String badObjectType;
+ /***/ public String badRef;
/***/ public String badSectionEntry;
/***/ public String bareRepositoryNoWorkdirAndIndex;
/***/ public String base64InputNotProperlyPadded;
@@ -103,6 +105,8 @@ public class JGitText extends TranslationBundle {
/***/ public String cannotBeRecursiveWhenTreesAreIncluded;
/***/ public String cannotChangeActionOnComment;
/***/ public String cannotChangeToComment;
+ /***/ public String cannotCheckoutFromUnbornBranch;
+ /***/ public String cannotCheckoutOursSwitchBranch;
/***/ public String cannotCombineSquashWithNoff;
/***/ public String cannotCombineTreeFilterWithRevFilter;
/***/ public String cannotCommitOnARepoWithState;
@@ -116,16 +120,23 @@ public class JGitText extends TranslationBundle {
/***/ public String cannotCreateTempDir;
/***/ public String cannotDeleteCheckedOutBranch;
/***/ public String cannotDeleteFile;
+ /***/ public String cannotDeleteObjectsPath;
/***/ public String cannotDeleteStaleTrackingRef;
/***/ public String cannotDeleteStaleTrackingRef2;
/***/ public String cannotDetermineProxyFor;
/***/ public String cannotDownload;
+ /***/ public String cannotEnterObjectsPath;
+ /***/ public String cannotEnterPathFromParent;
/***/ public String cannotExecute;
/***/ public String cannotGet;
+ /***/ public String cannotGetObjectsPath;
+ /***/ public String cannotListObjectsPath;
+ /***/ public String cannotListPackPath;
/***/ public String cannotListRefs;
/***/ public String cannotLock;
/***/ public String cannotLockPackIn;
/***/ public String cannotMatchOnEmptyString;
+ /***/ public String cannotMkdirObjectPath;
/***/ public String cannotMoveIndexTo;
/***/ public String cannotMovePackTo;
/***/ public String cannotOpenService;
@@ -137,14 +148,19 @@ public class JGitText extends TranslationBundle {
/***/ public String cannotReadCommit;
/***/ public String cannotReadFile;
/***/ public String cannotReadHEAD;
+ /***/ public String cannotReadIndex;
/***/ public String cannotReadObject;
+ /***/ public String cannotReadObjectsPath;
/***/ public String cannotReadTree;
/***/ public String cannotRebaseWithoutCurrentHead;
/***/ public String cannotResolveLocalTrackingRefForUpdating;
/***/ public String cannotSquashFixupWithoutPreviousCommit;
/***/ public String cannotStoreObjects;
+ /***/ public String cannotResolveUniquelyAbbrevObjectId;
/***/ public String cannotUnloadAModifiedTree;
+ /***/ public String cannotUpdateUnbornBranch;
/***/ public String cannotWorkWithOtherStagesThanZeroRightNow;
+ /***/ public String cannotWriteObjectsPath;
/***/ public String canOnlyCherryPickCommitsWithOneParent;
/***/ public String canOnlyRevertCommitsWithOneParent;
/***/ public String commitDoesNotHaveGivenParent;
@@ -158,39 +174,75 @@ public class JGitText extends TranslationBundle {
/***/ public String classCastNotA;
/***/ public String cloneNonEmptyDirectory;
/***/ public String collisionOn;
+ /***/ public String commandRejectedByHook;
/***/ public String commandWasCalledInTheWrongState;
/***/ public String commitAlreadyExists;
/***/ public String commitMessageNotSpecified;
/***/ public String commitOnRepoWithoutHEADCurrentlyNotSupported;
/***/ public String commitAmendOnInitialNotPossible;
- /***/ public String commitRejectedByHook;
/***/ public String compressingObjects;
/***/ public String connectionFailed;
/***/ public String connectionTimeOut;
/***/ public String contextMustBeNonNegative;
/***/ public String corruptionDetectedReReadingAt;
+ /***/ public String corruptObjectBadDate;
+ /***/ public String corruptObjectBadEmail;
/***/ public String corruptObjectBadStream;
/***/ public String corruptObjectBadStreamCorruptHeader;
+ /***/ public String corruptObjectBadTimezone;
+ /***/ public String corruptObjectDuplicateEntryNames;
/***/ public String corruptObjectGarbageAfterSize;
/***/ public String corruptObjectIncorrectLength;
+ /***/ public String corruptObjectIncorrectSorting;
/***/ public String corruptObjectInvalidEntryMode;
/***/ public String corruptObjectInvalidMode;
+ /***/ public String corruptObjectInvalidModeChar;
+ /***/ public String corruptObjectInvalidModeStartsZero;
/***/ public String corruptObjectInvalidMode2;
/***/ public String corruptObjectInvalidMode3;
+ /***/ public String corruptObjectInvalidName;
+ /***/ public String corruptObjectInvalidNameAux;
+ /***/ public String corruptObjectInvalidNameCon;
+ /***/ public String corruptObjectInvalidNameCom;
+ /***/ public String corruptObjectInvalidNameEnd;
+ /***/ public String corruptObjectInvalidNameIgnorableUnicode;
+ /***/ public String corruptObjectInvalidNameInvalidUtf8;
+ /***/ public String corruptObjectInvalidNameLpt;
+ /***/ public String corruptObjectInvalidNameNul;
+ /***/ public String corruptObjectInvalidNamePrn;
+ /***/ public String corruptObjectInvalidObject;
+ /***/ public String corruptObjectInvalidParent;
+ /***/ public String corruptObjectInvalidTree;
/***/ public String corruptObjectInvalidType;
/***/ public String corruptObjectInvalidType2;
/***/ public String corruptObjectMalformedHeader;
+ /***/ public String corruptObjectMissingEmail;
+ /***/ public String corruptObjectNameContainsByte;
+ /***/ public String corruptObjectNameContainsChar;
+ /***/ public String corruptObjectNameContainsNullByte;
+ /***/ public String corruptObjectNameContainsSlash;
+ /***/ public String corruptObjectNameDot;
+ /***/ public String corruptObjectNameDotDot;
+ /***/ public String corruptObjectNameZeroLength;
/***/ public String corruptObjectNegativeSize;
/***/ public String corruptObjectNoAuthor;
/***/ public String corruptObjectNoCommitter;
/***/ public String corruptObjectNoHeader;
/***/ public String corruptObjectNoObject;
+ /***/ public String corruptObjectNoObjectHeader;
/***/ public String corruptObjectNoTaggerBadHeader;
/***/ public String corruptObjectNoTaggerHeader;
+ /***/ public String corruptObjectNoTagHeader;
/***/ public String corruptObjectNoTagName;
/***/ public String corruptObjectNotree;
+ /***/ public String corruptObjectNotreeHeader;
/***/ public String corruptObjectNoType;
+ /***/ public String corruptObjectNoTypeHeader;
/***/ public String corruptObjectPackfileChecksumIncorrect;
+ /***/ public String corruptObjectTruncatedInMode;
+ /***/ public String corruptObjectTruncatedInName;
+ /***/ public String corruptObjectTruncatedInObjectId;
+ /***/ public String corruptObjectZeroId;
/***/ public String corruptPack;
/***/ public String couldNotCheckOutBecauseOfConflicts;
/***/ public String couldNotDeleteLockFileShouldNotHappen;
@@ -203,6 +255,7 @@ public class JGitText extends TranslationBundle {
/***/ public String couldNotRenameDeleteOldIndex;
/***/ public String couldNotRenameTemporaryFile;
/***/ public String couldNotRenameTemporaryIndexFileToIndex;
+ /***/ public String couldNotRewindToUpstreamCommit;
/***/ public String couldNotURLEncodeToUTF8;
/***/ public String couldNotWriteFile;
/***/ public String countingObjects;
@@ -238,7 +291,9 @@ public class JGitText extends TranslationBundle {
/***/ public String eitherGitDirOrWorkTreeRequired;
/***/ public String emptyCommit;
/***/ public String emptyPathNotPermitted;
+ /***/ public String emptyRef;
/***/ public String encryptionError;
+ /***/ public String encryptionOnlyPBE;
/***/ public String endOfFileInEscape;
/***/ public String entryNotFoundByPath;
/***/ public String enumValueNotSupported2;
@@ -252,7 +307,6 @@ public class JGitText extends TranslationBundle {
/***/ public String errorListing;
/***/ public String errorOccurredDuringUnpackingOnTheRemoteEnd;
/***/ public String errorReadingInfoRefs;
- /***/ public String errorSymlinksNotSupported;
/***/ public String exceptionCaughtDuringExecutionOfHook;
/***/ public String exceptionCaughtDuringExecutionOfAddCommand;
/***/ public String exceptionCaughtDuringExecutionOfArchiveCommand;
@@ -267,6 +321,7 @@ public class JGitText extends TranslationBundle {
/***/ public String exceptionCaughtDuringExecutionOfRevertCommand;
/***/ public String exceptionCaughtDuringExecutionOfRmCommand;
/***/ public String exceptionCaughtDuringExecutionOfTagCommand;
+ /***/ public String exceptionCaughtDuringExcecutionOfCommand;
/***/ public String exceptionHookExecutionInterrupted;
/***/ public String exceptionOccurredDuringAddingOfOptionToALogCommand;
/***/ public String exceptionOccurredDuringReadingOfGIT_DIR;
@@ -289,6 +344,9 @@ public class JGitText extends TranslationBundle {
/***/ public String fileIsTooBigForThisConvenienceMethod;
/***/ public String fileIsTooLarge;
/***/ public String fileModeNotSetForPath;
+ /***/ public String filterExecutionFailed;
+ /***/ public String filterExecutionFailedRc;
+ /***/ public String findingGarbage;
/***/ public String flagIsDisposed;
/***/ public String flagNotFromThis;
/***/ public String flagsAlreadyCreated;
@@ -316,6 +374,7 @@ public class JGitText extends TranslationBundle {
/***/ public String initFailedBareRepoDifferentDirs;
/***/ public String initFailedNonBareRepoSameDirs;
/***/ public String inMemoryBufferLimitExceeded;
+ /***/ public String inputDidntMatchLength;
/***/ public String inputStreamMustSupportMark;
/***/ public String integerValueOutOfRange;
/***/ public String internalRevisionError;
@@ -332,7 +391,10 @@ public class JGitText extends TranslationBundle {
/***/ public String invalidGitdirRef;
/***/ public String invalidGitType;
/***/ public String invalidId;
+ /***/ public String invalidId0;
/***/ public String invalidIdLength;
+ /***/ public String invalidIgnoreParamSubmodule;
+ /***/ public String invalidIgnoreRule;
/***/ public String invalidIntegerValue;
/***/ public String invalidKey;
/***/ public String invalidLineInConfigFile;
@@ -349,12 +411,14 @@ public class JGitText extends TranslationBundle {
/***/ public String invalidReflogRevision;
/***/ public String invalidRefName;
/***/ public String invalidRemote;
+ /***/ public String invalidShallowObject;
/***/ public String invalidStageForPath;
/***/ public String invalidTagOption;
/***/ public String invalidTimeout;
/***/ public String invalidURL;
/***/ public String invalidWildcards;
/***/ public String invalidRefSpec;
+ /***/ public String invalidRepositoryStateNoHead;
/***/ public String invalidWindowSize;
/***/ public String isAStaticFlagAndHasNorevWalkInstance;
/***/ public String JRELacksMD5Implementation;
@@ -365,8 +429,10 @@ public class JGitText extends TranslationBundle {
/***/ public String largeObjectOutOfMemory;
/***/ public String lengthExceedsMaximumArraySize;
/***/ public String listingAlternates;
+ /***/ public String listingPacks;
/***/ public String localObjectsIncomplete;
/***/ public String localRefIsMissingObjects;
+ /***/ public String localRepository;
/***/ public String lockCountMustBeGreaterOrEqual1;
/***/ public String lockError;
/***/ public String lockOnNotClosed;
@@ -428,6 +494,7 @@ public class JGitText extends TranslationBundle {
/***/ public String objectAtHasBadZlibStream;
/***/ public String objectAtPathDoesNotHaveId;
/***/ public String objectIsCorrupt;
+ /***/ public String objectIsCorrupt3;
/***/ public String objectIsNotA;
/***/ public String objectNotFound;
/***/ public String objectNotFoundIn;
@@ -443,13 +510,16 @@ public class JGitText extends TranslationBundle {
/***/ public String packChecksumMismatch;
/***/ public String packCorruptedWhileWritingToFilesystem;
/***/ public String packDoesNotMatchIndex;
+ /***/ public String packedRefsHandleIsStale;
/***/ public String packetSizeMustBeAtLeast;
/***/ public String packetSizeMustBeAtMost;
/***/ public String packfileCorruptionDetected;
/***/ public String packFileInvalid;
/***/ public String packfileIsTruncated;
/***/ public String packfileIsTruncatedNoParam;
+ /***/ public String packHandleIsStale;
/***/ public String packHasUnresolvedDeltas;
+ /***/ public String packInaccessible;
/***/ public String packingCancelledDuringObjectsWriting;
/***/ public String packObjectCountMismatch;
/***/ public String packRefs;
@@ -464,6 +534,8 @@ public class JGitText extends TranslationBundle {
/***/ public String pathNotConfigured;
/***/ public String peeledLineBeforeRef;
/***/ public String peerDidNotSupplyACompleteObjectGraph;
+ /***/ public String personIdentEmailNonNull;
+ /***/ public String personIdentNameNonNull;
/***/ public String prefixRemote;
/***/ public String problemWithResolvingPushRefSpecsLocally;
/***/ public String progressMonUploading;
@@ -473,6 +545,10 @@ public class JGitText extends TranslationBundle {
/***/ public String pullOnRepoWithoutHEADCurrentlyNotSupported;
/***/ public String pullTaskName;
/***/ public String pushCancelled;
+ /***/ public String pushCertificateInvalidField;
+ /***/ public String pushCertificateInvalidFieldValue;
+ /***/ public String pushCertificateInvalidHeader;
+ /***/ public String pushCertificateInvalidSignature;
/***/ public String pushIsNotSupportedForBundleTransport;
/***/ public String pushNotPermitted;
/***/ public String rawLogMessageDoesNotParseAsLogEntry;
@@ -507,6 +583,7 @@ public class JGitText extends TranslationBundle {
/***/ public String repositoryIsRequired;
/***/ public String repositoryNotFound;
/***/ public String repositoryState_applyMailbox;
+ /***/ public String repositoryState_bare;
/***/ public String repositoryState_bisecting;
/***/ public String repositoryState_conflicts;
/***/ public String repositoryState_merged;
@@ -520,6 +597,9 @@ public class JGitText extends TranslationBundle {
/***/ public String resolvingDeltas;
/***/ public String resultLengthIncorrect;
/***/ public String rewinding;
+ /***/ public String s3ActionDeletion;
+ /***/ public String s3ActionReading;
+ /***/ public String s3ActionWriting;
/***/ public String searchForReuse;
/***/ public String searchForSizes;
/***/ public String secondsAgo;
@@ -527,7 +607,6 @@ public class JGitText extends TranslationBundle {
/***/ public String sequenceTooLargeForDiffAlgorithm;
/***/ public String serviceNotEnabledNoName;
/***/ public String serviceNotPermitted;
- /***/ public String serviceNotPermittedNoName;
/***/ public String shallowCommitsAlreadyInitialized;
/***/ public String shortCompressedStreamAt;
/***/ public String shortReadOfBlock;
@@ -557,9 +636,13 @@ public class JGitText extends TranslationBundle {
/***/ public String stashFailed;
/***/ public String stashResolveFailed;
/***/ public String statelessRPCRequiresOptionToBeEnabled;
+ /***/ public String storePushCertMultipleRefs;
+ /***/ public String storePushCertOneRef;
+ /***/ public String storePushCertReflog;
/***/ public String submoduleExists;
/***/ public String submodulesNotSupported;
/***/ public String submoduleParentRemoteUrlInvalid;
+ /***/ public String supportOnlyPackIndexVersion2;
/***/ public String symlinkCannotBeWrittenAsTheLinkTarget;
/***/ public String systemConfigFileInvalid;
/***/ public String tagAlreadyExists;
@@ -583,6 +666,8 @@ public class JGitText extends TranslationBundle {
/***/ public String transportProtoLocal;
/***/ public String transportProtoSFTP;
/***/ public String transportProtoSSH;
+ /***/ public String transportProtoTest;
+ /***/ public String transportProvidedRefWithNoObjectId;
/***/ public String transportSSHRetryInterrupt;
/***/ public String treeEntryAlreadyExists;
/***/ public String treeFilterMarkerTooManyFilters;
@@ -593,11 +678,14 @@ public class JGitText extends TranslationBundle {
/***/ public String truncatedHunkOldLinesMissing;
/***/ public String tSizeMustBeGreaterOrEqual1;
/***/ public String unableToCheckConnectivity;
+ /***/ public String unableToCreateNewObject;
/***/ public String unableToStore;
/***/ public String unableToWrite;
+ /***/ public String unauthorized;
/***/ public String unencodeableFile;
/***/ public String unexpectedCompareResult;
/***/ public String unexpectedEndOfConfigFile;
+ /***/ public String unexpectedEndOfInput;
/***/ public String unexpectedHunkTrailer;
/***/ public String unexpectedOddResult;
/***/ public String unexpectedRefReport;
@@ -609,6 +697,7 @@ public class JGitText extends TranslationBundle {
/***/ public String unknownIndexVersionOrCorruptIndex;
/***/ public String unknownObject;
/***/ public String unknownObjectType;
+ /***/ public String unknownObjectType2;
/***/ public String unknownRepositoryFormat;
/***/ public String unknownRepositoryFormat2;
/***/ public String unknownZlibError;
@@ -617,16 +706,21 @@ public class JGitText extends TranslationBundle {
/***/ public String unpackException;
/***/ public String unreadablePackIndex;
/***/ public String unrecognizedRef;
+ /***/ public String unsetMark;
+ /***/ public String unsupportedAlternates;
/***/ public String unsupportedArchiveFormat;
/***/ public String unsupportedCommand0;
/***/ public String unsupportedEncryptionAlgorithm;
/***/ public String unsupportedEncryptionVersion;
/***/ public String unsupportedGC;
+ /***/ public String unsupportedMark;
/***/ public String unsupportedOperationNotAddAtEnd;
/***/ public String unsupportedPackIndexVersion;
/***/ public String unsupportedPackVersion;
+ /***/ public String updatingHeadFailed;
/***/ public String updatingReferences;
/***/ public String updatingRefFailed;
+ /***/ public String upstreamBranchName;
/***/ public String uriNotConfigured;
/***/ public String uriNotFound;
/***/ public String URINotSupported;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DeltaBaseCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DeltaBaseCache.java
index 53c05f0..64a63d7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DeltaBaseCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DeltaBaseCache.java
@@ -43,7 +43,6 @@
package org.eclipse.jgit.internal.storage.dfs;
-import java.lang.ref.SoftReference;
/**
* Caches recently used objects for {@link DfsReader}.
@@ -60,30 +59,28 @@ final class DeltaBaseCache {
}
private int maxByteCount;
+ private int curByteCount;
- private final Slot[] table;
-
- private Slot lruHead;
+ private final Entry[] table;
- private Slot lruTail;
-
- private int curByteCount;
+ private Entry lruHead;
+ private Entry lruTail;
DeltaBaseCache(DfsReader reader) {
- DfsReaderOptions options = reader.getOptions();
- maxByteCount = options.getDeltaBaseCacheLimit();
- table = new Slot[1 << TABLE_BITS];
+ this(reader.getOptions().getDeltaBaseCacheLimit());
+ }
+
+ DeltaBaseCache(int maxBytes) {
+ maxByteCount = maxBytes;
+ table = new Entry[1 << TABLE_BITS];
}
Entry get(DfsPackKey key, long position) {
- Slot e = table[hash(position)];
+ Entry e = table[hash(position)];
for (; e != null; e = e.tableNext) {
if (e.offset == position && key.equals(e.pack)) {
- Entry buf = e.data.get();
- if (buf != null) {
- moveToHead(e);
- return buf;
- }
+ moveToHead(e);
+ return e;
}
}
return null;
@@ -97,33 +94,24 @@ final class DeltaBaseCache {
releaseMemory();
int tableIdx = hash(offset);
- Slot e = new Slot(key, offset, data.length);
- e.data = new SoftReference<Entry>(new Entry(data, objectType));
+ Entry e = new Entry(key, offset, objectType, data);
e.tableNext = table[tableIdx];
table[tableIdx] = e;
- moveToHead(e);
+ lruPushHead(e);
}
private void releaseMemory() {
while (curByteCount > maxByteCount && lruTail != null) {
- Slot currOldest = lruTail;
- Slot nextOldest = currOldest.lruPrev;
-
- curByteCount -= currOldest.size;
- unlink(currOldest);
- removeFromTable(currOldest);
-
- if (nextOldest == null)
- lruHead = null;
- else
- nextOldest.lruNext = null;
- lruTail = nextOldest;
+ Entry e = lruTail;
+ curByteCount -= e.data.length;
+ lruRemove(e);
+ removeFromTable(e);
}
}
- private void removeFromTable(Slot e) {
+ private void removeFromTable(Entry e) {
int tableIdx = hash(e.offset);
- Slot p = table[tableIdx];
+ Entry p = table[tableIdx];
if (p == e) {
table[tableIdx] = e.tableNext;
@@ -136,59 +124,85 @@ final class DeltaBaseCache {
return;
}
}
- }
- private void moveToHead(final Slot e) {
- unlink(e);
- e.lruPrev = null;
- e.lruNext = lruHead;
- if (lruHead != null)
- lruHead.lruPrev = e;
- else
- lruTail = e;
- lruHead = e;
+ throw new IllegalStateException(String.format(
+ "entry for %s:%d not in table", //$NON-NLS-1$
+ e.pack, Long.valueOf(e.offset)));
}
- private void unlink(final Slot e) {
- Slot prev = e.lruPrev;
- Slot next = e.lruNext;
-
- if (prev != null)
- prev.lruNext = next;
- if (next != null)
- next.lruPrev = prev;
+ private void moveToHead(Entry e) {
+ if (e != lruHead) {
+ lruRemove(e);
+ lruPushHead(e);
+ }
}
- static class Entry {
- final byte[] data;
+ private void lruRemove(Entry e) {
+ Entry p = e.lruPrev;
+ Entry n = e.lruNext;
- final int type;
+ if (p != null) {
+ p.lruNext = n;
+ } else {
+ lruHead = n;
+ }
- Entry(final byte[] aData, final int aType) {
- data = aData;
- type = aType;
+ if (n != null) {
+ n.lruPrev = p;
+ } else {
+ lruTail = p;
}
}
- private static class Slot {
- final DfsPackKey pack;
+ private void lruPushHead(Entry e) {
+ Entry n = lruHead;
+ e.lruNext = n;
+ if (n != null)
+ n.lruPrev = e;
+ else
+ lruTail = e;
- final long offset;
+ e.lruPrev = null;
+ lruHead = e;
+ }
- final int size;
+ int getMemoryUsed() {
+ return curByteCount;
+ }
- Slot tableNext;
+ int getMemoryUsedByLruChainForTest() {
+ int r = 0;
+ for (Entry e = lruHead; e != null; e = e.lruNext) {
+ r += e.data.length;
+ }
+ return r;
+ }
- Slot lruPrev;
+ int getMemoryUsedByTableForTest() {
+ int r = 0;
+ for (int i = 0; i < table.length; i++) {
+ for (Entry e = table[i]; e != null; e = e.tableNext) {
+ r += e.data.length;
+ }
+ }
+ return r;
+ }
- Slot lruNext;
+ static class Entry {
+ final DfsPackKey pack;
+ final long offset;
+ final int type;
+ final byte[] data;
- SoftReference<Entry> data;
+ Entry tableNext;
+ Entry lruPrev;
+ Entry lruNext;
- Slot(DfsPackKey key, long offset, int size) {
+ Entry(DfsPackKey key, long offset, int type, byte[] data) {
this.pack = key;
this.offset = offset;
- this.size = size;
+ this.type = type;
+ this.data = data;
}
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlock.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlock.java
index c185332..7926536 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlock.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlock.java
@@ -46,7 +46,6 @@
package org.eclipse.jgit.internal.storage.dfs;
import java.io.IOException;
-import java.security.MessageDigest;
import java.util.zip.CRC32;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
@@ -101,12 +100,9 @@ final class DfsBlock {
out.update(block, ptr, cnt);
}
- void write(PackOutputStream out, long pos, int cnt, MessageDigest digest)
+ void write(PackOutputStream out, long pos, int cnt)
throws IOException {
- int ptr = (int) (pos - start);
- out.write(block, ptr, cnt);
- if (digest != null)
- digest.update(block, ptr, cnt);
+ out.write(block, (int) (pos - start), cnt);
}
void check(Inflater inf, byte[] tmp, long pos, int cnt)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
index 748a4a3..2e170a5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
@@ -135,6 +135,9 @@ public final class DfsBlockCache {
/** Maximum number of bytes the cache should hold. */
private final long maxBytes;
+ /** Pack files smaller than this size can be copied through the cache. */
+ private final long maxStreamThroughCache;
+
/**
* Suggested block size to read from pack files in.
* <p>
@@ -191,6 +194,7 @@ public final class DfsBlockCache {
eb = tableSize;
maxBytes = cfg.getBlockLimit();
+ maxStreamThroughCache = (long) (maxBytes * cfg.getStreamRatio());
blockSize = cfg.getBlockSize();
blockSizeShift = Integer.numberOfTrailingZeros(blockSize);
@@ -206,6 +210,10 @@ public final class DfsBlockCache {
statMiss = new AtomicLong();
}
+ boolean shouldCopyThroughCache(long length) {
+ return length <= maxStreamThroughCache;
+ }
+
/** @return total number of bytes in the cache. */
public long getCurrentSize() {
return liveBytes;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
index ca1451a..a7d13de 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCacheConfig.java
@@ -47,7 +47,11 @@ import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_CORE_SECTION;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DFS_SECTION;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BLOCK_LIMIT;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BLOCK_SIZE;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_STREAM_RATIO;
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Config;
/** Configuration parameters for {@link DfsBlockCache}. */
@@ -59,13 +63,14 @@ public class DfsBlockCacheConfig {
public static final int MB = 1024 * KB;
private long blockLimit;
-
private int blockSize;
+ private double streamRatio;
/** Create a default configuration. */
public DfsBlockCacheConfig() {
setBlockLimit(32 * MB);
setBlockSize(64 * KB);
+ setStreamRatio(0.30);
}
/**
@@ -106,6 +111,27 @@ public class DfsBlockCacheConfig {
}
/**
+ * @return highest percentage of {@link #getBlockLimit()} a single pack can
+ * occupy while being copied by the pack reuse strategy. <b>Default
+ * is 0.30, or 30%</b>.
+ * @since 4.0
+ */
+ public double getStreamRatio() {
+ return streamRatio;
+ }
+
+ /**
+ * @param ratio
+ * percentage of cache to occupy with a copied pack.
+ * @return {@code this}
+ * @since 4.0
+ */
+ public DfsBlockCacheConfig setStreamRatio(double ratio) {
+ streamRatio = Math.max(0, Math.min(ratio, 1.0));
+ return this;
+ }
+
+ /**
* Update properties by setting fields from the configuration.
* <p>
* If a property is not defined in the configuration, then it is left
@@ -127,6 +153,22 @@ public class DfsBlockCacheConfig {
CONFIG_DFS_SECTION,
CONFIG_KEY_BLOCK_SIZE,
getBlockSize()));
+
+ String v = rc.getString(
+ CONFIG_CORE_SECTION,
+ CONFIG_DFS_SECTION,
+ CONFIG_KEY_STREAM_RATIO);
+ if (v != null) {
+ try {
+ setStreamRatio(Double.parseDouble(v));
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().enumValueNotSupported3,
+ CONFIG_CORE_SECTION,
+ CONFIG_DFS_SECTION,
+ CONFIG_KEY_STREAM_RATIO, v));
+ }
+ }
return this;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsCachedPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsCachedPack.java
index 3da5184..a5308f6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsCachedPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsCachedPack.java
@@ -78,8 +78,7 @@ public class DfsCachedPack extends CachedPack {
return ((DfsObjectRepresentation) rep).pack == pack;
}
- void copyAsIs(PackOutputStream out, boolean validate, DfsReader ctx)
- throws IOException {
- pack.copyPackAsIs(out, validate, ctx);
+ void copyAsIs(PackOutputStream out, DfsReader ctx) throws IOException {
+ pack.copyPackAsIs(out, ctx);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
index deb6b7f..784507d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
@@ -44,48 +44,49 @@
package org.eclipse.jgit.internal.storage.dfs;
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC;
+import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC_TXN;
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.UNREACHABLE_GARBAGE;
import static org.eclipse.jgit.internal.storage.pack.PackExt.BITMAP_INDEX;
import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
-import static org.eclipse.jgit.lib.RefDatabase.ALL;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
+import java.util.Collection;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Set;
+import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
import org.eclipse.jgit.internal.storage.file.PackIndex;
import org.eclipse.jgit.internal.storage.pack.PackExt;
import org.eclipse.jgit.internal.storage.pack.PackWriter;
+import org.eclipse.jgit.internal.storage.reftree.RefTreeNames;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectIdOwnerMap;
+import org.eclipse.jgit.lib.ObjectIdSet;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.pack.PackConfig;
+import org.eclipse.jgit.storage.pack.PackStatistics;
import org.eclipse.jgit.util.io.CountingOutputStream;
/** Repack and garbage collect a repository. */
public class DfsGarbageCollector {
private final DfsRepository repo;
-
- private final DfsRefDatabase refdb;
-
+ private final RefDatabase refdb;
private final DfsObjDatabase objdb;
private final List<DfsPackDescription> newPackDesc;
- private final List<PackWriter.Statistics> newPackStats;
+ private final List<PackStatistics> newPackStats;
- private final List<PackWriter.ObjectIdSet> newPackObj;
+ private final List<ObjectIdSet> newPackObj;
private DfsReader ctx;
@@ -93,14 +94,11 @@ public class DfsGarbageCollector {
private long coalesceGarbageLimit = 50 << 20;
- private Map<String, Ref> refsBefore;
-
private List<DfsPackFile> packsBefore;
private Set<ObjectId> allHeads;
-
private Set<ObjectId> nonHeads;
-
+ private Set<ObjectId> txnHeads;
private Set<ObjectId> tagTargets;
/**
@@ -114,8 +112,8 @@ public class DfsGarbageCollector {
refdb = repo.getRefDatabase();
objdb = repo.getObjectDatabase();
newPackDesc = new ArrayList<DfsPackDescription>(4);
- newPackStats = new ArrayList<PackWriter.Statistics>(4);
- newPackObj = new ArrayList<PackWriter.ObjectIdSet>(4);
+ newPackStats = new ArrayList<PackStatistics>(4);
+ newPackObj = new ArrayList<ObjectIdSet>(4);
packConfig = new PackConfig(repo);
packConfig.setIndexVersion(2);
@@ -188,26 +186,30 @@ public class DfsGarbageCollector {
if (pm == null)
pm = NullProgressMonitor.INSTANCE;
if (packConfig.getIndexVersion() != 2)
- throw new IllegalStateException("Only index version 2");
+ throw new IllegalStateException(
+ JGitText.get().supportOnlyPackIndexVersion2);
ctx = (DfsReader) objdb.newReader();
try {
- refdb.clearCache();
+ refdb.refresh();
objdb.clearCache();
- refsBefore = refdb.getRefs(ALL);
+ Collection<Ref> refsBefore = RefTreeNames.allRefs(refdb);
packsBefore = packsToRebuild();
if (packsBefore.isEmpty())
return true;
allHeads = new HashSet<ObjectId>();
nonHeads = new HashSet<ObjectId>();
+ txnHeads = new HashSet<ObjectId>();
tagTargets = new HashSet<ObjectId>();
- for (Ref ref : refsBefore.values()) {
+ for (Ref ref : refsBefore) {
if (ref.isSymbolic() || ref.getObjectId() == null)
continue;
if (isHead(ref))
allHeads.add(ref.getObjectId());
+ else if (RefTreeNames.isRefTree(refdb, ref.getName()))
+ txnHeads.add(ref.getObjectId());
else
nonHeads.add(ref.getObjectId());
if (ref.getPeeledObjectId() != null)
@@ -219,6 +221,7 @@ public class DfsGarbageCollector {
try {
packHeads(pm);
packRest(pm);
+ packRefTreeGraph(pm);
packGarbage(pm);
objdb.commitPack(newPackDesc, toPrune());
rollback = false;
@@ -228,7 +231,7 @@ public class DfsGarbageCollector {
objdb.rollbackPack(newPackDesc);
}
} finally {
- ctx.release();
+ ctx.close();
}
}
@@ -256,7 +259,7 @@ public class DfsGarbageCollector {
}
/** @return statistics corresponding to the {@link #getNewPacks()}. */
- public List<PackWriter.Statistics> getNewPackStatistics() {
+ public List<PackStatistics> getNewPackStatistics() {
return newPackStats;
}
@@ -272,30 +275,36 @@ public class DfsGarbageCollector {
if (allHeads.isEmpty())
return;
- PackWriter pw = newPackWriter();
- try {
+ try (PackWriter pw = newPackWriter()) {
pw.setTagTargets(tagTargets);
- pw.preparePack(pm, allHeads, Collections.<ObjectId> emptySet());
+ pw.preparePack(pm, allHeads, PackWriter.NONE);
if (0 < pw.getObjectCount())
writePack(GC, pw, pm);
- } finally {
- pw.release();
}
}
-
private void packRest(ProgressMonitor pm) throws IOException {
if (nonHeads.isEmpty())
return;
- PackWriter pw = newPackWriter();
- try {
- for (PackWriter.ObjectIdSet packedObjs : newPackObj)
+ try (PackWriter pw = newPackWriter()) {
+ for (ObjectIdSet packedObjs : newPackObj)
pw.excludeObjects(packedObjs);
pw.preparePack(pm, nonHeads, allHeads);
if (0 < pw.getObjectCount())
writePack(GC, pw, pm);
- } finally {
- pw.release();
+ }
+ }
+
+ private void packRefTreeGraph(ProgressMonitor pm) throws IOException {
+ if (txnHeads.isEmpty())
+ return;
+
+ try (PackWriter pw = newPackWriter()) {
+ for (ObjectIdSet packedObjs : newPackObj)
+ pw.excludeObjects(packedObjs);
+ pw.preparePack(pm, txnHeads, PackWriter.NONE);
+ if (0 < pw.getObjectCount())
+ writePack(GC_TXN, pw, pm);
}
}
@@ -307,12 +316,11 @@ public class DfsGarbageCollector {
cfg.setDeltaCompress(false);
cfg.setBuildBitmaps(false);
- PackWriter pw = new PackWriter(cfg, ctx);
- pw.setDeltaBaseAsOffset(true);
- pw.setReuseDeltaCommits(true);
- try {
- RevWalk pool = new RevWalk(ctx);
- pm.beginTask("Finding garbage", objectsBefore());
+ try (PackWriter pw = new PackWriter(cfg, ctx);
+ RevWalk pool = new RevWalk(ctx)) {
+ pw.setDeltaBaseAsOffset(true);
+ pw.setReuseDeltaCommits(true);
+ pm.beginTask(JGitText.get().findingGarbage, objectsBefore());
for (DfsPackFile oldPack : packsBefore) {
PackIndex oldIdx = oldPack.getPackIndex(ctx);
for (PackIndex.MutableEntry ent : oldIdx) {
@@ -328,13 +336,11 @@ public class DfsGarbageCollector {
pm.endTask();
if (0 < pw.getObjectCount())
writePack(UNREACHABLE_GARBAGE, pw, pm);
- } finally {
- pw.release();
}
}
private boolean anyPackHas(AnyObjectId id) {
- for (PackWriter.ObjectIdSet packedObjs : newPackObj)
+ for (ObjectIdSet packedObjs : newPackObj)
if (packedObjs.contains(id))
return true;
return false;
@@ -395,17 +401,10 @@ public class DfsGarbageCollector {
}
}
- final ObjectIdOwnerMap<ObjectIdOwnerMap.Entry> packedObjs = pw
- .getObjectSet();
- newPackObj.add(new PackWriter.ObjectIdSet() {
- public boolean contains(AnyObjectId objectId) {
- return packedObjs.contains(objectId);
- }
- });
-
- PackWriter.Statistics stats = pw.getStatistics();
+ PackStatistics stats = pw.getStatistics();
pack.setPackStats(stats);
newPackStats.add(stats);
+ newPackObj.add(pw.getObjectSet());
DfsBlockCache.getInstance().getOrCreate(pack, null);
return pack;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java
index 5cab473..e5ae980 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java
@@ -95,16 +95,16 @@ public class DfsInserter extends ObjectInserter {
/** Always produce version 2 indexes, to get CRC data. */
private static final int INDEX_VERSION = 2;
- private final DfsObjDatabase db;
- private int compression = Deflater.BEST_COMPRESSION;
+ final DfsObjDatabase db;
+ int compression = Deflater.BEST_COMPRESSION;
- private List<PackedObjectInfo> objectList;
- private ObjectIdOwnerMap<PackedObjectInfo> objectMap;
+ List<PackedObjectInfo> objectList;
+ ObjectIdOwnerMap<PackedObjectInfo> objectMap;
- private DfsBlockCache cache;
- private DfsPackKey packKey;
- private DfsPackDescription packDsc;
- private PackStream packOut;
+ DfsBlockCache cache;
+ DfsPackKey packKey;
+ DfsPackDescription packDsc;
+ PackStream packOut;
private boolean rollback;
/**
@@ -137,7 +137,8 @@ public class DfsInserter extends ObjectInserter {
ObjectId id = idFor(type, data, off, len);
if (objectMap != null && objectMap.contains(id))
return id;
- if (db.has(id))
+ // Ignore unreachable (garbage) objects here.
+ if (db.has(id, true))
return id;
long offset = beginObject(type, len);
@@ -216,7 +217,7 @@ public class DfsInserter extends ObjectInserter {
}
@Override
- public void release() {
+ public void close() {
if (packOut != null) {
try {
packOut.close();
@@ -322,7 +323,7 @@ public class DfsInserter extends ObjectInserter {
private class PackStream extends OutputStream {
private final DfsOutputStream out;
private final MessageDigest md;
- private final byte[] hdrBuf;
+ final byte[] hdrBuf;
private final Deflater deflater;
private final int blockSize;
@@ -600,8 +601,8 @@ public class DfsInserter extends ObjectInserter {
}
@Override
- public void release() {
- ctx.release();
+ public void close() {
+ ctx.close();
}
}
@@ -631,7 +632,7 @@ public class DfsInserter extends ObjectInserter {
// The newly created pack is registered in the cache.
return ctx.open(id, type).openStream();
} finally {
- ctx.release();
+ ctx.close();
}
}
@@ -642,7 +643,7 @@ public class DfsInserter extends ObjectInserter {
new ReadBackStream(pos), inf, bufsz), bufsz)) {
@Override
public void close() throws IOException {
- ctx.release();
+ ctx.close();
super.close();
}
};
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
index b92f784..3641560 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
@@ -54,6 +54,7 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.ObjectDatabase;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectReader;
@@ -90,6 +91,13 @@ public abstract class DfsObjDatabase extends ObjectDatabase {
GC(1),
/**
+ * RefTreeGraph pack was created by Git garbage collection.
+ *
+ * @see DfsGarbageCollector
+ */
+ GC_TXN(1),
+
+ /**
* The pack was created by compacting multiple packs together.
* <p>
* Packs created by compacting multiple packs together aren't nearly as
@@ -181,6 +189,28 @@ public abstract class DfsObjDatabase extends ObjectDatabase {
}
/**
+ * Does the requested object exist in this database?
+ * <p>
+ * This differs from ObjectDatabase's implementation in that we can selectively
+ * ignore unreachable (garbage) objects.
+ *
+ * @param objectId
+ * identity of the object to test for existence of.
+ * @param avoidUnreachableObjects
+ * if true, ignore objects that are unreachable.
+ * @return true if the specified object is stored in this database.
+ * @throws IOException
+ * the object store cannot be accessed.
+ */
+ public boolean has(AnyObjectId objectId, boolean avoidUnreachableObjects)
+ throws IOException {
+ try (ObjectReader or = newReader()) {
+ or.setAvoidUnreachableObjects(avoidUnreachableObjects);
+ return or.has(objectId);
+ }
+ }
+
+ /**
* Generate a new unique name for a pack file.
*
* @param source
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
index 8372884..11aef7f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
@@ -62,11 +62,13 @@ import org.eclipse.jgit.internal.storage.pack.PackWriter;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdSet;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.revwalk.RevFlag;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.pack.PackConfig;
+import org.eclipse.jgit.storage.pack.PackStatistics;
import org.eclipse.jgit.util.BlockList;
import org.eclipse.jgit.util.io.CountingOutputStream;
@@ -90,11 +92,11 @@ public class DfsPackCompactor {
private final List<DfsPackFile> srcPacks;
- private final List<PackWriter.ObjectIdSet> exclude;
+ private final List<ObjectIdSet> exclude;
private final List<DfsPackDescription> newPacks;
- private final List<PackWriter.Statistics> newStats;
+ private final List<PackStatistics> newStats;
private int autoAddSize;
@@ -112,9 +114,9 @@ public class DfsPackCompactor {
repo = repository;
autoAddSize = 5 * 1024 * 1024; // 5 MiB
srcPacks = new ArrayList<DfsPackFile>();
- exclude = new ArrayList<PackWriter.ObjectIdSet>(4);
+ exclude = new ArrayList<ObjectIdSet>(4);
newPacks = new ArrayList<DfsPackDescription>(1);
- newStats = new ArrayList<PackWriter.Statistics>(1);
+ newStats = new ArrayList<PackStatistics>(1);
}
/**
@@ -163,7 +165,7 @@ public class DfsPackCompactor {
* objects to not include.
* @return {@code this}.
*/
- public DfsPackCompactor exclude(PackWriter.ObjectIdSet set) {
+ public DfsPackCompactor exclude(ObjectIdSet set) {
exclude.add(set);
return this;
}
@@ -179,17 +181,10 @@ public class DfsPackCompactor {
*/
public DfsPackCompactor exclude(DfsPackFile pack) throws IOException {
final PackIndex idx;
- DfsReader ctx = (DfsReader) repo.newObjectReader();
- try {
+ try (DfsReader ctx = (DfsReader) repo.newObjectReader()) {
idx = pack.getPackIndex(ctx);
- } finally {
- ctx.release();
}
- return exclude(new PackWriter.ObjectIdSet() {
- public boolean contains(AnyObjectId id) {
- return idx.hasObject(id);
- }
- });
+ return exclude(idx);
}
/**
@@ -206,8 +201,7 @@ public class DfsPackCompactor {
pm = NullProgressMonitor.INSTANCE;
DfsObjDatabase objdb = repo.getObjectDatabase();
- DfsReader ctx = (DfsReader) objdb.newReader();
- try {
+ try (DfsReader ctx = (DfsReader) objdb.newReader()) {
PackConfig pc = new PackConfig(repo);
pc.setIndexVersion(2);
pc.setDeltaCompress(false);
@@ -235,8 +229,8 @@ public class DfsPackCompactor {
writePack(objdb, pack, pw, pm);
writeIndex(objdb, pack, pw);
- PackWriter.Statistics stats = pw.getStatistics();
- pw.release();
+ PackStatistics stats = pw.getStatistics();
+ pw.close();
pw = null;
pack.setPackStats(stats);
@@ -250,11 +244,10 @@ public class DfsPackCompactor {
}
} finally {
if (pw != null)
- pw.release();
+ pw.close();
}
} finally {
rw = null;
- ctx.release();
}
}
@@ -269,7 +262,7 @@ public class DfsPackCompactor {
}
/** @return statistics corresponding to the {@link #getNewPacks()}. */
- public List<PackWriter.Statistics> getNewPackStatistics() {
+ public List<PackStatistics> getNewPackStatistics() {
return newStats;
}
@@ -347,7 +340,7 @@ public class DfsPackCompactor {
RevObject obj = rw.lookupOrNull(id);
if (obj != null && (obj.has(added) || obj.has(isBase)))
continue;
- for (PackWriter.ObjectIdSet e : exclude)
+ for (ObjectIdSet e : exclude)
if (e.contains(id))
continue SCAN;
want.add(new ObjectIdWithOffset(id, ent.getOffset()));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackDescription.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackDescription.java
index fba5157..2b9d0e5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackDescription.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackDescription.java
@@ -50,7 +50,7 @@ import java.util.Map;
import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
import org.eclipse.jgit.internal.storage.pack.PackExt;
-import org.eclipse.jgit.internal.storage.pack.PackWriter;
+import org.eclipse.jgit.storage.pack.PackStatistics;
/**
* Description of a DFS stored pack/index file.
@@ -75,7 +75,7 @@ public class DfsPackDescription implements Comparable<DfsPackDescription> {
private long deltaCount;
- private PackWriter.Statistics stats;
+ private PackStatistics stats;
private int extensions;
@@ -225,11 +225,11 @@ public class DfsPackDescription implements Comparable<DfsPackDescription> {
* DfsGarbageCollector or DfsPackCompactor, and only when the pack
* is being committed to the repository.
*/
- public PackWriter.Statistics getPackStats() {
+ public PackStatistics getPackStats() {
return stats;
}
- DfsPackDescription setPackStats(PackWriter.Statistics stats) {
+ DfsPackDescription setPackStats(PackStatistics stats) {
this.stats = stats;
setFileSize(PACK, stats.getTotalBytes());
setObjectCount(stats.getTotalObjects());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
index d5e1a58..96f1d54 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
@@ -54,6 +54,7 @@ import java.io.BufferedInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.text.MessageFormat;
import java.util.Set;
@@ -80,7 +81,6 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.LongList;
/**
@@ -214,7 +214,17 @@ public final class DfsPackFile {
index = cache.put(key, POS_INDEX, sz, idx);
}
- PackIndex getPackIndex(DfsReader ctx) throws IOException {
+ /**
+ * Get the PackIndex for this PackFile.
+ *
+ * @param ctx
+ * reader context to support reading from the backing store if
+ * the index is not already loaded in memory.
+ * @return the PackIndex.
+ * @throws IOException
+ * the pack index is not available, or is corrupt.
+ */
+ public PackIndex getPackIndex(DfsReader ctx) throws IOException {
return idx(ctx);
}
@@ -454,11 +464,73 @@ public final class DfsPackFile {
return dstbuf;
}
- void copyPackAsIs(PackOutputStream out, boolean validate, DfsReader ctx)
+ void copyPackAsIs(PackOutputStream out, DfsReader ctx)
+ throws IOException {
+ // If the length hasn't been determined yet, pin to set it.
+ if (length == -1) {
+ ctx.pin(this, 0);
+ ctx.unpin();
+ }
+ if (cache.shouldCopyThroughCache(length))
+ copyPackThroughCache(out, ctx);
+ else
+ copyPackBypassCache(out, ctx);
+ }
+
+ private void copyPackThroughCache(PackOutputStream out, DfsReader ctx)
+ throws IOException {
+ long position = 12;
+ long remaining = length - (12 + 20);
+ while (0 < remaining) {
+ DfsBlock b = cache.getOrLoad(this, position, ctx);
+ int ptr = (int) (position - b.start);
+ int n = (int) Math.min(b.size() - ptr, remaining);
+ b.write(out, position, n);
+ position += n;
+ remaining -= n;
+ }
+ }
+
+ private long copyPackBypassCache(PackOutputStream out, DfsReader ctx)
throws IOException {
- // Pin the first window, this ensures the length is accurate.
- ctx.pin(this, 0);
- ctx.copyPackAsIs(this, length, validate, out);
+ try (ReadableChannel rc = ctx.db.openFile(packDesc, PACK)) {
+ ByteBuffer buf = newCopyBuffer(out, rc);
+ if (ctx.getOptions().getStreamPackBufferSize() > 0)
+ rc.setReadAheadBytes(ctx.getOptions().getStreamPackBufferSize());
+ long position = 12;
+ long remaining = length - (12 + 20);
+ while (0 < remaining) {
+ DfsBlock b = cache.get(key, alignToBlock(position));
+ if (b != null) {
+ int ptr = (int) (position - b.start);
+ int n = (int) Math.min(b.size() - ptr, remaining);
+ b.write(out, position, n);
+ position += n;
+ remaining -= n;
+ rc.position(position);
+ continue;
+ }
+
+ buf.position(0);
+ int n = read(rc, buf);
+ if (n <= 0)
+ throw packfileIsTruncated();
+ else if (n > remaining)
+ n = (int) remaining;
+ out.write(buf.array(), 0, n);
+ position += n;
+ remaining -= n;
+ }
+ return position;
+ }
+ }
+
+ private ByteBuffer newCopyBuffer(PackOutputStream out, ReadableChannel rc) {
+ int bs = blockSize(rc);
+ byte[] copyBuf = out.getCopyBuffer();
+ if (bs > copyBuf.length)
+ copyBuf = new byte[bs];
+ return ByteBuffer.wrap(copyBuf, 0, bs);
}
void copyAsIs(PackOutputStream out, DfsObjectToPack src,
@@ -494,22 +566,26 @@ public final class DfsPackFile {
c = buf[headerCnt++] & 0xff;
} while ((c & 128) != 0);
if (validate) {
+ assert(crc1 != null && crc2 != null);
crc1.update(buf, 0, headerCnt);
crc2.update(buf, 0, headerCnt);
}
} else if (typeCode == Constants.OBJ_REF_DELTA) {
if (validate) {
+ assert(crc1 != null && crc2 != null);
crc1.update(buf, 0, headerCnt);
crc2.update(buf, 0, headerCnt);
}
readFully(src.offset + headerCnt, buf, 0, 20, ctx);
if (validate) {
+ assert(crc1 != null && crc2 != null);
crc1.update(buf, 0, 20);
crc2.update(buf, 0, 20);
}
headerCnt += 20;
} else if (validate) {
+ assert(crc1 != null && crc2 != null);
crc1.update(buf, 0, headerCnt);
crc2.update(buf, 0, headerCnt);
}
@@ -526,6 +602,7 @@ public final class DfsPackFile {
quickCopy = ctx.quickCopy(this, dataOffset, dataLength);
if (validate && idx(ctx).hasCRC32Support()) {
+ assert(crc1 != null);
// Index has the CRC32 code cached, validate the object.
//
expectedCRC = idx(ctx).findCRC32(src);
@@ -549,6 +626,7 @@ public final class DfsPackFile {
Long.valueOf(src.offset), getPackName()));
}
} else if (validate) {
+ assert(crc1 != null);
// We don't have a CRC32 code in the index, so compute it
// now while inflating the raw data to get zlib to tell us
// whether or not the data is safe.
@@ -607,7 +685,7 @@ public final class DfsPackFile {
// and we have it pinned. Write this out without copying.
//
out.writeHeader(src, inflatedLength);
- quickCopy.write(out, dataOffset, (int) dataLength, null);
+ quickCopy.write(out, dataOffset, (int) dataLength);
} else if (dataLength <= buf.length) {
// Tiny optimization: Lots of objects are very small deltas or
@@ -636,16 +714,21 @@ public final class DfsPackFile {
while (cnt > 0) {
final int n = (int) Math.min(cnt, buf.length);
readFully(pos, buf, 0, n, ctx);
- if (validate)
+ if (validate) {
+ assert(crc2 != null);
crc2.update(buf, 0, n);
+ }
out.write(buf, 0, n);
pos += n;
cnt -= n;
}
- if (validate && crc2.getValue() != expectedCRC) {
- throw new CorruptObjectException(MessageFormat.format(
- JGitText.get().objectAtHasBadZlibStream,
- Long.valueOf(src.offset), getPackName()));
+ if (validate) {
+ assert(crc2 != null);
+ if (crc2.getValue() != expectedCRC) {
+ throw new CorruptObjectException(MessageFormat.format(
+ JGitText.get().objectAtHasBadZlibStream,
+ Long.valueOf(src.offset), getPackName()));
+ }
}
}
}
@@ -658,6 +741,12 @@ public final class DfsPackFile {
invalid = true;
}
+ private IOException packfileIsTruncated() {
+ invalid = true;
+ return new IOException(MessageFormat.format(
+ JGitText.get().packfileIsTruncated, getPackName()));
+ }
+
private void readFully(long position, byte[] dstbuf, int dstoff, int cnt,
DfsReader ctx) throws IOException {
if (ctx.copy(this, position, dstbuf, dstoff, cnt) != cnt)
@@ -682,18 +771,8 @@ public final class DfsPackFile {
ReadableChannel rc = ctx.db.openFile(packDesc, PACK);
try {
- // If the block alignment is not yet known, discover it. Prefer the
- // larger size from either the cache or the file itself.
- int size = blockSize;
- if (size == 0) {
- size = rc.blockSize();
- if (size <= 0)
- size = cache.getBlockSize();
- else if (size < cache.getBlockSize())
- size = (cache.getBlockSize() / size) * size;
- blockSize = size;
- pos = (pos / size) * size;
- }
+ int size = blockSize(rc);
+ pos = (pos / size) * size;
// If the size of the file is not yet known, try to discover it.
// Channels may choose to return -1 to indicate they don't
@@ -715,7 +794,7 @@ public final class DfsPackFile {
byte[] buf = new byte[size];
rc.position(pos);
- int cnt = IO.read(rc, buf, 0, size);
+ int cnt = read(rc, ByteBuffer.wrap(buf, 0, size));
if (cnt != size) {
if (0 <= len) {
throw new EOFException(MessageFormat.format(
@@ -744,6 +823,30 @@ public final class DfsPackFile {
}
}
+ private int blockSize(ReadableChannel rc) {
+ // If the block alignment is not yet known, discover it. Prefer the
+ // larger size from either the cache or the file itself.
+ int size = blockSize;
+ if (size == 0) {
+ size = rc.blockSize();
+ if (size <= 0)
+ size = cache.getBlockSize();
+ else if (size < cache.getBlockSize())
+ size = (cache.getBlockSize() / size) * size;
+ blockSize = size;
+ }
+ return size;
+ }
+
+ private static int read(ReadableChannel rc, ByteBuffer buf)
+ throws IOException {
+ int n;
+ do {
+ n = rc.read(buf);
+ } while (0 < n && buf.hasRemaining());
+ return buf.position();
+ }
+
ObjectLoader load(DfsReader ctx, long pos)
throws IOException {
try {
@@ -840,6 +943,7 @@ public final class DfsPackFile {
if (data == null)
throw new LargeObjectException();
+ assert(delta != null);
do {
// Cache only the base immediately before desired object.
if (cached)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
index 4cf7cbe..1665c2c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
@@ -44,14 +44,10 @@
package org.eclipse.jgit.internal.storage.dfs;
-import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
import java.io.IOException;
-import java.security.MessageDigest;
-import java.text.MessageFormat;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@@ -81,7 +77,6 @@ import org.eclipse.jgit.lib.AsyncObjectLoaderQueue;
import org.eclipse.jgit.lib.AsyncObjectSizeQueue;
import org.eclipse.jgit.lib.BitmapIndex;
import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.InflaterCache;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
@@ -216,7 +211,8 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
}
if (typeHint == OBJ_ANY)
- throw new MissingObjectException(objectId.copy(), "unknown");
+ throw new MissingObjectException(objectId.copy(),
+ JGitText.get().unknownObjectType2);
throw new MissingObjectException(objectId.copy(), typeHint);
}
@@ -345,7 +341,8 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
public ObjectLoader open() throws IOException {
if (cur.pack == null)
- throw new MissingObjectException(cur.id, "unknown");
+ throw new MissingObjectException(cur.id,
+ JGitText.get().unknownObjectType2);
return cur.pack.load(DfsReader.this, cur.offset);
}
@@ -382,7 +379,8 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
if (idItr.hasNext()) {
cur = idItr.next();
if (cur.pack == null)
- throw new MissingObjectException(cur.id, "unknown");
+ throw new MissingObjectException(cur.id,
+ JGitText.get().unknownObjectType2);
sz = cur.pack.getObjectSize(DfsReader.this, cur.offset);
return true;
} else if (findAllError != null) {
@@ -435,7 +433,8 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
}
if (typeHint == OBJ_ANY)
- throw new MissingObjectException(objectId.copy(), "unknown");
+ throw new MissingObjectException(objectId.copy(),
+ JGitText.get().unknownObjectType2);
throw new MissingObjectException(objectId.copy(), typeHint);
}
@@ -498,9 +497,9 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
out.writeObject(otp);
}
- public void copyPackAsIs(PackOutputStream out, CachedPack pack,
- boolean validate) throws IOException {
- ((DfsCachedPack) pack).copyAsIs(out, validate, this);
+ public void copyPackAsIs(PackOutputStream out, CachedPack pack)
+ throws IOException {
+ ((DfsCachedPack) pack).copyAsIs(out, this);
}
/**
@@ -547,52 +546,6 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
return cnt - need;
}
- void copyPackAsIs(DfsPackFile pack, long length, boolean validate,
- PackOutputStream out) throws IOException {
- MessageDigest md = null;
- if (validate) {
- md = Constants.newMessageDigest();
- byte[] buf = out.getCopyBuffer();
- pin(pack, 0);
- if (block.copy(0, buf, 0, 12) != 12) {
- pack.setInvalid();
- throw new IOException(MessageFormat.format(
- JGitText.get().packfileIsTruncated, pack.getPackName()));
- }
- md.update(buf, 0, 12);
- }
-
- long position = 12;
- long remaining = length - (12 + 20);
- while (0 < remaining) {
- pin(pack, position);
-
- int ptr = (int) (position - block.start);
- int n = (int) Math.min(block.size() - ptr, remaining);
- block.write(out, position, n, md);
- position += n;
- remaining -= n;
- }
-
- if (md != null) {
- byte[] buf = new byte[20];
- byte[] actHash = md.digest();
-
- pin(pack, position);
- if (block.copy(position, buf, 0, 20) != 20) {
- pack.setInvalid();
- throw new IOException(MessageFormat.format(
- JGitText.get().packfileIsTruncated, pack.getPackName()));
- }
- if (!Arrays.equals(actHash, buf)) {
- pack.setInvalid();
- throw new IOException(MessageFormat.format(
- JGitText.get().packfileCorruptionDetected,
- pack.getPackDescription().getFileName(PACK)));
- }
- }
- }
-
/**
* Inflate a region of the pack starting at {@code position}.
*
@@ -664,9 +617,13 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
}
}
+ void unpin() {
+ block = null;
+ }
+
/** Release the current window cursor. */
@Override
- public void release() {
+ public void close() {
last = null;
block = null;
baseCache = null;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java
index 2a62547..8419807 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderOptions.java
@@ -46,6 +46,7 @@ package org.eclipse.jgit.internal.storage.dfs;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_CORE_SECTION;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DFS_SECTION;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_DELTA_BASE_CACHE_LIMIT;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_STREAM_BUFFER;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_STREAM_FILE_TRESHOLD;
import org.eclipse.jgit.lib.Config;
@@ -60,9 +61,10 @@ public class DfsReaderOptions {
public static final int MiB = 1024 * KiB;
private int deltaBaseCacheLimit;
-
private int streamFileThreshold;
+ private int streamPackBufferSize;
+
/** Create a default reader configuration. */
public DfsReaderOptions() {
setDeltaBaseCacheLimit(10 * MiB);
@@ -105,6 +107,27 @@ public class DfsReaderOptions {
}
/**
+ * @return number of bytes to use for buffering when streaming a pack file
+ * during copying. If 0 the block size of the pack is used.
+ * @since 4.0
+ */
+ public int getStreamPackBufferSize() {
+ return streamPackBufferSize;
+ }
+
+ /**
+ * @param bufsz
+ * new buffer size in bytes for buffers used when streaming pack
+ * files during copying.
+ * @return {@code this}
+ * @since 4.0
+ */
+ public DfsReaderOptions setStreamPackBufferSize(int bufsz) {
+ streamPackBufferSize = Math.max(0, bufsz);
+ return this;
+ }
+
+ /**
* Update properties by setting fields from the configuration.
* <p>
* If a property is not defined in the configuration, then it is left
@@ -130,6 +153,12 @@ public class DfsReaderOptions {
sft = Math.min(sft, maxMem / 4); // don't use more than 1/4 of the heap
sft = Math.min(sft, Integer.MAX_VALUE); // cannot exceed array length
setStreamFileThreshold((int) sft);
+
+ setStreamPackBufferSize(rc.getInt(
+ CONFIG_CORE_SECTION,
+ CONFIG_DFS_SECTION,
+ CONFIG_KEY_STREAM_BUFFER,
+ getStreamPackBufferSize()));
return this;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefDatabase.java
index 593aaac..e5469f6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRefDatabase.java
@@ -91,6 +91,13 @@ public abstract class DfsRefDatabase extends RefDatabase {
}
@Override
+ public Ref exactRef(String name) throws IOException {
+ RefCache curr = read();
+ Ref ref = curr.ids.get(name);
+ return ref != null ? resolve(ref, 0, curr.ids) : null;
+ }
+
+ @Override
public Ref getRef(String needle) throws IOException {
RefCache curr = read();
for (String prefix : SEARCH_PATH) {
@@ -103,14 +110,6 @@ public abstract class DfsRefDatabase extends RefDatabase {
return null;
}
- private Ref getOneRef(String refName) throws IOException {
- RefCache curr = read();
- Ref ref = curr.ids.get(refName);
- if (ref != null)
- return resolve(ref, 0, curr.ids);
- return ref;
- }
-
@Override
public List<Ref> getAdditionalRefs() {
return Collections.emptyList();
@@ -183,8 +182,7 @@ public abstract class DfsRefDatabase extends RefDatabase {
private Ref doPeel(final Ref leaf) throws MissingObjectException,
IOException {
- RevWalk rw = new RevWalk(repository);
- try {
+ try (RevWalk rw = new RevWalk(repository)) {
RevObject obj = rw.parseAny(leaf.getObjectId());
if (obj instanceof RevTag) {
return new ObjectIdRef.PeeledTag(
@@ -198,8 +196,6 @@ public abstract class DfsRefDatabase extends RefDatabase {
leaf.getName(),
leaf.getObjectId());
}
- } finally {
- rw.release();
}
}
@@ -215,7 +211,7 @@ public abstract class DfsRefDatabase extends RefDatabase {
public RefUpdate newUpdate(String refName, boolean detach)
throws IOException {
boolean detachingSymbolicRef = false;
- Ref ref = getOneRef(refName);
+ Ref ref = exactRef(refName);
if (ref == null)
ref = new ObjectIdRef.Unpeeled(NEW, refName, null);
else
@@ -266,6 +262,11 @@ public abstract class DfsRefDatabase extends RefDatabase {
}
@Override
+ public void refresh() {
+ clearCache();
+ }
+
+ @Override
public void close() {
clearCache();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java
index 122f6d3..ef88450 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java
@@ -44,8 +44,13 @@
package org.eclipse.jgit.internal.storage.dfs;
import java.io.IOException;
+import java.io.InputStream;
import java.text.MessageFormat;
+import java.util.Collections;
+import org.eclipse.jgit.attributes.AttributesNode;
+import org.eclipse.jgit.attributes.AttributesNodeProvider;
+import org.eclipse.jgit.attributes.AttributesRule;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.RefUpdate;
@@ -74,9 +79,6 @@ public abstract class DfsRepository extends Repository {
@Override
public abstract DfsObjDatabase getObjectDatabase();
- @Override
- public abstract DfsRefDatabase getRefDatabase();
-
/** @return a description of this repository. */
public DfsRepositoryDescription getDescription() {
return description;
@@ -90,7 +92,10 @@ public abstract class DfsRepository extends Repository {
* the repository cannot be checked.
*/
public boolean exists() throws IOException {
- return getRefDatabase().exists();
+ if (getRefDatabase() instanceof DfsRefDatabase) {
+ return ((DfsRefDatabase) getRefDatabase()).exists();
+ }
+ return true;
}
@Override
@@ -112,7 +117,7 @@ public abstract class DfsRepository extends Repository {
@Override
public void scanForRepoChanges() throws IOException {
- getRefDatabase().clearCache();
+ getRefDatabase().refresh();
getObjectDatabase().clearCache();
}
@@ -126,4 +131,36 @@ public abstract class DfsRepository extends Repository {
public ReflogReader getReflogReader(String refName) throws IOException {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public AttributesNodeProvider createAttributesNodeProvider() {
+ // TODO Check if the implementation used in FileRepository can be used
+ // for this kind of repository
+ return new EmptyAttributesNodeProvider();
+ }
+
+ private static class EmptyAttributesNodeProvider implements
+ AttributesNodeProvider {
+ private EmptyAttributesNode emptyAttributesNode = new EmptyAttributesNode();
+
+ public AttributesNode getInfoAttributesNode() throws IOException {
+ return emptyAttributesNode;
+ }
+
+ public AttributesNode getGlobalAttributesNode() throws IOException {
+ return emptyAttributesNode;
+ }
+
+ private static class EmptyAttributesNode extends AttributesNode {
+
+ public EmptyAttributesNode() {
+ super(Collections.<AttributesRule> emptyList());
+ }
+
+ @Override
+ public void parse(InputStream in) throws IOException {
+ // Do nothing
+ }
+ }
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepositoryBuilder.java
index e0c0d0a..77e060a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepositoryBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepositoryBuilder.java
@@ -46,6 +46,7 @@ package org.eclipse.jgit.internal.storage.dfs;
import java.io.File;
import java.io.IOException;
+import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.BaseRepositoryBuilder;
/**
@@ -141,7 +142,8 @@ public abstract class DfsRepositoryBuilder<B extends DfsRepositoryBuilder, R ext
@Override
public B addAlternateObjectDirectory(File other) {
- throw new UnsupportedOperationException("Alternates not supported");
+ throw new UnsupportedOperationException(
+ JGitText.get().unsupportedAlternates);
}
@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java
index 18fedf8..5e246b4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java
@@ -9,15 +9,26 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Ref.Storage;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.SymbolicRef;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
import org.eclipse.jgit.util.RefList;
/**
@@ -31,11 +42,20 @@ import org.eclipse.jgit.util.RefList;
* is garbage collected. Closing the repository has no impact on its memory.
*/
public class InMemoryRepository extends DfsRepository {
- private static final AtomicInteger packId = new AtomicInteger();
+ /** Builder for in-memory repositories. */
+ public static class Builder
+ extends DfsRepositoryBuilder<Builder, InMemoryRepository> {
+ @Override
+ public InMemoryRepository build() throws IOException {
+ return new InMemoryRepository(this);
+ }
+ }
- private final DfsObjDatabase objdb;
+ static final AtomicInteger packId = new AtomicInteger();
- private final DfsRefDatabase refdb;
+ private final DfsObjDatabase objdb;
+ private final RefDatabase refdb;
+ private boolean performsAtomicTransactions = true;
/**
* Initialize a new in-memory repository.
@@ -45,13 +65,11 @@ public class InMemoryRepository extends DfsRepository {
* @since 2.0
*/
public InMemoryRepository(DfsRepositoryDescription repoDesc) {
- super(new DfsRepositoryBuilder<DfsRepositoryBuilder, InMemoryRepository>() {
- @Override
- public InMemoryRepository build() throws IOException {
- throw new UnsupportedOperationException();
- }
- }.setRepositoryDescription(repoDesc));
+ this(new Builder().setRepositoryDescription(repoDesc));
+ }
+ InMemoryRepository(Builder builder) {
+ super(builder);
objdb = new MemObjDatabase(this);
refdb = new MemRefDatabase();
}
@@ -62,10 +80,21 @@ public class InMemoryRepository extends DfsRepository {
}
@Override
- public DfsRefDatabase getRefDatabase() {
+ public RefDatabase getRefDatabase() {
return refdb;
}
+ /**
+ * Enable (or disable) the atomic reference transaction support.
+ * <p>
+ * Useful for testing atomic support enabled or disabled.
+ *
+ * @param atomic
+ */
+ public void setPerformsAtomicTransactions(boolean atomic) {
+ performsAtomicTransactions = atomic;
+ }
+
private class MemObjDatabase extends DfsObjDatabase {
private List<DfsPackDescription> packs = new ArrayList<DfsPackDescription>();
@@ -129,7 +158,7 @@ public class InMemoryRepository extends DfsRepository {
}
private static class MemPack extends DfsPackDescription {
- private final Map<PackExt, byte[]>
+ final Map<PackExt, byte[]>
fileMap = new HashMap<PackExt, byte[]>();
MemPack(String name, DfsRepositoryDescription repoDesc) {
@@ -217,70 +246,206 @@ public class InMemoryRepository extends DfsRepository {
public int blockSize() {
return 0;
}
+
+ public void setReadAheadBytes(int b) {
+ // Unnecessary on a byte array.
+ }
}
private class MemRefDatabase extends DfsRefDatabase {
private final ConcurrentMap<String, Ref> refs = new ConcurrentHashMap<String, Ref>();
+ private final ReadWriteLock lock = new ReentrantReadWriteLock(true /* fair */);
MemRefDatabase() {
super(InMemoryRepository.this);
}
@Override
+ public boolean performsAtomicTransactions() {
+ return performsAtomicTransactions;
+ }
+
+ @Override
+ public BatchRefUpdate newBatchUpdate() {
+ return new BatchRefUpdate(this) {
+ @Override
+ public void execute(RevWalk walk, ProgressMonitor monitor)
+ throws IOException {
+ if (performsAtomicTransactions()) {
+ try {
+ lock.writeLock().lock();
+ batch(walk, getCommands());
+ } finally {
+ lock.writeLock().unlock();
+ }
+ } else {
+ super.execute(walk, monitor);
+ }
+ }
+ };
+ }
+
+ @Override
protected RefCache scanAllRefs() throws IOException {
RefList.Builder<Ref> ids = new RefList.Builder<Ref>();
RefList.Builder<Ref> sym = new RefList.Builder<Ref>();
- for (Ref ref : refs.values()) {
- if (ref.isSymbolic())
- sym.add(ref);
- ids.add(ref);
+ try {
+ lock.readLock().lock();
+ for (Ref ref : refs.values()) {
+ if (ref.isSymbolic())
+ sym.add(ref);
+ ids.add(ref);
+ }
+ } finally {
+ lock.readLock().unlock();
}
ids.sort();
sym.sort();
return new RefCache(ids.toRefList(), sym.toRefList());
}
+ private void batch(RevWalk walk, List<ReceiveCommand> cmds) {
+ // Validate that the target exists in a new RevWalk, as the RevWalk
+ // from the RefUpdate might be reading back unflushed objects.
+ Map<ObjectId, ObjectId> peeled = new HashMap<>();
+ try (RevWalk rw = new RevWalk(getRepository())) {
+ for (ReceiveCommand c : cmds) {
+ if (c.getResult() != ReceiveCommand.Result.NOT_ATTEMPTED) {
+ ReceiveCommand.abort(cmds);
+ return;
+ }
+
+ if (!ObjectId.zeroId().equals(c.getNewId())) {
+ try {
+ RevObject o = rw.parseAny(c.getNewId());
+ if (o instanceof RevTag) {
+ peeled.put(o, rw.peel(o).copy());
+ }
+ } catch (IOException e) {
+ c.setResult(ReceiveCommand.Result.REJECTED_MISSING_OBJECT);
+ ReceiveCommand.abort(cmds);
+ return;
+ }
+ }
+ }
+ }
+
+ // Check all references conform to expected old value.
+ for (ReceiveCommand c : cmds) {
+ Ref r = refs.get(c.getRefName());
+ if (r == null) {
+ if (c.getType() != ReceiveCommand.Type.CREATE) {
+ c.setResult(ReceiveCommand.Result.LOCK_FAILURE);
+ ReceiveCommand.abort(cmds);
+ return;
+ }
+ } else {
+ ObjectId objectId = r.getObjectId();
+ if (r.isSymbolic() || objectId == null
+ || !objectId.equals(c.getOldId())) {
+ c.setResult(ReceiveCommand.Result.LOCK_FAILURE);
+ ReceiveCommand.abort(cmds);
+ return;
+ }
+ }
+ }
+
+ // Write references.
+ for (ReceiveCommand c : cmds) {
+ if (c.getType() == ReceiveCommand.Type.DELETE) {
+ refs.remove(c.getRefName());
+ c.setResult(ReceiveCommand.Result.OK);
+ continue;
+ }
+
+ ObjectId p = peeled.get(c.getNewId());
+ Ref r;
+ if (p != null) {
+ r = new ObjectIdRef.PeeledTag(Storage.PACKED,
+ c.getRefName(), c.getNewId(), p);
+ } else {
+ r = new ObjectIdRef.PeeledNonTag(Storage.PACKED,
+ c.getRefName(), c.getNewId());
+ }
+ refs.put(r.getName(), r);
+ c.setResult(ReceiveCommand.Result.OK);
+ }
+ clearCache();
+ }
+
@Override
protected boolean compareAndPut(Ref oldRef, Ref newRef)
throws IOException {
- ObjectId id = newRef.getObjectId();
- if (id != null) {
- RevWalk rw = new RevWalk(getRepository());
- try {
- // Validate that the target exists in a new RevWalk, as the RevWalk
- // from the RefUpdate might be reading back unflushed objects.
- rw.parseAny(id);
- } finally {
- rw.release();
+ try {
+ lock.writeLock().lock();
+ ObjectId id = newRef.getObjectId();
+ if (id != null) {
+ try (RevWalk rw = new RevWalk(getRepository())) {
+ // Validate that the target exists in a new RevWalk, as the RevWalk
+ // from the RefUpdate might be reading back unflushed objects.
+ rw.parseAny(id);
+ }
}
- }
- String name = newRef.getName();
- if (oldRef == null || oldRef.getStorage() == Storage.NEW)
- return refs.putIfAbsent(name, newRef) == null;
- Ref cur = refs.get(name);
- if (cur != null && eq(cur, oldRef))
- return refs.replace(name, cur, newRef);
- else
- return false;
+ String name = newRef.getName();
+ if (oldRef == null)
+ return refs.putIfAbsent(name, newRef) == null;
+
+ Ref cur = refs.get(name);
+ Ref toCompare = cur;
+ if (toCompare != null) {
+ if (toCompare.isSymbolic()) {
+ // Arm's-length dereference symrefs before the compare, since
+ // DfsRefUpdate#doLink(String) stores them undereferenced.
+ Ref leaf = toCompare.getLeaf();
+ if (leaf.getObjectId() == null) {
+ leaf = refs.get(leaf.getName());
+ if (leaf.isSymbolic())
+ // Not supported at the moment.
+ throw new IllegalArgumentException();
+ toCompare = new SymbolicRef(
+ name,
+ new ObjectIdRef.Unpeeled(
+ Storage.NEW,
+ leaf.getName(),
+ leaf.getObjectId()));
+ } else
+ toCompare = toCompare.getLeaf();
+ }
+ if (eq(toCompare, oldRef))
+ return refs.replace(name, cur, newRef);
+ }
+
+ if (oldRef.getStorage() == Storage.NEW)
+ return refs.putIfAbsent(name, newRef) == null;
+ return false;
+ } finally {
+ lock.writeLock().unlock();
+ }
}
@Override
protected boolean compareAndRemove(Ref oldRef) throws IOException {
- String name = oldRef.getName();
- Ref cur = refs.get(name);
- if (cur != null && eq(cur, oldRef))
- return refs.remove(name, cur);
- else
- return false;
+ try {
+ lock.writeLock().lock();
+ String name = oldRef.getName();
+ Ref cur = refs.get(name);
+ if (cur != null && eq(cur, oldRef))
+ return refs.remove(name, cur);
+ else
+ return false;
+ } finally {
+ lock.writeLock().unlock();
+ }
}
private boolean eq(Ref a, Ref b) {
- if (a.getObjectId() == null && b.getObjectId() == null)
- return true;
- if (a.getObjectId() != null)
- return a.getObjectId().equals(b.getObjectId());
- return false;
+ if (!Objects.equals(a.getName(), b.getName()))
+ return false;
+ // Compare leaf object IDs, since the oldRef passed into compareAndPut
+ // when detaching a symref is an ObjectIdRef.
+ return Objects.equals(a.getLeaf().getObjectId(),
+ b.getLeaf().getObjectId());
}
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/LargePackedWholeObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/LargePackedWholeObject.java
index 4b050c5..6d40a75 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/LargePackedWholeObject.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/LargePackedWholeObject.java
@@ -112,8 +112,10 @@ final class LargePackedWholeObject extends ObjectLoader {
ObjectId obj = pack.getReverseIdx(ctx).findObject(objectOffset);
return ctx.open(obj, type).openStream();
} finally {
- ctx.release();
+ ctx.close();
}
+ } finally {
+ ctx.close();
}
// Align buffer to inflater size, at a larger than default block.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/PackInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/PackInputStream.java
index 805d243..bb8445b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/PackInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/PackInputStream.java
@@ -80,6 +80,6 @@ final class PackInputStream extends InputStream {
@Override
public void close() {
- ctx.release();
+ ctx.close();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReadableChannel.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReadableChannel.java
index 5ec7079..240d552 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReadableChannel.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/ReadableChannel.java
@@ -100,4 +100,33 @@ public interface ReadableChannel extends ReadableByteChannel {
* not need to be a power of 2.
*/
public int blockSize();
+
+ /**
+ * Recommend the channel maintain a read-ahead buffer.
+ * <p>
+ * A read-ahead buffer of approximately {@code bufferSize} in bytes may be
+ * allocated and used by the channel to smooth out latency for read.
+ * <p>
+ * Callers can continue to read in smaller than {@code bufferSize} chunks.
+ * With read-ahead buffering enabled read latency may fluctuate in a pattern
+ * of one slower read followed by {@code (bufferSize / readSize) - 1} fast
+ * reads satisfied by the read-ahead buffer. When summed up overall time to
+ * read the same contiguous range should be lower than if read-ahead was not
+ * enabled, as the implementation can combine reads to increase throughput.
+ * <p>
+ * To avoid unnecessary IO callers should only enable read-ahead if the
+ * majority of the channel will be accessed in order.
+ * <p>
+ * Implementations may chose to read-ahead using asynchronous APIs or
+ * background threads, or may simply aggregate reads using a buffer.
+ * <p>
+ * This read ahead stays in effect until the channel is closed or the buffer
+ * size is set to 0.
+ *
+ * @param bufferSize
+ * requested size of the read ahead buffer, in bytes.
+ * @throws IOException
+ * if the read ahead cannot be adjusted.
+ */
+ public void setReadAheadBytes(int bufferSize) throws IOException;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java
index 0c3c736..b27bcc4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java
@@ -63,11 +63,11 @@ import org.eclipse.jgit.util.BlockList;
public class BitmapIndexImpl implements BitmapIndex {
private static final int EXTRA_BITS = 10 * 1024;
- private final PackBitmapIndex packIndex;
+ final PackBitmapIndex packIndex;
- private final MutableBitmapIndex mutableIndex;
+ final MutableBitmapIndex mutableIndex;
- private final int indexObjectCount;
+ final int indexObjectCount;
/**
* Creates a BitmapIndex that is back by Compressed bitmaps.
@@ -85,18 +85,20 @@ public class BitmapIndexImpl implements BitmapIndex {
return packIndex;
}
+ @Override
public CompressedBitmap getBitmap(AnyObjectId objectId) {
EWAHCompressedBitmap compressed = packIndex.getBitmap(objectId);
if (compressed == null)
return null;
- return new CompressedBitmap(compressed);
+ return new CompressedBitmap(compressed, this);
}
+ @Override
public CompressedBitmapBuilder newBitmapBuilder() {
- return new CompressedBitmapBuilder();
+ return new CompressedBitmapBuilder(this);
}
- private int findPosition(AnyObjectId objectId) {
+ int findPosition(AnyObjectId objectId) {
int position = packIndex.findPosition(objectId);
if (position < 0) {
position = mutableIndex.findPosition(objectId);
@@ -106,10 +108,10 @@ public class BitmapIndexImpl implements BitmapIndex {
return position;
}
- private int addObject(AnyObjectId objectId, int type) {
+ int findOrInsert(AnyObjectId objectId, int type) {
int position = findPosition(objectId);
if (position < 0) {
- position = mutableIndex.addObject(objectId, type);
+ position = mutableIndex.findOrInsert(objectId, type);
position += indexObjectCount;
}
return position;
@@ -122,11 +124,11 @@ public class BitmapIndexImpl implements BitmapIndex {
private BitSet toRemove;
- private ComboBitset() {
+ ComboBitset() {
this(new EWAHCompressedBitmap());
}
- private ComboBitset(EWAHCompressedBitmap bitmap) {
+ ComboBitset(EWAHCompressedBitmap bitmap) {
this.inflatingBitmap = new InflatingBitSet(bitmap);
}
@@ -197,15 +199,22 @@ public class BitmapIndexImpl implements BitmapIndex {
}
}
- private final class CompressedBitmapBuilder implements BitmapBuilder {
- private ComboBitset bitset = new ComboBitset();
+ private static final class CompressedBitmapBuilder implements BitmapBuilder {
+ private ComboBitset bitset;
+ private final BitmapIndexImpl bitmapIndex;
+ CompressedBitmapBuilder(BitmapIndexImpl bitmapIndex) {
+ this.bitset = new ComboBitset();
+ this.bitmapIndex = bitmapIndex;
+ }
+
+ @Override
public boolean add(AnyObjectId objectId, int type) {
- int position = addObject(objectId, type);
+ int position = bitmapIndex.findOrInsert(objectId, type);
if (bitset.contains(position))
return false;
- Bitmap entry = getBitmap(objectId);
+ Bitmap entry = bitmapIndex.getBitmap(objectId);
if (entry != null) {
or(entry);
return false;
@@ -215,120 +224,142 @@ public class BitmapIndexImpl implements BitmapIndex {
return true;
}
+ @Override
public boolean contains(AnyObjectId objectId) {
- int position = findPosition(objectId);
+ int position = bitmapIndex.findPosition(objectId);
return 0 <= position && bitset.contains(position);
}
+ @Override
+ public BitmapBuilder addObject(AnyObjectId objectId, int type) {
+ bitset.set(bitmapIndex.findOrInsert(objectId, type));
+ return this;
+ }
+
+ @Override
public void remove(AnyObjectId objectId) {
- int position = findPosition(objectId);
+ int position = bitmapIndex.findPosition(objectId);
if (0 <= position)
bitset.remove(position);
}
+ @Override
public CompressedBitmapBuilder or(Bitmap other) {
- if (isSameCompressedBitmap(other)) {
- bitset.or(((CompressedBitmap) other).bitmap);
- } else if (isSameCompressedBitmapBuilder(other)) {
- CompressedBitmapBuilder b = (CompressedBitmapBuilder) other;
- bitset.or(b.bitset.combine());
- } else {
- throw new IllegalArgumentException();
- }
+ bitset.or(ewahBitmap(other));
return this;
}
+ @Override
public CompressedBitmapBuilder andNot(Bitmap other) {
- if (isSameCompressedBitmap(other)) {
- bitset.andNot(((CompressedBitmap) other).bitmap);
- } else if (isSameCompressedBitmapBuilder(other)) {
- CompressedBitmapBuilder b = (CompressedBitmapBuilder) other;
- bitset.andNot(b.bitset.combine());
- } else {
- throw new IllegalArgumentException();
- }
+ bitset.andNot(ewahBitmap(other));
return this;
}
+ @Override
public CompressedBitmapBuilder xor(Bitmap other) {
- if (isSameCompressedBitmap(other)) {
- bitset.xor(((CompressedBitmap) other).bitmap);
- } else if (isSameCompressedBitmapBuilder(other)) {
- CompressedBitmapBuilder b = (CompressedBitmapBuilder) other;
- bitset.xor(b.bitset.combine());
- } else {
- throw new IllegalArgumentException();
- }
+ bitset.xor(ewahBitmap(other));
return this;
}
/** @return the fully built immutable bitmap */
+ @Override
public CompressedBitmap build() {
- return new CompressedBitmap(bitset.combine());
+ return new CompressedBitmap(bitset.combine(), bitmapIndex);
}
+ @Override
public Iterator<BitmapObject> iterator() {
return build().iterator();
}
+ @Override
public int cardinality() {
return bitset.combine().cardinality();
}
+ @Override
public boolean removeAllOrNone(PackBitmapIndex index) {
- if (!packIndex.equals(index))
+ if (!bitmapIndex.packIndex.equals(index))
return false;
EWAHCompressedBitmap curr = bitset.combine()
- .xor(ones(indexObjectCount));
+ .xor(ones(bitmapIndex.indexObjectCount));
IntIterator ii = curr.intIterator();
- if (ii.hasNext() && ii.next() < indexObjectCount)
+ if (ii.hasNext() && ii.next() < bitmapIndex.indexObjectCount)
return false;
bitset = new ComboBitset(curr);
return true;
}
- private BitmapIndexImpl getBitmapIndex() {
- return BitmapIndexImpl.this;
+ @Override
+ public BitmapIndexImpl getBitmapIndex() {
+ return bitmapIndex;
}
- }
- final class CompressedBitmap implements Bitmap {
- private final EWAHCompressedBitmap bitmap;
+ private EWAHCompressedBitmap ewahBitmap(Bitmap other) {
+ if (other instanceof CompressedBitmap) {
+ CompressedBitmap b = (CompressedBitmap) other;
+ if (b.bitmapIndex != bitmapIndex) {
+ throw new IllegalArgumentException();
+ }
+ return b.bitmap;
+ }
+ if (other instanceof CompressedBitmapBuilder) {
+ CompressedBitmapBuilder b = (CompressedBitmapBuilder) other;
+ if (b.bitmapIndex != bitmapIndex) {
+ throw new IllegalArgumentException();
+ }
+ return b.bitset.combine();
+ }
+ throw new IllegalArgumentException();
+ }
+ }
- private CompressedBitmap(EWAHCompressedBitmap bitmap) {
+ /**
+ * Wrapper for a {@link EWAHCompressedBitmap} and {@link PackBitmapIndex}.
+ * <p>
+ * For a EWAHCompressedBitmap {@code bitmap} representing a vector of
+ * bits, {@code new CompressedBitmap(bitmap, bitmapIndex)} represents the
+ * objects at those positions in {@code bitmapIndex.packIndex}.
+ */
+ public static final class CompressedBitmap implements Bitmap {
+ final EWAHCompressedBitmap bitmap;
+ final BitmapIndexImpl bitmapIndex;
+
+ /**
+ * Construct compressed bitmap for given bitmap and bitmap index
+ *
+ * @param bitmap
+ * @param bitmapIndex
+ */
+ public CompressedBitmap(EWAHCompressedBitmap bitmap, BitmapIndexImpl bitmapIndex) {
this.bitmap = bitmap;
+ this.bitmapIndex = bitmapIndex;
}
+ @Override
public CompressedBitmap or(Bitmap other) {
- return new CompressedBitmap(bitmap.or(bitmapOf(other)));
+ return new CompressedBitmap(bitmap.or(ewahBitmap(other)), bitmapIndex);
}
+ @Override
public CompressedBitmap andNot(Bitmap other) {
- return new CompressedBitmap(bitmap.andNot(bitmapOf(other)));
+ return new CompressedBitmap(bitmap.andNot(ewahBitmap(other)), bitmapIndex);
}
+ @Override
public CompressedBitmap xor(Bitmap other) {
- return new CompressedBitmap(bitmap.xor(bitmapOf(other)));
- }
-
- private EWAHCompressedBitmap bitmapOf(Bitmap other) {
- if (isSameCompressedBitmap(other))
- return ((CompressedBitmap) other).bitmap;
- if (isSameCompressedBitmapBuilder(other))
- return ((CompressedBitmapBuilder) other).build().bitmap;
- CompressedBitmapBuilder builder = newBitmapBuilder();
- builder.or(other);
- return builder.build().bitmap;
+ return new CompressedBitmap(bitmap.xor(ewahBitmap(other)), bitmapIndex);
}
private final IntIterator ofObjectType(int type) {
- return packIndex.ofObjectType(bitmap, type).intIterator();
+ return bitmapIndex.packIndex.ofObjectType(bitmap, type).intIterator();
}
+ @Override
public Iterator<BitmapObject> iterator() {
- final IntIterator dynamic = bitmap.andNot(ones(indexObjectCount))
+ final IntIterator dynamic = bitmap.andNot(ones(bitmapIndex.indexObjectCount))
.intIterator();
final IntIterator commits = ofObjectType(Constants.OBJ_COMMIT);
final IntIterator trees = ofObjectType(Constants.OBJ_TREE);
@@ -365,12 +396,12 @@ public class BitmapIndexImpl implements BitmapIndex {
throw new NoSuchElementException();
int position = cached.next();
- if (position < indexObjectCount) {
+ if (position < bitmapIndex.indexObjectCount) {
out.type = type;
- out.objectId = packIndex.getObject(position);
+ out.objectId = bitmapIndex.packIndex.getObject(position);
} else {
- position -= indexObjectCount;
- MutableEntry entry = mutableIndex.getObject(position);
+ position -= bitmapIndex.indexObjectCount;
+ MutableEntry entry = bitmapIndex.mutableIndex.getObject(position);
out.type = entry.type;
out.objectId = entry;
}
@@ -387,8 +418,22 @@ public class BitmapIndexImpl implements BitmapIndex {
return bitmap;
}
- private BitmapIndexImpl getPackBitmapIndex() {
- return BitmapIndexImpl.this;
+ private EWAHCompressedBitmap ewahBitmap(Bitmap other) {
+ if (other instanceof CompressedBitmap) {
+ CompressedBitmap b = (CompressedBitmap) other;
+ if (b.bitmapIndex != bitmapIndex) {
+ throw new IllegalArgumentException();
+ }
+ return b.bitmap;
+ }
+ if (other instanceof CompressedBitmapBuilder) {
+ CompressedBitmapBuilder b = (CompressedBitmapBuilder) other;
+ if (b.bitmapIndex != bitmapIndex) {
+ throw new IllegalArgumentException();
+ }
+ return b.bitset.combine();
+ }
+ throw new IllegalArgumentException();
}
}
@@ -419,7 +464,7 @@ public class BitmapIndexImpl implements BitmapIndex {
}
}
- int addObject(AnyObjectId objectId, int type) {
+ int findOrInsert(AnyObjectId objectId, int type) {
MutableEntry entry = new MutableEntry(
objectId, type, revList.size());
revList.add(entry);
@@ -429,9 +474,9 @@ public class BitmapIndexImpl implements BitmapIndex {
}
private static final class MutableEntry extends ObjectIdOwnerMap.Entry {
- private final int type;
+ final int type;
- private final int position;
+ final int position;
MutableEntry(AnyObjectId objectId, int type, int position) {
super(objectId);
@@ -456,23 +501,7 @@ public class BitmapIndexImpl implements BitmapIndex {
}
}
- private boolean isSameCompressedBitmap(Bitmap other) {
- if (other instanceof CompressedBitmap) {
- CompressedBitmap b = (CompressedBitmap) other;
- return this == b.getPackBitmapIndex();
- }
- return false;
- }
-
- private boolean isSameCompressedBitmapBuilder(Bitmap other) {
- if (other instanceof CompressedBitmapBuilder) {
- CompressedBitmapBuilder b = (CompressedBitmapBuilder) other;
- return this == b.getBitmapIndex();
- }
- return false;
- }
-
- private static final EWAHCompressedBitmap ones(int sizeInBits) {
+ static final EWAHCompressedBitmap ones(int sizeInBits) {
EWAHCompressedBitmap mask = new EWAHCompressedBitmap();
mask.addStreamOfEmptyWords(
true, sizeInBits / EWAHCompressedBitmap.wordinbits);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteArrayWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteArrayWindow.java
index 863c553..dc720bc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteArrayWindow.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteArrayWindow.java
@@ -46,7 +46,6 @@
package org.eclipse.jgit.internal.storage.file;
import java.io.IOException;
-import java.security.MessageDigest;
import java.util.zip.CRC32;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
@@ -84,12 +83,10 @@ final class ByteArrayWindow extends ByteWindow {
}
@Override
- void write(PackOutputStream out, long pos, int cnt, MessageDigest digest)
+ void write(PackOutputStream out, long pos, int cnt)
throws IOException {
int ptr = (int) (pos - start);
out.write(array, ptr, cnt);
- if (digest != null)
- digest.update(array, ptr, cnt);
}
void check(Inflater inf, byte[] tmp, long pos, int cnt)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteBufferWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteBufferWindow.java
index 31925d2..05ddd69 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteBufferWindow.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteBufferWindow.java
@@ -47,7 +47,6 @@ package org.eclipse.jgit.internal.storage.file;
import java.io.IOException;
import java.nio.ByteBuffer;
-import java.security.MessageDigest;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
@@ -76,7 +75,7 @@ final class ByteBufferWindow extends ByteWindow {
}
@Override
- void write(PackOutputStream out, long pos, int cnt, MessageDigest digest)
+ void write(PackOutputStream out, long pos, int cnt)
throws IOException {
final ByteBuffer s = buffer.slice();
s.position((int) (pos - start));
@@ -86,8 +85,6 @@ final class ByteBufferWindow extends ByteWindow {
int n = Math.min(cnt, buf.length);
s.get(buf, 0, n);
out.write(buf, 0, n);
- if (digest != null)
- digest.update(buf, 0, n);
cnt -= n;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteWindow.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteWindow.java
index ab5eb7c..e774a14 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteWindow.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ByteWindow.java
@@ -45,7 +45,6 @@
package org.eclipse.jgit.internal.storage.file;
import java.io.IOException;
-import java.security.MessageDigest;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
@@ -121,8 +120,8 @@ abstract class ByteWindow {
*/
protected abstract int copy(int pos, byte[] dstbuf, int dstoff, int cnt);
- abstract void write(PackOutputStream out, long pos, int cnt,
- MessageDigest md) throws IOException;
+ abstract void write(PackOutputStream out, long pos, int cnt)
+ throws IOException;
final int setInput(long pos, Inflater inf) throws DataFormatException {
return setInput((int) (pos - start), inf);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/DeltaBaseCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/DeltaBaseCache.java
index 2f30496..a95dea7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/DeltaBaseCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/DeltaBaseCache.java
@@ -50,7 +50,7 @@ import org.eclipse.jgit.storage.file.WindowCacheConfig;
class DeltaBaseCache {
private static final int CACHE_SZ = 1024;
- private static final SoftReference<Entry> DEAD;
+ static final SoftReference<Entry> DEAD;
private static int hash(final long position) {
return (((int) position) << 22) >>> 22;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
index 995621e..62d2d69 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
@@ -49,16 +49,21 @@ package org.eclipse.jgit.internal.storage.file;
import static org.eclipse.jgit.lib.RefDatabase.ALL;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.HashSet;
import java.util.Set;
+import org.eclipse.jgit.attributes.AttributesNode;
+import org.eclipse.jgit.attributes.AttributesNodeProvider;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.events.ConfigChangedEvent;
import org.eclipse.jgit.events.ConfigChangedListener;
import org.eclipse.jgit.events.IndexChangedEvent;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.reftree.RefTreeDatabase;
import org.eclipse.jgit.internal.storage.file.ObjectDirectory.AlternateHandle;
import org.eclipse.jgit.internal.storage.file.ObjectDirectory.AlternateRepository;
import org.eclipse.jgit.lib.BaseRepositoryBuilder;
@@ -197,7 +202,22 @@ public class FileRepository extends Repository {
}
});
- refs = new RefDirectory(this);
+ final long repositoryFormatVersion = getConfig().getLong(
+ ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_REPO_FORMAT_VERSION, 0);
+
+ String reftype = repoConfig.getString(
+ "extensions", null, "refsStorage"); //$NON-NLS-1$ //$NON-NLS-2$
+ if (repositoryFormatVersion >= 1 && reftype != null) {
+ if (StringUtils.equalsIgnoreCase(reftype, "reftree")) { //$NON-NLS-1$
+ refs = new RefTreeDatabase(this, new RefDirectory(this));
+ } else {
+ throw new IOException(JGitText.get().unknownRepositoryFormat);
+ }
+ } else {
+ refs = new RefDirectory(this);
+ }
+
objectDatabase = new ObjectDirectory(repoConfig, //
options.getObjectDirectory(), //
options.getAlternateObjectDirectories(), //
@@ -205,10 +225,7 @@ public class FileRepository extends Repository {
new File(getDirectory(), Constants.SHALLOW));
if (objectDatabase.exists()) {
- final long repositoryFormatVersion = getConfig().getLong(
- ConfigConstants.CONFIG_CORE_SECTION, null,
- ConfigConstants.CONFIG_KEY_REPO_FORMAT_VERSION, 0);
- if (repositoryFormatVersion > 0)
+ if (repositoryFormatVersion > 1)
throw new IOException(MessageFormat.format(
JGitText.get().unknownRepositoryFormat2,
Long.valueOf(repositoryFormatVersion)));
@@ -479,4 +496,63 @@ public class FileRepository extends Repository {
return new ReflogReaderImpl(this, ref.getName());
return null;
}
+
+ @Override
+ public AttributesNodeProvider createAttributesNodeProvider() {
+ return new AttributesNodeProviderImpl(this);
+ }
+
+ /**
+ * Implementation a {@link AttributesNodeProvider} for a
+ * {@link FileRepository}.
+ *
+ * @author <a href="mailto:arthur.daussy at obeo.fr">Arthur Daussy</a>
+ *
+ */
+ static class AttributesNodeProviderImpl implements
+ AttributesNodeProvider {
+
+ private AttributesNode infoAttributesNode;
+
+ private AttributesNode globalAttributesNode;
+
+ /**
+ * Constructor.
+ *
+ * @param repo
+ * {@link Repository} that will provide the attribute nodes.
+ */
+ protected AttributesNodeProviderImpl(Repository repo) {
+ infoAttributesNode = new InfoAttributesNode(repo);
+ globalAttributesNode = new GlobalAttributesNode(repo);
+ }
+
+ public AttributesNode getInfoAttributesNode() throws IOException {
+ if (infoAttributesNode instanceof InfoAttributesNode)
+ infoAttributesNode = ((InfoAttributesNode) infoAttributesNode)
+ .load();
+ return infoAttributesNode;
+ }
+
+ public AttributesNode getGlobalAttributesNode() throws IOException {
+ if (globalAttributesNode instanceof GlobalAttributesNode)
+ globalAttributesNode = ((GlobalAttributesNode) globalAttributesNode)
+ .load();
+ return globalAttributesNode;
+ }
+
+ static void loadRulesFromFile(AttributesNode r, File attrs)
+ throws FileNotFoundException, IOException {
+ if (attrs.exists()) {
+ FileInputStream in = new FileInputStream(attrs);
+ try {
+ r.parse(in);
+ } finally {
+ in.close();
+ }
+ }
+ }
+
+ }
+
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
index 48335e4..2ce0d47 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
@@ -45,7 +45,6 @@ package org.eclipse.jgit.internal.storage.file;
import static org.eclipse.jgit.internal.storage.pack.PackExt.BITMAP_INDEX;
import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
-import static org.eclipse.jgit.lib.RefDatabase.ALL;
import java.io.File;
import java.io.FileOutputStream;
@@ -53,6 +52,7 @@ import java.io.IOException;
import java.io.OutputStream;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
+import java.nio.file.StandardCopyOption;
import java.text.MessageFormat;
import java.text.ParseException;
import java.util.ArrayList;
@@ -62,14 +62,14 @@ import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
+import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
+import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -78,18 +78,19 @@ import org.eclipse.jgit.errors.NoWorkTreeException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.pack.PackExt;
import org.eclipse.jgit.internal.storage.pack.PackWriter;
-import org.eclipse.jgit.internal.storage.pack.PackWriter.ObjectIdSet;
-import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.internal.storage.reftree.RefTreeNames;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdSet;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Ref.Storage;
import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.ReflogEntry;
+import org.eclipse.jgit.lib.ReflogReader;
import org.eclipse.jgit.revwalk.ObjectWalk;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -126,7 +127,7 @@ public class GC {
* difference between the current refs and the refs which existed during
* last {@link #repack()}.
*/
- private Map<String, Ref> lastPackedRefs;
+ private Collection<Ref> lastPackedRefs;
/**
* Holds the starting time of the last repack() execution. This is needed in
@@ -175,13 +176,17 @@ public class GC {
/**
* Delete old pack files. What is 'old' is defined by specifying a set of
* old pack files and a set of new pack files. Each pack file contained in
- * old pack files but not contained in new pack files will be deleted.
+ * old pack files but not contained in new pack files will be deleted. If an
+ * expirationDate is set then pack files which are younger than the
+ * expirationDate will not be deleted.
*
* @param oldPacks
* @param newPacks
+ * @throws ParseException
*/
private void deleteOldPacks(Collection<PackFile> oldPacks,
- Collection<PackFile> newPacks) {
+ Collection<PackFile> newPacks) throws ParseException {
+ long expireDate = getExpireDate();
oldPackLoop: for (PackFile oldPack : oldPacks) {
String oldName = oldPack.getPackName();
// check whether an old pack file is also among the list of new
@@ -190,7 +195,8 @@ public class GC {
if (oldName.equals(newPack.getPackName()))
continue oldPackLoop;
- if (!oldPack.shouldBeKept()) {
+ if (!oldPack.shouldBeKept()
+ && oldPack.getPackFile().lastModified() < expireDate) {
oldPack.close();
prunePack(oldName);
}
@@ -303,22 +309,7 @@ public class GC {
*/
public void prune(Set<ObjectId> objectsToKeep) throws IOException,
ParseException {
- long expireDate = Long.MAX_VALUE;
-
- if (expire == null && expireAgeMillis == -1) {
- String pruneExpireStr = repo.getConfig().getString(
- ConfigConstants.CONFIG_GC_SECTION, null,
- ConfigConstants.CONFIG_KEY_PRUNEEXPIRE);
- if (pruneExpireStr == null)
- pruneExpireStr = PRUNE_EXPIRE_DEFAULT;
- expire = GitDateParser.parse(pruneExpireStr, null, SystemReader
- .getInstance().getLocale());
- expireAgeMillis = -1;
- }
- if (expire != null)
- expireDate = expire.getTime();
- if (expireAgeMillis != -1)
- expireDate = System.currentTimeMillis() - expireAgeMillis;
+ long expireDate = getExpireDate();
// Collect all loose objects which are old enough, not referenced from
// the index and not in objectsToKeep
@@ -370,17 +361,20 @@ public class GC {
// during last repack(). Only those refs will survive which have been
// added or modified since the last repack. Only these can save existing
// loose refs from being pruned.
- Map<String, Ref> newRefs;
+ Collection<Ref> newRefs;
if (lastPackedRefs == null || lastPackedRefs.isEmpty())
newRefs = getAllRefs();
else {
- newRefs = new HashMap<String, Ref>();
- for (Iterator<Map.Entry<String, Ref>> i = getAllRefs().entrySet()
- .iterator(); i.hasNext();) {
- Entry<String, Ref> newEntry = i.next();
- Ref old = lastPackedRefs.get(newEntry.getKey());
- if (!equals(newEntry.getValue(), old))
- newRefs.put(newEntry.getKey(), newEntry.getValue());
+ Map<String, Ref> last = new HashMap<>();
+ for (Ref r : lastPackedRefs) {
+ last.put(r.getName(), r);
+ }
+ newRefs = new ArrayList<>();
+ for (Ref r : getAllRefs()) {
+ Ref old = last.get(r.getName());
+ if (!equals(r, old)) {
+ newRefs.add(r);
+ }
}
}
@@ -392,10 +386,10 @@ public class GC {
// leave this method.
ObjectWalk w = new ObjectWalk(repo);
try {
- for (Ref cr : newRefs.values())
+ for (Ref cr : newRefs)
w.markStart(w.parseAny(cr.getObjectId()));
if (lastPackedRefs != null)
- for (Ref lpr : lastPackedRefs.values())
+ for (Ref lpr : lastPackedRefs)
w.markUninteresting(w.parseAny(lpr.getObjectId()));
removeReferenced(deletionCandidates, w);
} finally {
@@ -413,11 +407,11 @@ public class GC {
// additional reflog entries not handled during last repack()
ObjectWalk w = new ObjectWalk(repo);
try {
- for (Ref ar : getAllRefs().values())
+ for (Ref ar : getAllRefs())
for (ObjectId id : listRefLogObjects(ar, lastRepackTime))
w.markStart(w.parseAny(id));
if (lastPackedRefs != null)
- for (Ref lpr : lastPackedRefs.values())
+ for (Ref lpr : lastPackedRefs)
w.markUninteresting(w.parseAny(lpr.getObjectId()));
removeReferenced(deletionCandidates, w);
} finally {
@@ -435,6 +429,26 @@ public class GC {
repo.getObjectDatabase().close();
}
+ private long getExpireDate() throws ParseException {
+ long expireDate = Long.MAX_VALUE;
+
+ if (expire == null && expireAgeMillis == -1) {
+ String pruneExpireStr = repo.getConfig().getString(
+ ConfigConstants.CONFIG_GC_SECTION, null,
+ ConfigConstants.CONFIG_KEY_PRUNEEXPIRE);
+ if (pruneExpireStr == null)
+ pruneExpireStr = PRUNE_EXPIRE_DEFAULT;
+ expire = GitDateParser.parse(pruneExpireStr, null, SystemReader
+ .getInstance().getLocale());
+ expireAgeMillis = -1;
+ }
+ if (expire != null)
+ expireDate = expire.getTime();
+ if (expireAgeMillis != -1)
+ expireDate = System.currentTimeMillis() - expireAgeMillis;
+ return expireDate;
+ }
+
/**
* Remove all entries from a map which key is the id of an object referenced
* by the given ObjectWalk
@@ -472,9 +486,10 @@ public class GC {
return false;
return r1.getTarget().getName().equals(r2.getTarget().getName());
} else {
- if (r2.isSymbolic())
+ if (r2.isSymbolic()) {
return false;
- return r1.getObjectId().equals(r2.getObjectId());
+ }
+ return Objects.equals(r1.getObjectId(), r2.getObjectId());
}
}
@@ -517,19 +532,23 @@ public class GC {
Collection<PackFile> toBeDeleted = repo.getObjectDatabase().getPacks();
long time = System.currentTimeMillis();
- Map<String, Ref> refsBefore = getAllRefs();
+ Collection<Ref> refsBefore = getAllRefs();
Set<ObjectId> allHeads = new HashSet<ObjectId>();
Set<ObjectId> nonHeads = new HashSet<ObjectId>();
+ Set<ObjectId> txnHeads = new HashSet<ObjectId>();
Set<ObjectId> tagTargets = new HashSet<ObjectId>();
Set<ObjectId> indexObjects = listNonHEADIndexObjects();
+ RefDatabase refdb = repo.getRefDatabase();
- for (Ref ref : refsBefore.values()) {
+ for (Ref ref : refsBefore) {
nonHeads.addAll(listRefLogObjects(ref, 0));
if (ref.isSymbolic() || ref.getObjectId() == null)
continue;
if (ref.getName().startsWith(Constants.R_HEADS))
allHeads.add(ref.getObjectId());
+ else if (RefTreeNames.isRefTree(refdb, ref.getName()))
+ txnHeads.add(ref.getObjectId());
else
nonHeads.add(ref.getObjectId());
if (ref.getPeeledObjectId() != null)
@@ -539,7 +558,7 @@ public class GC {
List<ObjectIdSet> excluded = new LinkedList<ObjectIdSet>();
for (final PackFile f : repo.getObjectDatabase().getPacks())
if (f.shouldBeKept())
- excluded.add(objectIdSet(f.getIndex()));
+ excluded.add(f.getIndex());
tagTargets.addAll(allHeads);
nonHeads.addAll(indexObjects);
@@ -551,7 +570,7 @@ public class GC {
tagTargets, excluded);
if (heads != null) {
ret.add(heads);
- excluded.add(0, objectIdSet(heads.getIndex()));
+ excluded.add(0, heads.getIndex());
}
}
if (!nonHeads.isEmpty()) {
@@ -559,7 +578,19 @@ public class GC {
if (rest != null)
ret.add(rest);
}
- deleteOldPacks(toBeDeleted, ret);
+ if (!txnHeads.isEmpty()) {
+ PackFile txn = writePack(txnHeads, PackWriter.NONE, null, excluded);
+ if (txn != null)
+ ret.add(txn);
+ }
+ try {
+ deleteOldPacks(toBeDeleted, ret);
+ } catch (ParseException e) {
+ // TODO: the exception has to be wrapped into an IOException because
+ // throwing the ParseException directly would break the API, instead
+ // we should throw a ConfigInvalidException
+ throw new IOException(e);
+ }
prunePacked();
lastPackedRefs = refsBefore;
@@ -575,7 +606,11 @@ public class GC {
* @throws IOException
*/
private Set<ObjectId> listRefLogObjects(Ref ref, long minTime) throws IOException {
- List<ReflogEntry> rlEntries = repo.getReflogReader(ref.getName())
+ ReflogReader reflogReader = repo.getReflogReader(ref.getName());
+ if (reflogReader == null) {
+ return Collections.emptySet();
+ }
+ List<ReflogEntry> rlEntries = reflogReader
.getReverseEntries();
if (rlEntries == null || rlEntries.isEmpty())
return Collections.<ObjectId> emptySet();
@@ -600,11 +635,16 @@ public class GC {
* @return a map where names of refs point to ref objects
* @throws IOException
*/
- private Map<String, Ref> getAllRefs() throws IOException {
- Map<String, Ref> ret = repo.getRefDatabase().getRefs(ALL);
- for (Ref ref : repo.getRefDatabase().getAdditionalRefs())
- ret.put(ref.getName(), ref);
- return ret;
+ private Collection<Ref> getAllRefs() throws IOException {
+ Collection<Ref> refs = RefTreeNames.allRefs(repo.getRefDatabase());
+ List<Ref> addl = repo.getRefDatabase().getAdditionalRefs();
+ if (!addl.isEmpty()) {
+ List<Ref> all = new ArrayList<>(refs.size() + addl.size());
+ all.addAll(refs);
+ all.addAll(addl);
+ return all;
+ }
+ return refs;
}
/**
@@ -618,22 +658,16 @@ public class GC {
*/
private Set<ObjectId> listNonHEADIndexObjects()
throws CorruptObjectException, IOException {
- RevWalk revWalk = null;
- try {
- if (repo.getIndexFile() == null)
- return Collections.emptySet();
- } catch (NoWorkTreeException e) {
+ if (repo.isBare()) {
return Collections.emptySet();
}
- TreeWalk treeWalk = new TreeWalk(repo);
- try {
+ try (TreeWalk treeWalk = new TreeWalk(repo)) {
treeWalk.addTree(new DirCacheIterator(repo.readDirCache()));
ObjectId headID = repo.resolve(Constants.HEAD);
if (headID != null) {
- revWalk = new RevWalk(repo);
- treeWalk.addTree(revWalk.parseTree(headID));
- revWalk.dispose();
- revWalk = null;
+ try (RevWalk revWalk = new RevWalk(repo)) {
+ treeWalk.addTree(revWalk.parseTree(headID));
+ }
}
treeWalk.setFilter(TreeFilter.ANY_DIFF);
@@ -662,15 +696,11 @@ public class GC {
}
}
return ret;
- } finally {
- if (revWalk != null)
- revWalk.dispose();
- treeWalk.release();
}
}
- private PackFile writePack(Set<? extends ObjectId> want,
- Set<? extends ObjectId> have, Set<ObjectId> tagTargets,
+ private PackFile writePack(@NonNull Set<? extends ObjectId> want,
+ @NonNull Set<? extends ObjectId> have, Set<ObjectId> tagTargets,
List<ObjectIdSet> excludeObjects) throws IOException {
File tmpPack = null;
Map<PackExt, File> tmpExts = new TreeMap<PackExt, File>(
@@ -689,8 +719,9 @@ public class GC {
}
});
- PackWriter pw = new PackWriter((pconfig == null) ? new PackConfig(repo) : pconfig, repo.newObjectReader());
- try {
+ try (PackWriter pw = new PackWriter(
+ (pconfig == null) ? new PackConfig(repo) : pconfig,
+ repo.newObjectReader())) {
// prepare the PackWriter
pw.setDeltaBaseAsOffset(true);
pw.setReuseDeltaCommits(false);
@@ -775,42 +806,35 @@ public class GC {
break;
}
tmpPack.setReadOnly();
- boolean delete = true;
- try {
- FileUtils.rename(tmpPack, realPack);
- delete = false;
- for (Map.Entry<PackExt, File> tmpEntry : tmpExts.entrySet()) {
- File tmpExt = tmpEntry.getValue();
- tmpExt.setReadOnly();
-
- File realExt = nameFor(
- id, "." + tmpEntry.getKey().getExtension()); //$NON-NLS-1$
- try {
- FileUtils.rename(tmpExt, realExt);
- } catch (IOException e) {
- File newExt = new File(realExt.getParentFile(),
- realExt.getName() + ".new"); //$NON-NLS-1$
- if (!tmpExt.renameTo(newExt))
- newExt = tmpExt;
- throw new IOException(MessageFormat.format(
- JGitText.get().panicCantRenameIndexFile, newExt,
- realExt));
- }
- }
- } finally {
- if (delete) {
- if (tmpPack.exists())
- tmpPack.delete();
- for (File tmpExt : tmpExts.values()) {
- if (tmpExt.exists())
- tmpExt.delete();
+ FileUtils.rename(tmpPack, realPack, StandardCopyOption.ATOMIC_MOVE);
+ for (Map.Entry<PackExt, File> tmpEntry : tmpExts.entrySet()) {
+ File tmpExt = tmpEntry.getValue();
+ tmpExt.setReadOnly();
+
+ File realExt = nameFor(id,
+ "." + tmpEntry.getKey().getExtension()); //$NON-NLS-1$
+ try {
+ FileUtils.rename(tmpExt, realExt,
+ StandardCopyOption.ATOMIC_MOVE);
+ } catch (IOException e) {
+ File newExt = new File(realExt.getParentFile(),
+ realExt.getName() + ".new"); //$NON-NLS-1$
+ try {
+ FileUtils.rename(tmpExt, newExt,
+ StandardCopyOption.ATOMIC_MOVE);
+ } catch (IOException e2) {
+ newExt = tmpExt;
+ e = e2;
}
+ throw new IOException(MessageFormat.format(
+ JGitText.get().panicCantRenameIndexFile, newExt,
+ realExt), e);
}
}
+
return repo.getObjectDatabase().openPack(realPack);
} finally {
- pw.release();
if (tmpPack != null && tmpPack.exists())
tmpPack.delete();
for (File tmpExt : tmpExts.values()) {
@@ -867,6 +891,11 @@ public class GC {
*/
public long numberOfPackedRefs;
+ /**
+ * The number of bitmaps in the bitmap indices.
+ */
+ public long numberOfBitmaps;
+
public String toString() {
final StringBuilder b = new StringBuilder();
b.append("numberOfPackedObjects=").append(numberOfPackedObjects); //$NON-NLS-1$
@@ -876,15 +905,15 @@ public class GC {
b.append(", numberOfPackedRefs=").append(numberOfPackedRefs); //$NON-NLS-1$
b.append(", sizeOfLooseObjects=").append(sizeOfLooseObjects); //$NON-NLS-1$
b.append(", sizeOfPackedObjects=").append(sizeOfPackedObjects); //$NON-NLS-1$
+ b.append(", numberOfBitmaps=").append(numberOfBitmaps); //$NON-NLS-1$
return b.toString();
}
}
/**
- * Returns the number of objects stored in pack files. If an object is
- * contained in multiple pack files it is counted as often as it occurs.
+ * Returns information about objects and pack files for a FileRepository.
*
- * @return the number of objects stored in pack files
+ * @return information about objects and pack files for a FileRepository
* @throws IOException
*/
public RepoStatistics getStatistics() throws IOException {
@@ -894,6 +923,8 @@ public class GC {
ret.numberOfPackedObjects += f.getIndex().getObjectCount();
ret.numberOfPackFiles++;
ret.sizeOfPackedObjects += f.getPackFile().length();
+ if (f.getBitmapIndex() != null)
+ ret.numberOfBitmaps += f.getBitmapIndex().getBitmapCount();
}
File objDir = repo.getObjectsDirectory();
String[] fanout = objDir.list();
@@ -979,12 +1010,4 @@ public class GC {
this.expire = expire;
expireAgeMillis = -1;
}
-
- private static ObjectIdSet objectIdSet(final PackIndex idx) {
- return new ObjectIdSet() {
- public boolean contains(AnyObjectId objectId) {
- return idx.hasObject(objectId);
- }
- };
- }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GlobalAttributesNode.java
similarity index 61%
copy from org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInputStream.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GlobalAttributesNode.java
index 9cb8349..454d3bf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GlobalAttributesNode.java
@@ -1,5 +1,6 @@
/*
- * Copyright (C) 2010, Google Inc.
+ * Copyright (C) 2014, Arthur Daussy <arthur.daussy at obeo.fr>
+ * Copyright (C) 2015, Christian Halstrick <christian.halstrick at sap.com>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -40,46 +41,47 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-
package org.eclipse.jgit.internal.storage.file;
+import java.io.File;
import java.io.IOException;
-import java.io.InputStream;
-
-class PackInputStream extends InputStream {
- private final WindowCursor wc;
-
- private final PackFile pack;
- private long pos;
+import org.eclipse.jgit.attributes.AttributesNode;
+import org.eclipse.jgit.lib.CoreConfig;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.util.FS;
- PackInputStream(PackFile pack, long pos, WindowCursor wc)
- throws IOException {
- this.pack = pack;
- this.pos = pos;
- this.wc = wc;
+/** Attribute node loaded from global system-wide file. */
+public class GlobalAttributesNode extends AttributesNode {
+ final Repository repository;
- // Pin the first window, to ensure the pack is open and valid.
- //
- wc.pin(pack, pos);
+ /**
+ * @param repository
+ */
+ public GlobalAttributesNode(Repository repository) {
+ this.repository = repository;
}
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- int n = wc.copy(pack, pos, b, off, len);
- pos += n;
- return n;
- }
-
- @Override
- public int read() throws IOException {
- byte[] buf = new byte[1];
- int n = read(buf, 0, 1);
- return n == 1 ? buf[0] & 0xff : -1;
- }
+ /**
+ * @return the attributes node
+ * @throws IOException
+ */
+ public AttributesNode load() throws IOException {
+ AttributesNode r = new AttributesNode();
- @Override
- public void close() {
- wc.release();
+ FS fs = repository.getFS();
+ String path = repository.getConfig().get(CoreConfig.KEY)
+ .getAttributesFile();
+ if (path != null) {
+ File attributesFile;
+ if (path.startsWith("~/")) { //$NON-NLS-1$
+ attributesFile = fs.resolve(fs.userHome(),
+ path.substring(2));
+ } else {
+ attributesFile = fs.resolve(null, path);
+ }
+ FileRepository.AttributesNodeProviderImpl.loadRulesFromFile(r, attributesFile);
+ }
+ return r.getRules().isEmpty() ? null : r;
}
-}
\ No newline at end of file
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java
similarity index 66%
copy from org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInputStream.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java
index 9cb8349..bda5cbe 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java
@@ -1,5 +1,6 @@
/*
- * Copyright (C) 2010, Google Inc.
+ * Copyright (C) 2014, Arthur Daussy <arthur.daussy at obeo.fr>
+ * Copyright (C) 2015, Christian Halstrick <christian.halstrick at sap.com>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -40,46 +41,41 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-
package org.eclipse.jgit.internal.storage.file;
+import java.io.File;
import java.io.IOException;
-import java.io.InputStream;
-class PackInputStream extends InputStream {
- private final WindowCursor wc;
+import org.eclipse.jgit.attributes.AttributesNode;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.util.FS;
- private final PackFile pack;
+/** Attribute node loaded from the $GIT_DIR/info/attributes file. */
+public class InfoAttributesNode extends AttributesNode {
+ final Repository repository;
- private long pos;
+ /**
+ * @param repository
+ */
+ public InfoAttributesNode(Repository repository) {
+ this.repository = repository;
+ }
- PackInputStream(PackFile pack, long pos, WindowCursor wc)
- throws IOException {
- this.pack = pack;
- this.pos = pos;
- this.wc = wc;
+ /**
+ * @return the attributes node
+ * @throws IOException
+ */
+ public AttributesNode load() throws IOException {
+ AttributesNode r = new AttributesNode();
- // Pin the first window, to ensure the pack is open and valid.
- //
- wc.pin(pack, pos);
- }
+ FS fs = repository.getFS();
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- int n = wc.copy(pack, pos, b, off, len);
- pos += n;
- return n;
- }
+ File attributes = fs.resolve(repository.getDirectory(),
+ Constants.INFO_ATTRIBUTES);
+ FileRepository.AttributesNodeProviderImpl.loadRulesFromFile(r, attributes);
- @Override
- public int read() throws IOException {
- byte[] buf = new byte[1];
- int n = read(buf, 0, 1);
- return n == 1 ? buf[0] & 0xff : -1;
+ return r.getRules().isEmpty() ? null : r;
}
- @Override
- public void close() {
- wc.release();
- }
}
\ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LazyObjectIdSetFile.java
similarity index 57%
copy from org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LazyObjectIdSetFile.java
index a0a5d95..1e2617c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListTagCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LazyObjectIdSetFile.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, Ketan Padegaonkar <ketanpadegaonkar at gmail.com>
+ * Copyright (C) 2015, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -40,62 +40,67 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.api;
+package org.eclipse.jgit.internal.storage.file;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Map;
+import java.io.InputStreamReader;
+import java.io.Reader;
-import org.eclipse.jgit.api.errors.GitAPIException;
-import org.eclipse.jgit.api.errors.JGitInternalException;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.MutableObjectId;
+import org.eclipse.jgit.lib.ObjectIdOwnerMap;
+import org.eclipse.jgit.lib.ObjectIdSet;
-/**
- * Used to obtain a list of tags.
- *
- * @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-tag.html"
- * >Git documentation about Tag</a>
- */
-public class ListTagCommand extends GitCommand<List<Ref>> {
+/** Lazily loads a set of ObjectIds, one per line. */
+public class LazyObjectIdSetFile implements ObjectIdSet {
+ private final File src;
+ private ObjectIdOwnerMap<Entry> set;
/**
- * @param repo
+ * Create a new lazy set from a file.
+ *
+ * @param src
+ * the source file.
*/
- protected ListTagCommand(Repository repo) {
- super(repo);
+ public LazyObjectIdSetFile(File src) {
+ this.src = src;
}
- /**
- * @return the tags available
- */
- public List<Ref> call() throws GitAPIException {
- checkCallable();
- Map<String, Ref> refList;
- List<Ref> tags = new ArrayList<Ref>();
- RevWalk revWalk = new RevWalk(repo);
- try {
- refList = repo.getRefDatabase().getRefs(Constants.R_TAGS);
- for (Ref ref : refList.values()) {
- tags.add(ref);
+ @Override
+ public boolean contains(AnyObjectId objectId) {
+ if (set == null) {
+ set = load();
+ }
+ return set.contains(objectId);
+ }
+
+ private ObjectIdOwnerMap<Entry> load() {
+ ObjectIdOwnerMap<Entry> r = new ObjectIdOwnerMap<>();
+ try (FileInputStream fin = new FileInputStream(src);
+ Reader rin = new InputStreamReader(fin, UTF_8);
+ BufferedReader br = new BufferedReader(rin)) {
+ MutableObjectId id = new MutableObjectId();
+ for (String line; (line = br.readLine()) != null;) {
+ id.fromString(line);
+ if (!r.contains(id)) {
+ r.add(new Entry(id));
+ }
}
} catch (IOException e) {
- throw new JGitInternalException(e.getMessage(), e);
- } finally {
- revWalk.release();
+ // Ignore IO errors accessing the lazy set.
}
- Collections.sort(tags, new Comparator<Ref>() {
- public int compare(Ref o1, Ref o2) {
- return o1.getName().compareTo(o2.getName());
- }
- });
- setCallable(false);
- return tags;
+ return r;
}
+ static class Entry extends ObjectIdOwnerMap.Entry {
+ Entry(AnyObjectId id) {
+ super(id);
+ }
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalCachedPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalCachedPack.java
index b70ebcf..fd9dcda 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalCachedPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LocalCachedPack.java
@@ -79,10 +79,10 @@ class LocalCachedPack extends CachedPack {
return cnt;
}
- void copyAsIs(PackOutputStream out, boolean validate, WindowCursor wc)
+ void copyAsIs(PackOutputStream out, WindowCursor wc)
throws IOException {
for (PackFile pack : getPacks())
- pack.copyPackAsIs(out, validate, wc);
+ pack.copyPackAsIs(out, wc);
}
@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
index 06eb42c..ce9677a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
@@ -54,6 +54,7 @@ import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
+import java.nio.file.StandardCopyOption;
import java.text.MessageFormat;
import org.eclipse.jgit.errors.LockFailedException;
@@ -120,16 +121,14 @@ public class LockFile {
private boolean haveLck;
- private FileOutputStream os;
+ FileOutputStream os;
private boolean needSnapshot;
- private boolean fsync;
+ boolean fsync;
private FileSnapshot commitSnapshot;
- private final FS fs;
-
/**
* Create a new lock for any file.
*
@@ -138,11 +137,24 @@ public class LockFile {
* @param fs
* the file system abstraction which will be necessary to perform
* certain file system operations.
+ * @deprecated use {@link LockFile#LockFile(File)} instead
*/
+ @Deprecated
public LockFile(final File f, final FS fs) {
ref = f;
lck = getLockFile(ref);
- this.fs = fs;
+ }
+
+ /**
+ * Create a new lock for any file.
+ *
+ * @param f
+ * the file that will be locked.
+ * @since 4.2
+ */
+ public LockFile(final File f) {
+ ref = f;
+ lck = getLockFile(ref);
}
/**
@@ -227,6 +239,10 @@ public class LockFile {
fis.close();
}
} catch (FileNotFoundException fnfe) {
+ if (ref.exists()) {
+ unlock();
+ throw fnfe;
+ }
// Don't worry about a file that doesn't exist yet, it
// conceptually has no current content to copy.
//
@@ -437,56 +453,14 @@ public class LockFile {
}
saveStatInformation();
- if (lck.renameTo(ref)) {
+ try {
+ FileUtils.rename(lck, ref, StandardCopyOption.ATOMIC_MOVE);
haveLck = false;
return true;
+ } catch (IOException e) {
+ unlock();
+ return false;
}
- if (!ref.exists() || deleteRef()) {
- if (renameLock()) {
- haveLck = false;
- return true;
- }
- }
- unlock();
- return false;
- }
-
- private boolean deleteRef() {
- if (!fs.retryFailedLockFileCommit())
- return ref.delete();
-
- // File deletion fails on windows if another thread is
- // concurrently reading the same file. So try a few times.
- //
- for (int attempts = 0; attempts < 10; attempts++) {
- if (ref.delete())
- return true;
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- return false;
- }
- }
- return false;
- }
-
- private boolean renameLock() {
- if (!fs.retryFailedLockFileCommit())
- return lck.renameTo(ref);
-
- // File renaming fails on windows if another thread is
- // concurrently reading the same file. So try a few times.
- //
- for (int attempts = 0; attempts < 10; attempts++) {
- if (lck.renameTo(ref))
- return true;
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- return false;
- }
- }
- return false;
}
private void saveStatInformation() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
index 687408e..ea80528 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
@@ -52,6 +52,9 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
+import java.nio.file.AtomicMoveNotSupportedException;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -433,16 +436,14 @@ public class ObjectDirectory extends FileObjectDatabase {
ObjectLoader openLooseObject(WindowCursor curs, AnyObjectId id)
throws IOException {
- try {
- File path = fileFor(id);
- FileInputStream in = new FileInputStream(path);
- try {
- unpackedObjectCache.add(id);
- return UnpackedObject.open(in, path, id, curs);
- } finally {
- in.close();
- }
+ File path = fileFor(id);
+ try (FileInputStream in = new FileInputStream(path)) {
+ unpackedObjectCache.add(id);
+ return UnpackedObject.open(in, path, id, curs);
} catch (FileNotFoundException noFile) {
+ if (path.exists()) {
+ throw noFile;
+ }
unpackedObjectCache.remove(id);
return null;
}
@@ -513,15 +514,14 @@ public class ObjectDirectory extends FileObjectDatabase {
private long getLooseObjectSize(WindowCursor curs, AnyObjectId id)
throws IOException {
- try {
- FileInputStream in = new FileInputStream(fileFor(id));
- try {
- unpackedObjectCache.add(id);
- return UnpackedObject.getSize(in, id, curs);
- } finally {
- in.close();
- }
+ File f = fileFor(id);
+ try (FileInputStream in = new FileInputStream(f)) {
+ unpackedObjectCache.add(id);
+ return UnpackedObject.getSize(in, id, curs);
} catch (FileNotFoundException noFile) {
+ if (f.exists()) {
+ throw noFile;
+ }
unpackedObjectCache.remove(id);
return -1;
}
@@ -554,22 +554,38 @@ public class ObjectDirectory extends FileObjectDatabase {
}
private void handlePackError(IOException e, PackFile p) {
- String tmpl;
+ String warnTmpl = null;
if ((e instanceof CorruptObjectException)
|| (e instanceof PackInvalidException)) {
- tmpl = JGitText.get().corruptPack;
+ warnTmpl = JGitText.get().corruptPack;
// Assume the pack is corrupted, and remove it from the list.
removePack(p);
} else if (e instanceof FileNotFoundException) {
- tmpl = JGitText.get().packWasDeleted;
+ if (p.getPackFile().exists()) {
+ warnTmpl = JGitText.get().packInaccessible;
+ } else {
+ warnTmpl = JGitText.get().packWasDeleted;
+ }
+ removePack(p);
+ } else if (FileUtils.isStaleFileHandle(e)) {
+ warnTmpl = JGitText.get().packHandleIsStale;
removePack(p);
+ }
+ if (warnTmpl != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(MessageFormat.format(warnTmpl,
+ p.getPackFile().getAbsolutePath()), e);
+ } else {
+ LOG.warn(MessageFormat.format(warnTmpl,
+ p.getPackFile().getAbsolutePath()));
+ }
} else {
- tmpl = JGitText.get().exceptionWhileReadingPack;
// Don't remove the pack from the list, as the error may be
// transient.
+ LOG.error(MessageFormat.format(
+ JGitText.get().exceptionWhileReadingPack, p.getPackFile()
+ .getAbsolutePath()), e);
}
- LOG.error(MessageFormat.format(tmpl,
- p.getPackFile().getAbsolutePath()), e);
}
@Override
@@ -587,7 +603,7 @@ public class ObjectDirectory extends FileObjectDatabase {
}
final File dst = fileFor(id);
- if (fs.exists(dst)) {
+ if (dst.exists()) {
// We want to be extra careful and avoid replacing an object
// that already exists. We can't be sure renameTo() would
// fail on all platforms if dst exists, so we check first.
@@ -595,10 +611,16 @@ public class ObjectDirectory extends FileObjectDatabase {
FileUtils.delete(tmp, FileUtils.RETRY);
return InsertLooseObjectResult.EXISTS_LOOSE;
}
- if (tmp.renameTo(dst)) {
+ try {
+ Files.move(tmp.toPath(), dst.toPath(),
+ StandardCopyOption.ATOMIC_MOVE);
dst.setReadOnly();
unpackedObjectCache.add(id);
return InsertLooseObjectResult.INSERTED;
+ } catch (AtomicMoveNotSupportedException e) {
+ LOG.error(e.getMessage(), e);
+ } catch (IOException e) {
+ // ignore
}
// Maybe the directory doesn't exist yet as the object
@@ -606,10 +628,16 @@ public class ObjectDirectory extends FileObjectDatabase {
// try the rename first as the directory likely does exist.
//
FileUtils.mkdir(dst.getParentFile(), true);
- if (tmp.renameTo(dst)) {
+ try {
+ Files.move(tmp.toPath(), dst.toPath(),
+ StandardCopyOption.ATOMIC_MOVE);
dst.setReadOnly();
unpackedObjectCache.add(id);
return InsertLooseObjectResult.INSERTED;
+ } catch (AtomicMoveNotSupportedException e) {
+ LOG.error(e.getMessage(), e);
+ } catch (IOException e) {
+ LOG.debug(e.getMessage(), e);
}
if (!createDuplicate && has(id)) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryInserter.java
index 812c899..fb41172 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryInserter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryInserter.java
@@ -55,10 +55,12 @@ import java.io.OutputStream;
import java.nio.channels.Channels;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
+import java.text.MessageFormat;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import org.eclipse.jgit.errors.ObjectWritingException;
+import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
@@ -123,7 +125,8 @@ class ObjectDirectoryInserter extends ObjectInserter {
}
final File dst = db.fileFor(id);
- throw new ObjectWritingException("Unable to create new object: " + dst);
+ throw new ObjectWritingException(MessageFormat
+ .format(JGitText.get().unableToCreateNewObject, dst));
}
@Override
@@ -142,7 +145,7 @@ class ObjectDirectoryInserter extends ObjectInserter {
}
@Override
- public void release() {
+ public void close() {
if (deflate != null) {
try {
deflate.end();
@@ -242,7 +245,7 @@ class ObjectDirectoryInserter extends ObjectInserter {
}
private static EOFException shortInput(long missing) {
- return new EOFException("Input did not match supplied length. "
- + missing + " bytes are missing.");
+ return new EOFException(MessageFormat.format(
+ JGitText.get().inputDidntMatchLength, Long.valueOf(missing)));
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryPackParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryPackParser.java
index a186b81..2e6c245 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryPackParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectoryPackParser.java
@@ -50,6 +50,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
+import java.nio.file.StandardCopyOption;
import java.security.MessageDigest;
import java.text.MessageFormat;
import java.util.Arrays;
@@ -366,11 +367,10 @@ public class ObjectDirectoryPackParser extends PackParser {
@Override
protected void onEndThinPack() throws IOException {
- final byte[] tailHash = this.tailDigest.digest();
final byte[] buf = buffer();
final MessageDigest origDigest = Constants.newMessageDigest();
- final MessageDigest tailDigest = Constants.newMessageDigest();
+ final MessageDigest tailDigest2 = Constants.newMessageDigest();
final MessageDigest packDigest = Constants.newMessageDigest();
long origRemaining = origEnd;
@@ -393,15 +393,15 @@ public class ObjectDirectoryPackParser extends PackParser {
origDigest.update(buf, 0, origCnt);
origRemaining -= origCnt;
if (origRemaining == 0)
- tailDigest.update(buf, origCnt, n - origCnt);
+ tailDigest2.update(buf, origCnt, n - origCnt);
} else
- tailDigest.update(buf, 0, n);
+ tailDigest2.update(buf, 0, n);
packDigest.update(buf, 0, n);
}
- if (!Arrays.equals(origDigest.digest(), origHash)
- || !Arrays.equals(tailDigest.digest(), tailHash))
+ if (!Arrays.equals(origDigest.digest(), origHash) || !Arrays
+ .equals(tailDigest2.digest(), this.tailDigest.digest()))
throw new IOException(
JGitText.get().packCorruptedWhileWritingToFilesystem);
@@ -477,20 +477,25 @@ public class ObjectDirectoryPackParser extends PackParser {
}
}
- if (!tmpPack.renameTo(finalPack)) {
+ try {
+ FileUtils.rename(tmpPack, finalPack,
+ StandardCopyOption.ATOMIC_MOVE);
+ } catch (IOException e) {
cleanupTemporaryFiles();
keep.unlock();
throw new IOException(MessageFormat.format(
- JGitText.get().cannotMovePackTo, finalPack));
+ JGitText.get().cannotMovePackTo, finalPack), e);
}
- if (!tmpIdx.renameTo(finalIdx)) {
+ try {
+ FileUtils.rename(tmpIdx, finalIdx, StandardCopyOption.ATOMIC_MOVE);
+ } catch (IOException e) {
cleanupTemporaryFiles();
keep.unlock();
if (!finalPack.delete())
finalPack.deleteOnExit();
throw new IOException(MessageFormat.format(
- JGitText.get().cannotMoveIndexTo, finalIdx));
+ JGitText.get().cannotMoveIndexTo, finalIdx), e);
}
try {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndex.java
index ae4de84..e743cb4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndex.java
@@ -193,4 +193,11 @@ public abstract class PackBitmapIndex {
* pack that this index was generated from.
*/
public abstract int getObjectCount();
+
+ /**
+ * Returns the number of bitmaps in this bitmap index.
+ *
+ * @return the number of bitmaps in this bitmap index.
+ */
+ public abstract int getBitmapCount();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java
index 05cd100..4ff09a1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexBuilder.java
@@ -44,7 +44,7 @@
package org.eclipse.jgit.internal.storage.file;
import java.text.MessageFormat;
-import java.util.Arrays;
+import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
@@ -73,30 +73,35 @@ public class PackBitmapIndexBuilder extends BasePackBitmapIndex {
private final EWAHCompressedBitmap trees;
private final EWAHCompressedBitmap blobs;
private final EWAHCompressedBitmap tags;
- private final ObjectToPack[] byOffset;
- private final BlockList<StoredBitmap>
+ private final BlockList<PositionEntry> byOffset;
+ final BlockList<StoredBitmap>
byAddOrder = new BlockList<StoredBitmap>();
- private final ObjectIdOwnerMap<PositionEntry>
+ final ObjectIdOwnerMap<PositionEntry>
positionEntries = new ObjectIdOwnerMap<PositionEntry>();
/**
* Creates a PackBitmapIndex used for building the contents of an index
* file.
*
- * @param byName
- * objects sorted by name.
+ * @param objects
+ * objects sorted by name. The list must be initially sorted by
+ * ObjectId (name); it will be resorted in place.
*/
- public PackBitmapIndexBuilder(List<ObjectToPack> byName) {
+ public PackBitmapIndexBuilder(List<ObjectToPack> objects) {
super(new ObjectIdOwnerMap<StoredBitmap>());
- byOffset = sortByOffset(byName);
+ byOffset = new BlockList<>(objects.size());
+ sortByOffsetAndIndex(byOffset, positionEntries, objects);
- int sizeInWords = Math.max(byOffset.length / 64, 4);
+ // 64 objects fit in a single long word (64 bits).
+ // On average a repository is 30% commits, 30% trees, 30% blobs.
+ // Initialize bitmap capacity for worst case to minimize growing.
+ int sizeInWords = Math.max(4, byOffset.size() / 64 / 3);
commits = new EWAHCompressedBitmap(sizeInWords);
trees = new EWAHCompressedBitmap(sizeInWords);
blobs = new EWAHCompressedBitmap(sizeInWords);
tags = new EWAHCompressedBitmap(sizeInWords);
- for (int i = 0; i < byOffset.length; i++) {
- int type = byOffset[i].getType();
+ for (int i = 0; i < objects.size(); i++) {
+ int type = objects.get(i).getType();
switch (type) {
case Constants.OBJ_COMMIT:
commits.set(i);
@@ -115,22 +120,39 @@ public class PackBitmapIndexBuilder extends BasePackBitmapIndex {
JGitText.get().badObjectType, String.valueOf(type)));
}
}
+ commits.trim();
+ trees.trim();
+ blobs.trim();
+ tags.trim();
}
- private ObjectToPack[] sortByOffset(List<ObjectToPack> entries) {
- ObjectToPack[] result = new ObjectToPack[entries.size()];
- for (int i = 0; i < result.length; i++) {
- result[i] = entries.get(i);
- positionEntries.add(new PositionEntry(result[i], i));
+ private static void sortByOffsetAndIndex(BlockList<PositionEntry> byOffset,
+ ObjectIdOwnerMap<PositionEntry> positionEntries,
+ List<ObjectToPack> entries) {
+ for (int i = 0; i < entries.size(); i++) {
+ positionEntries.add(new PositionEntry(entries.get(i), i));
}
- Arrays.sort(result, new Comparator<ObjectToPack>() {
+ Collections.sort(entries, new Comparator<ObjectToPack>() {
public int compare(ObjectToPack a, ObjectToPack b) {
return Long.signum(a.getOffset() - b.getOffset());
}
});
- for (int i = 0; i < result.length; i++)
- positionEntries.get(result[i]).offsetPosition = i;
- return result;
+ for (int i = 0; i < entries.size(); i++) {
+ PositionEntry e = positionEntries.get(entries.get(i));
+ e.offsetPosition = i;
+ byOffset.add(e);
+ }
+ }
+
+ /** @return set of objects included in the pack. */
+ public ObjectIdOwnerMap<ObjectIdOwnerMap.Entry> getObjectSet() {
+ ObjectIdOwnerMap<ObjectIdOwnerMap.Entry> r = new ObjectIdOwnerMap<>();
+ for (PositionEntry e : byOffset) {
+ r.add(new ObjectIdOwnerMap.Entry(e) {
+ // A new entry that copies the ObjectId
+ });
+ }
+ return r;
}
/**
@@ -168,6 +190,7 @@ public class PackBitmapIndexBuilder extends BasePackBitmapIndex {
*/
public void addBitmap(
AnyObjectId objectId, EWAHCompressedBitmap bitmap, int flags) {
+ bitmap.trim();
StoredBitmap result = new StoredBitmap(objectId, bitmap, null, flags);
getBitmaps().add(result);
byAddOrder.add(result);
@@ -199,7 +222,7 @@ public class PackBitmapIndexBuilder extends BasePackBitmapIndex {
@Override
public ObjectId getObject(int position) throws IllegalArgumentException {
- ObjectId objectId = byOffset[position];
+ ObjectId objectId = byOffset.get(position);
if (objectId == null)
throw new IllegalArgumentException();
return objectId;
@@ -230,7 +253,7 @@ public class PackBitmapIndexBuilder extends BasePackBitmapIndex {
return PackBitmapIndexV1.OPT_FULL;
}
- /** @return the number of bitmaps. */
+ @Override
public int getBitmapCount() {
return getBitmaps().size();
}
@@ -243,7 +266,7 @@ public class PackBitmapIndexBuilder extends BasePackBitmapIndex {
@Override
public int getObjectCount() {
- return byOffset.length;
+ return byOffset.size();
}
/** @return an iterator over the xor compressed entries. */
@@ -307,7 +330,7 @@ public class PackBitmapIndexBuilder extends BasePackBitmapIndex {
private final int xorOffset;
private final int flags;
- private StoredEntry(long objectId, EWAHCompressedBitmap bitmap,
+ StoredEntry(long objectId, EWAHCompressedBitmap bitmap,
int xorOffset, int flags) {
this.objectId = objectId;
this.bitmap = bitmap;
@@ -337,11 +360,11 @@ public class PackBitmapIndexBuilder extends BasePackBitmapIndex {
}
private static final class PositionEntry extends ObjectIdOwnerMap.Entry {
- private final int namePosition;
+ final int namePosition;
- private int offsetPosition;
+ int offsetPosition;
- private PositionEntry(AnyObjectId objectId, int namePosition) {
+ PositionEntry(AnyObjectId objectId, int namePosition) {
super(objectId);
this.namePosition = namePosition;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java
index 6b96b07..7cd68b6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java
@@ -66,7 +66,7 @@ public class PackBitmapIndexRemapper extends PackBitmapIndex
implements Iterable<PackBitmapIndexRemapper.Entry> {
private final BasePackBitmapIndex oldPackIndex;
- private final PackBitmapIndex newPackIndex;
+ final PackBitmapIndex newPackIndex;
private final ObjectIdOwnerMap<StoredBitmap> convertedBitmaps;
private final BitSet inflated;
private final int[] prevToNewMapping;
@@ -199,7 +199,7 @@ public class PackBitmapIndexRemapper extends PackBitmapIndex
public final class Entry extends ObjectId {
private final int flags;
- private Entry(AnyObjectId src, int flags) {
+ Entry(AnyObjectId src, int flags) {
super(src);
this.flags = flags;
}
@@ -209,4 +209,10 @@ public class PackBitmapIndexRemapper extends PackBitmapIndex
return flags;
}
}
+
+ @Override
+ public int getBitmapCount() {
+ // The count is only useful for the end index, not the remapper.
+ return 0;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexV1.java
index 6d944fd..a7ab00d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexV1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexV1.java
@@ -60,8 +60,7 @@ import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.NB;
/**
- * Support for the pack bitmap index v1 format, which contains experimental
- * support for bitmaps.
+ * Support for the pack bitmap index v1 format.
*
* @see PackBitmapIndex
*/
@@ -214,6 +213,11 @@ class PackBitmapIndexV1 extends BasePackBitmapIndex {
}
@Override
+ public int getBitmapCount() {
+ return bitmaps.size();
+ }
+
+ @Override
public boolean equals(Object o) {
// TODO(cranger): compare the pack checksum?
if (o instanceof PackBitmapIndexV1)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java
index eb22938..b385b8a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackFile.java
@@ -51,6 +51,7 @@ import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
+import java.io.InterruptedIOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel.MapMode;
@@ -118,7 +119,7 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
private int activeCopyRawData;
- private int packLastModified;
+ int packLastModified;
private volatile boolean invalid;
@@ -177,6 +178,9 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
packFile.getPath()));
}
loadedIdx = idx;
+ } catch (InterruptedIOException e) {
+ // don't invalidate the pack, we are interrupted from another thread
+ throw e;
} catch (IOException e) {
invalid = true;
throw e;
@@ -344,11 +348,11 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
return dstbuf;
}
- void copyPackAsIs(PackOutputStream out, boolean validate, WindowCursor curs)
+ void copyPackAsIs(PackOutputStream out, WindowCursor curs)
throws IOException {
// Pin the first window, this ensures the length is accurate.
curs.pin(this, 0);
- curs.copyPackAsIs(this, length, validate, out);
+ curs.copyPackAsIs(this, length, out);
}
final void copyAsIs(PackOutputStream out, LocalObjectToPack src,
@@ -388,22 +392,26 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
c = buf[headerCnt++] & 0xff;
} while ((c & 128) != 0);
if (validate) {
+ assert(crc1 != null && crc2 != null);
crc1.update(buf, 0, headerCnt);
crc2.update(buf, 0, headerCnt);
}
} else if (typeCode == Constants.OBJ_REF_DELTA) {
if (validate) {
+ assert(crc1 != null && crc2 != null);
crc1.update(buf, 0, headerCnt);
crc2.update(buf, 0, headerCnt);
}
readFully(src.offset + headerCnt, buf, 0, 20, curs);
if (validate) {
+ assert(crc1 != null && crc2 != null);
crc1.update(buf, 0, 20);
crc2.update(buf, 0, 20);
}
headerCnt += 20;
} else if (validate) {
+ assert(crc1 != null && crc2 != null);
crc1.update(buf, 0, headerCnt);
crc2.update(buf, 0, headerCnt);
}
@@ -420,6 +428,7 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
quickCopy = curs.quickCopy(this, dataOffset, dataLength);
if (validate && idx().hasCRC32Support()) {
+ assert(crc1 != null);
// Index has the CRC32 code cached, validate the object.
//
expectedCRC = idx().findCRC32(src);
@@ -452,6 +461,7 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
if (quickCopy != null) {
quickCopy.check(inf, tmp, dataOffset, (int) dataLength);
} else {
+ assert(crc1 != null);
long pos = dataOffset;
long cnt = dataLength;
while (cnt > 0) {
@@ -471,6 +481,7 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
JGitText.get().shortCompressedStreamAt,
Long.valueOf(src.offset)));
}
+ assert(crc1 != null);
expectedCRC = crc1.getValue();
} else {
expectedCRC = -1;
@@ -501,7 +512,7 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
// and we have it pinned. Write this out without copying.
//
out.writeHeader(src, inflatedLength);
- quickCopy.write(out, dataOffset, (int) dataLength, null);
+ quickCopy.write(out, dataOffset, (int) dataLength);
} else if (dataLength <= buf.length) {
// Tiny optimization: Lots of objects are very small deltas or
@@ -530,16 +541,21 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
while (cnt > 0) {
final int n = (int) Math.min(cnt, buf.length);
readFully(pos, buf, 0, n, curs);
- if (validate)
+ if (validate) {
+ assert(crc2 != null);
crc2.update(buf, 0, n);
+ }
out.write(buf, 0, n);
pos += n;
cnt -= n;
}
- if (validate && crc2.getValue() != expectedCRC) {
- throw new CorruptObjectException(MessageFormat.format(
- JGitText.get().objectAtHasBadZlibStream,
- Long.valueOf(src.offset), getPackFile()));
+ if (validate) {
+ assert(crc2 != null);
+ if (crc2.getValue() != expectedCRC) {
+ throw new CorruptObjectException(MessageFormat.format(
+ JGitText.get().objectAtHasBadZlibStream,
+ Long.valueOf(src.offset), getPackFile()));
+ }
}
}
}
@@ -604,22 +620,26 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
length = fd.length();
onOpenPack();
}
+ } catch (InterruptedIOException e) {
+ // don't invalidate the pack, we are interrupted from another thread
+ openFail(false);
+ throw e;
} catch (IOException ioe) {
- openFail();
+ openFail(true);
throw ioe;
} catch (RuntimeException re) {
- openFail();
+ openFail(true);
throw re;
} catch (Error re) {
- openFail();
+ openFail(true);
throw re;
}
}
- private void openFail() {
+ private void openFail(boolean invalidate) {
activeWindows = 0;
activeCopyRawData = 0;
- invalid = true;
+ invalid = invalidate;
doClose();
}
@@ -801,6 +821,7 @@ public class PackFile implements Iterable<PackIndex.MutableEntry> {
if (data == null)
throw new IOException(JGitText.get().inMemoryBufferLimitExceeded);
+ assert(delta != null);
do {
// Cache only the base immediately before desired object.
if (cached)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java
index 0040aea..f36bd4d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndex.java
@@ -60,6 +60,7 @@ import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdSet;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.NB;
@@ -72,7 +73,8 @@ import org.eclipse.jgit.util.NB;
* by ObjectId.
* </p>
*/
-public abstract class PackIndex implements Iterable<PackIndex.MutableEntry> {
+public abstract class PackIndex
+ implements Iterable<PackIndex.MutableEntry>, ObjectIdSet {
/**
* Open an existing pack <code>.idx</code> file for reading.
* <p>
@@ -166,6 +168,11 @@ public abstract class PackIndex implements Iterable<PackIndex.MutableEntry> {
return findOffset(id) != -1;
}
+ @Override
+ public boolean contains(AnyObjectId id) {
+ return findOffset(id) != -1;
+ }
+
/**
* Provide iterator that gives access to index entries. Note, that iterator
* returns reference to mutable object, the same reference in each call -
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java
index ab3297a..e5a729d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java
@@ -67,7 +67,7 @@ class PackIndexV1 extends PackIndex {
private final long[] idxHeader;
- private byte[][] idxdata;
+ byte[][] idxdata;
private long objectCnt;
@@ -233,9 +233,9 @@ class PackIndexV1 extends PackIndex {
}
private class IndexV1Iterator extends EntriesIterator {
- private int levelOne;
+ int levelOne;
- private int levelTwo;
+ int levelTwo;
@Override
protected MutableEntry initEntry() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java
index 70cf20f..d87336f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV2.java
@@ -75,16 +75,16 @@ class PackIndexV2 extends PackIndex {
private final long[] fanoutTable;
/** 256 arrays of contiguous object names. */
- private int[][] names;
+ int[][] names;
/** 256 arrays of the 32 bit offset data, matching {@link #names}. */
- private byte[][] offset32;
+ byte[][] offset32;
/** 256 arrays of the CRC-32 of objects, matching {@link #names}. */
private byte[][] crc32;
/** 64 bit offset table. */
- private byte[] offset64;
+ byte[] offset64;
PackIndexV2(final InputStream fd) throws IOException {
final byte[] fanoutRaw = new byte[4 * FANOUT];
@@ -232,7 +232,7 @@ class PackIndexV2 extends PackIndex {
final int levelOne = objId.getFirstByte();
final int levelTwo = binarySearchLevelTwo(objId, levelOne);
if (levelTwo == -1)
- throw new MissingObjectException(objId.copy(), "unknown");
+ throw new MissingObjectException(objId.copy(), "unknown"); //$NON-NLS-1$
return NB.decodeUInt32(crc32[levelOne], levelTwo << 2);
}
@@ -304,9 +304,9 @@ class PackIndexV2 extends PackIndex {
}
private class EntriesIteratorV2 extends EntriesIterator {
- private int levelOne;
+ int levelOne;
- private int levelTwo;
+ int levelTwo;
@Override
protected MutableEntry initEntry() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInputStream.java
index 9cb8349..154809b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackInputStream.java
@@ -80,6 +80,6 @@ class PackInputStream extends InputStream {
@Override
public void close() {
- wc.release();
+ wc.close();
}
}
\ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
index 0abf0c8..2c8e5f9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
@@ -73,6 +73,7 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
+import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.errors.InvalidObjectIdException;
import org.eclipse.jgit.errors.LockFailedException;
import org.eclipse.jgit.errors.MissingObjectException;
@@ -98,6 +99,8 @@ import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
import org.eclipse.jgit.util.RefList;
import org.eclipse.jgit.util.RefMap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Traditional file system based {@link RefDatabase}.
@@ -115,6 +118,9 @@ import org.eclipse.jgit.util.RefMap;
* overall size of a Git repository on disk.
*/
public class RefDirectory extends RefDatabase {
+ private final static Logger LOG = LoggerFactory
+ .getLogger(RefDirectory.class);
+
/** Magic string denoting the start of a symbolic reference file. */
public static final String SYMREF = "ref: "; //$NON-NLS-1$
@@ -133,7 +139,7 @@ public class RefDirectory extends RefDatabase {
private final File gitDir;
- private final File refsDir;
+ final File refsDir;
private final ReflogWriter logWriter;
@@ -150,7 +156,7 @@ public class RefDirectory extends RefDatabase {
private final AtomicReference<RefList<LooseRef>> looseRefs = new AtomicReference<RefList<LooseRef>>();
/** Immutable sorted list of packed references. */
- private final AtomicReference<PackedRefList> packedRefs = new AtomicReference<PackedRefList>();
+ final AtomicReference<PackedRefList> packedRefs = new AtomicReference<PackedRefList>();
/**
* Number of modifications made to this database.
@@ -257,6 +263,30 @@ public class RefDirectory extends RefDatabase {
}
@Override
+ public Ref exactRef(String name) throws IOException {
+ RefList<Ref> packed = getPackedRefs();
+ Ref ref;
+ try {
+ ref = readRef(name, packed);
+ if (ref != null) {
+ ref = resolve(ref, 0, null, null, packed);
+ }
+ } catch (IOException e) {
+ if (name.contains("/") //$NON-NLS-1$
+ || !(e.getCause() instanceof InvalidObjectIdException)) {
+ throw e;
+ }
+
+ // While looking for a ref outside of refs/ (e.g., 'config'), we
+ // found a non-ref file (e.g., a config file) instead. Treat this
+ // as a ref-not-found condition.
+ ref = null;
+ }
+ fireRefsChanged();
+ return ref;
+ }
+
+ @Override
public Ref getRef(final String needle) throws IOException {
final RefList<Ref> packed = getPackedRefs();
Ref ref = null;
@@ -265,6 +295,8 @@ public class RefDirectory extends RefDatabase {
ref = readRef(prefix + needle, packed);
if (ref != null) {
ref = resolve(ref, 0, null, null, packed);
+ }
+ if (ref != null) {
break;
}
} catch (IOException e) {
@@ -477,8 +509,7 @@ public class RefDirectory extends RefDatabase {
private ObjectIdRef doPeel(final Ref leaf) throws MissingObjectException,
IOException {
- RevWalk rw = new RevWalk(getRepository());
- try {
+ try (RevWalk rw = new RevWalk(getRepository())) {
RevObject obj = rw.parseAny(leaf.getObjectId());
if (obj instanceof RevTag) {
return new ObjectIdRef.PeeledTag(leaf.getStorage(), leaf
@@ -487,8 +518,6 @@ public class RefDirectory extends RefDatabase {
return new ObjectIdRef.PeeledNonTag(leaf.getStorage(), leaf
.getName(), leaf.getObjectId());
}
- } finally {
- rw.release();
}
}
@@ -687,16 +716,20 @@ public class RefDirectory extends RefDatabase {
*/
private Ref peeledPackedRef(Ref f)
throws MissingObjectException, IOException {
- if (f.getStorage().isPacked() && f.isPeeled())
+ if (f.getStorage().isPacked() && f.isPeeled()) {
return f;
- if (!f.isPeeled())
+ }
+ if (!f.isPeeled()) {
f = peel(f);
- if (f.getPeeledObjectId() != null)
+ }
+ ObjectId peeledObjectId = f.getPeeledObjectId();
+ if (peeledObjectId != null) {
return new ObjectIdRef.PeeledTag(PACKED, f.getName(),
- f.getObjectId(), f.getPeeledObjectId());
- else
+ f.getObjectId(), peeledObjectId);
+ } else {
return new ObjectIdRef.PeeledNonTag(PACKED, f.getName(),
f.getObjectId());
+ }
}
void log(final RefUpdate update, final String msg, final boolean deref)
@@ -749,22 +782,40 @@ public class RefDirectory extends RefDatabase {
}
private PackedRefList readPackedRefs() throws IOException {
- final FileSnapshot snapshot = FileSnapshot.save(packedRefsFile);
- final BufferedReader br;
- final MessageDigest digest = Constants.newMessageDigest();
- try {
- br = new BufferedReader(new InputStreamReader(
- new DigestInputStream(new FileInputStream(packedRefsFile),
- digest), CHARSET));
- } catch (FileNotFoundException noPackedRefs) {
- // Ignore it and leave the new list empty.
- return PackedRefList.NO_PACKED_REFS;
- }
- try {
- return new PackedRefList(parsePackedRefs(br), snapshot,
- ObjectId.fromRaw(digest.digest()));
- } finally {
- br.close();
+ int maxStaleRetries = 5;
+ int retries = 0;
+ while (true) {
+ final FileSnapshot snapshot = FileSnapshot.save(packedRefsFile);
+ final BufferedReader br;
+ final MessageDigest digest = Constants.newMessageDigest();
+ try {
+ br = new BufferedReader(new InputStreamReader(
+ new DigestInputStream(new FileInputStream(packedRefsFile),
+ digest), CHARSET));
+ } catch (FileNotFoundException noPackedRefs) {
+ if (packedRefsFile.exists()) {
+ throw noPackedRefs;
+ }
+ // Ignore it and leave the new list empty.
+ return PackedRefList.NO_PACKED_REFS;
+ }
+ try {
+ return new PackedRefList(parsePackedRefs(br), snapshot,
+ ObjectId.fromRaw(digest.digest()));
+ } catch (IOException e) {
+ if (FileUtils.isStaleFileHandle(e) && retries < maxStaleRetries) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(MessageFormat.format(
+ JGitText.get().packedRefsHandleIsStale,
+ Integer.valueOf(retries)), e);
+ }
+ retries++;
+ continue;
+ }
+ throw e;
+ } finally {
+ br.close();
+ }
}
}
@@ -884,8 +935,7 @@ public class RefDirectory extends RefDatabase {
return n;
}
- @SuppressWarnings("null")
- private LooseRef scanRef(LooseRef ref, String name) throws IOException {
+ LooseRef scanRef(LooseRef ref, String name) throws IOException {
final File path = fileFor(name);
FileSnapshot currentSnapshot = null;
@@ -902,7 +952,10 @@ public class RefDirectory extends RefDatabase {
try {
buf = IO.readSome(path, limit);
} catch (FileNotFoundException noFile) {
- return null; // doesn't exist; not a reference.
+ if (path.exists() && path.isFile()) {
+ throw noFile;
+ }
+ return null; // doesn't exist or no file; not a reference.
}
int n = buf.length;
@@ -923,6 +976,7 @@ public class RefDirectory extends RefDatabase {
final String target = RawParseUtils.decode(buf, 5, n);
if (ref != null && ref.isSymbolic()
&& ref.getTarget().getName().equals(target)) {
+ assert(currentSnapshot != null);
currentSnapshot.setClean(otherSnapshot);
return ref;
}
@@ -936,7 +990,8 @@ public class RefDirectory extends RefDatabase {
try {
id = ObjectId.fromString(buf, 0);
if (ref != null && !ref.isSymbolic()
- && ref.getTarget().getObjectId().equals(id)) {
+ && id.equals(ref.getTarget().getObjectId())) {
+ assert(currentSnapshot != null);
currentSnapshot.setClean(otherSnapshot);
return ref;
}
@@ -1053,8 +1108,8 @@ public class RefDirectory extends RefDatabase {
implements LooseRef {
private final FileSnapshot snapShot;
- LoosePeeledTag(FileSnapshot snapshot, String refName, ObjectId id,
- ObjectId p) {
+ LoosePeeledTag(FileSnapshot snapshot, @NonNull String refName,
+ @NonNull ObjectId id, @NonNull ObjectId p) {
super(LOOSE, refName, id, p);
this.snapShot = snapshot;
}
@@ -1072,7 +1127,8 @@ public class RefDirectory extends RefDatabase {
implements LooseRef {
private final FileSnapshot snapShot;
- LooseNonTag(FileSnapshot snapshot, String refName, ObjectId id) {
+ LooseNonTag(FileSnapshot snapshot, @NonNull String refName,
+ @NonNull ObjectId id) {
super(LOOSE, refName, id);
this.snapShot = snapshot;
}
@@ -1090,7 +1146,8 @@ public class RefDirectory extends RefDatabase {
implements LooseRef {
private FileSnapshot snapShot;
- LooseUnpeeled(FileSnapshot snapShot, String refName, ObjectId id) {
+ LooseUnpeeled(FileSnapshot snapShot, @NonNull String refName,
+ @NonNull ObjectId id) {
super(LOOSE, refName, id);
this.snapShot = snapShot;
}
@@ -1099,13 +1156,24 @@ public class RefDirectory extends RefDatabase {
return snapShot;
}
+ @NonNull
+ @Override
+ public ObjectId getObjectId() {
+ ObjectId id = super.getObjectId();
+ assert id != null; // checked in constructor
+ return id;
+ }
+
public LooseRef peel(ObjectIdRef newLeaf) {
- if (newLeaf.getPeeledObjectId() != null)
+ ObjectId peeledObjectId = newLeaf.getPeeledObjectId();
+ ObjectId objectId = getObjectId();
+ if (peeledObjectId != null) {
return new LoosePeeledTag(snapShot, getName(),
- getObjectId(), newLeaf.getPeeledObjectId());
- else
+ objectId, peeledObjectId);
+ } else {
return new LooseNonTag(snapShot, getName(),
- getObjectId());
+ objectId);
+ }
}
}
@@ -1113,7 +1181,8 @@ public class RefDirectory extends RefDatabase {
LooseRef {
private final FileSnapshot snapShot;
- LooseSymbolicRef(FileSnapshot snapshot, String refName, Ref target) {
+ LooseSymbolicRef(FileSnapshot snapshot, @NonNull String refName,
+ @NonNull Ref target) {
super(refName, target);
this.snapShot = snapshot;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryRename.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryRename.java
index 878fc19..4b803a5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryRename.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryRename.java
@@ -46,6 +46,8 @@ package org.eclipse.jgit.internal.storage.file;
import java.io.File;
import java.io.IOException;
+import java.nio.file.AtomicMoveNotSupportedException;
+import java.nio.file.StandardCopyOption;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
@@ -54,6 +56,8 @@ import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.util.FileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Rename any reference stored by {@link RefDirectory}.
@@ -66,6 +70,9 @@ import org.eclipse.jgit.util.FileUtils;
* directory that happens to match the source name.
*/
class RefDirectoryRename extends RefRename {
+ private static final Logger LOG = LoggerFactory
+ .getLogger(RefDirectoryRename.class);
+
private final RefDirectory refdb;
/**
@@ -96,8 +103,7 @@ class RefDirectoryRename extends RefRename {
objId = source.getOldObjectId();
updateHEAD = needToUpdateHEAD();
tmp = refdb.newTemporaryUpdate();
- final RevWalk rw = new RevWalk(refdb.getRepository());
- try {
+ try (final RevWalk rw = new RevWalk(refdb.getRepository())) {
// First backup the source so its never unreachable.
tmp.setNewObjectId(objId);
tmp.setForceUpdate(true);
@@ -178,7 +184,6 @@ class RefDirectoryRename extends RefRename {
} catch (IOException err) {
FileUtils.delete(refdb.fileFor(tmp.getName()));
}
- rw.release();
}
}
@@ -203,13 +208,25 @@ class RefDirectoryRename extends RefRename {
}
private static boolean rename(File src, File dst) {
- if (src.renameTo(dst))
+ try {
+ FileUtils.rename(src, dst, StandardCopyOption.ATOMIC_MOVE);
return true;
+ } catch (AtomicMoveNotSupportedException e) {
+ LOG.error(e.getMessage(), e);
+ } catch (IOException e) {
+ // ignore
+ }
File dir = dst.getParentFile();
if ((dir.exists() || !dir.mkdirs()) && !dir.isDirectory())
return false;
- return src.renameTo(dst);
+ try {
+ FileUtils.rename(src, dst, StandardCopyOption.ATOMIC_MOVE);
+ return true;
+ } catch (IOException e) {
+ LOG.error(e.getMessage(), e);
+ return false;
+ }
}
private boolean linkHEAD(RefUpdate target) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryUpdate.java
index 8ad7ad2..7858ee1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectoryUpdate.java
@@ -128,11 +128,11 @@ class RefDirectoryUpdate extends RefUpdate {
private String toResultString(final Result status) {
switch (status) {
case FORCED:
- return "forced-update";
+ return "forced-update"; //$NON-NLS-1$
case FAST_FORWARD:
- return "fast forward";
+ return "fast forward"; //$NON-NLS-1$
case NEW:
- return "created";
+ return "created"; //$NON-NLS-1$
default:
return null;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java
index dadc631..2f583b2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java
@@ -96,6 +96,9 @@ class ReflogReaderImpl implements ReflogReader {
try {
log = IO.readFully(logName);
} catch (FileNotFoundException e) {
+ if (logName.exists()) {
+ throw e;
+ }
return null;
}
@@ -118,6 +121,9 @@ class ReflogReaderImpl implements ReflogReader {
try {
log = IO.readFully(logName);
} catch (FileNotFoundException e) {
+ if (logName.exists()) {
+ throw e;
+ }
return Collections.emptyList();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java
index cb95a76..a027437 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java
@@ -91,11 +91,8 @@ public class UnpackedObject {
*/
public static ObjectLoader parse(byte[] raw, AnyObjectId id)
throws IOException {
- WindowCursor wc = new WindowCursor(null);
- try {
+ try (WindowCursor wc = new WindowCursor(null)) {
return open(new ByteArrayInputStream(raw), null, id, wc);
- } finally {
- wc.release();
}
}
@@ -235,7 +232,7 @@ public class UnpackedObject {
}
}
- private static void checkValidEndOfStream(InputStream in, Inflater inf,
+ static void checkValidEndOfStream(InputStream in, Inflater inf,
AnyObjectId id, final byte[] buf) throws IOException,
CorruptObjectException {
for (;;) {
@@ -269,7 +266,7 @@ public class UnpackedObject {
}
}
- private static boolean isStandardFormat(final byte[] hdr) {
+ static boolean isStandardFormat(final byte[] hdr) {
/*
* We must determine if the buffer contains the standard
* zlib-deflated stream or the experimental format based
@@ -301,7 +298,7 @@ public class UnpackedObject {
return (fb & 0x8f) == 0x08 && (((fb << 8) | hdr[1] & 0xff) % 31) == 0;
}
- private static InputStream inflate(final InputStream in, final long size,
+ static InputStream inflate(final InputStream in, final long size,
final ObjectId id) {
final Inflater inf = InflaterCache.get();
return new InflaterInputStream(in, inf) {
@@ -337,11 +334,11 @@ public class UnpackedObject {
return new InflaterInputStream(in, inf, BUFFER_SIZE);
}
- private static BufferedInputStream buffer(InputStream in) {
+ static BufferedInputStream buffer(InputStream in) {
return new BufferedInputStream(in, BUFFER_SIZE);
}
- private static int readSome(InputStream in, final byte[] hdr, int off,
+ static int readSome(InputStream in, final byte[] hdr, int off,
int cnt) throws IOException {
int avail = 0;
while (0 < cnt) {
@@ -366,7 +363,7 @@ public class UnpackedObject {
private final FileObjectDatabase source;
- private LargeObject(int type, long size, File path, AnyObjectId id,
+ LargeObject(int type, long size, File path, AnyObjectId id,
FileObjectDatabase db) {
this.type = type;
this.size = size;
@@ -402,6 +399,9 @@ public class UnpackedObject {
try {
in = buffer(new FileInputStream(path));
} catch (FileNotFoundException gone) {
+ if (path.exists()) {
+ throw gone;
+ }
// If the loose file no longer exists, it may have been
// moved into a pack file in the mean time. Try again
// to locate the object.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java
index 85c3c74..a555e10 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCursor.java
@@ -45,9 +45,6 @@
package org.eclipse.jgit.internal.storage.file;
import java.io.IOException;
-import java.security.MessageDigest;
-import java.text.MessageFormat;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
@@ -145,7 +142,8 @@ final class WindowCursor extends ObjectReader implements ObjectReuseAsIs {
final ObjectLoader ldr = db.openObject(this, objectId);
if (ldr == null) {
if (typeHint == OBJ_ANY)
- throw new MissingObjectException(objectId.copy(), "unknown");
+ throw new MissingObjectException(objectId.copy(),
+ JGitText.get().unknownObjectType2);
throw new MissingObjectException(objectId.copy(), typeHint);
}
if (typeHint != OBJ_ANY && ldr.getType() != typeHint)
@@ -164,7 +162,8 @@ final class WindowCursor extends ObjectReader implements ObjectReuseAsIs {
long sz = db.getObjectSize(this, objectId);
if (sz < 0) {
if (typeHint == OBJ_ANY)
- throw new MissingObjectException(objectId.copy(), "unknown");
+ throw new MissingObjectException(objectId.copy(),
+ JGitText.get().unknownObjectType2);
throw new MissingObjectException(objectId.copy(), typeHint);
}
return sz;
@@ -232,27 +231,13 @@ final class WindowCursor extends ObjectReader implements ObjectReuseAsIs {
return cnt - need;
}
- public void copyPackAsIs(PackOutputStream out, CachedPack pack,
- boolean validate) throws IOException {
- ((LocalCachedPack) pack).copyAsIs(out, validate, this);
+ public void copyPackAsIs(PackOutputStream out, CachedPack pack)
+ throws IOException {
+ ((LocalCachedPack) pack).copyAsIs(out, this);
}
- void copyPackAsIs(final PackFile pack, final long length, boolean validate,
+ void copyPackAsIs(final PackFile pack, final long length,
final PackOutputStream out) throws IOException {
- MessageDigest md = null;
- if (validate) {
- md = Constants.newMessageDigest();
- byte[] buf = out.getCopyBuffer();
- pin(pack, 0);
- if (window.copy(0, buf, 0, 12) != 12) {
- pack.setInvalid();
- throw new IOException(MessageFormat.format(
- JGitText.get().packfileIsTruncated, pack.getPackFile()
- .getPath()));
- }
- md.update(buf, 0, 12);
- }
-
long position = 12;
long remaining = length - (12 + 20);
while (0 < remaining) {
@@ -260,29 +245,10 @@ final class WindowCursor extends ObjectReader implements ObjectReuseAsIs {
int ptr = (int) (position - window.start);
int n = (int) Math.min(window.size() - ptr, remaining);
- window.write(out, position, n, md);
+ window.write(out, position, n);
position += n;
remaining -= n;
}
-
- if (md != null) {
- byte[] buf = new byte[20];
- byte[] actHash = md.digest();
-
- pin(pack, position);
- if (window.copy(position, buf, 0, 20) != 20) {
- pack.setInvalid();
- throw new IOException(MessageFormat.format(
- JGitText.get().packfileIsTruncated, pack.getPackFile()
- .getPath()));
- }
- if (!Arrays.equals(actHash, buf)) {
- pack.setInvalid();
- throw new IOException(MessageFormat.format(
- JGitText.get().packfileCorruptionDetected, pack
- .getPackFile().getPath()));
- }
- }
}
/**
@@ -364,7 +330,8 @@ final class WindowCursor extends ObjectReader implements ObjectReuseAsIs {
}
/** Release the current window cursor. */
- public void release() {
+ @Override
+ public void close() {
window = null;
baseCache = null;
try {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BinaryDelta.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BinaryDelta.java
index 97092cd..2565931 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BinaryDelta.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/BinaryDelta.java
@@ -255,8 +255,13 @@ public class BinaryDelta {
shift += 7;
} while ((c & 0x80) != 0);
- if (includeHeader)
- r.append("DELTA( BASE=" + baseLen + " RESULT=" + resLen + " )\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ if (includeHeader) {
+ r.append("DELTA( BASE="); //$NON-NLS-1$
+ r.append(baseLen);
+ r.append(" RESULT="); //$NON-NLS-1$
+ r.append(resLen);
+ r.append(" )\n"); //$NON-NLS-1$
+ }
while (deltaPtr < delta.length) {
final int cmd = delta[deltaPtr++] & 0xff;
@@ -285,8 +290,11 @@ public class BinaryDelta {
if (copySize == 0)
copySize = 0x10000;
- r.append(" COPY (" + copyOffset + ", " + copySize + ")\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
-
+ r.append(" COPY ("); //$NON-NLS-1$
+ r.append(copyOffset);
+ r.append(", "); //$NON-NLS-1$
+ r.append(copySize);
+ r.append(")\n"); //$NON-NLS-1$
} else if (cmd != 0) {
// Anything else the data is literal within the delta
// itself.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaTask.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaTask.java
index 9534053..4292742 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaTask.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/DeltaTask.java
@@ -73,7 +73,7 @@ final class DeltaTask implements Callable<Object> {
final int endIndex;
private long totalWeight;
- private long bytesPerUnit;
+ long bytesPerUnit;
Block(int threads, PackConfig config, ObjectReader reader,
DeltaCache dc, ThreadSafeProgressMonitor pm,
@@ -110,10 +110,12 @@ final class DeltaTask implements Callable<Object> {
maxWork = s.size();
}
}
- if (maxTask == null)
+ if (maxTask == null) {
return null;
- if (maxTask.tryStealWork(maxSlice))
+ }
+ if (maxTask.tryStealWork(maxSlice)) {
return forThread.initWindow(maxSlice);
+ }
}
}
@@ -138,26 +140,30 @@ final class DeltaTask implements Callable<Object> {
for (; w < weightPerThread && i < endIndex;) {
if (nextTop < topPaths.size()
&& i == topPaths.get(nextTop).slice.beginIndex) {
- if (s < i)
+ if (s < i) {
task.add(new Slice(s, i));
+ }
s = i = topPaths.get(nextTop++).slice.endIndex;
- } else
- w += list[i++].getWeight();
+ } else {
+ w += getAdjustedWeight(list[i++]);
+ }
}
// Round up the slice to the end of a path.
if (s < i) {
int h = list[i - 1].getPathHash();
while (i < endIndex) {
- if (h == list[i].getPathHash())
+ if (h == list[i].getPathHash()) {
i++;
- else
+ } else {
break;
+ }
}
task.add(new Slice(s, i));
}
- if (!task.slices.isEmpty())
+ if (!task.slices.isEmpty()) {
tasks.add(task);
+ }
}
while (topPathItr.hasNext()) {
WeightedPath p = topPathItr.next();
@@ -174,8 +180,8 @@ final class DeltaTask implements Callable<Object> {
threads);
int cp = beginIndex;
int ch = list[cp].getPathHash();
- long cw = list[cp].getWeight();
- totalWeight = list[cp].getWeight();
+ long cw = getAdjustedWeight(list[cp]);
+ totalWeight = cw;
for (int i = cp + 1; i < endIndex; i++) {
ObjectToPack o = list[i];
@@ -184,24 +190,25 @@ final class DeltaTask implements Callable<Object> {
if (topPaths.size() < threads) {
Slice s = new Slice(cp, i);
topPaths.add(new WeightedPath(cw, s));
- if (topPaths.size() == threads)
+ if (topPaths.size() == threads) {
Collections.sort(topPaths);
+ }
} else if (topPaths.get(0).weight < cw) {
Slice s = new Slice(cp, i);
WeightedPath p = new WeightedPath(cw, s);
topPaths.set(0, p);
- if (p.compareTo(topPaths.get(1)) > 0)
+ if (p.compareTo(topPaths.get(1)) > 0) {
Collections.sort(topPaths);
+ }
}
}
cp = i;
ch = o.getPathHash();
cw = 0;
}
- if (o.isEdge() || o.doNotAttemptDelta())
- continue;
- cw += o.getWeight();
- totalWeight += o.getWeight();
+ int weight = getAdjustedWeight(o);
+ cw += weight;
+ totalWeight += weight;
}
// Sort by starting index to identify gaps later.
@@ -212,12 +219,22 @@ final class DeltaTask implements Callable<Object> {
});
bytesPerUnit = 1;
- while (MAX_METER <= (totalWeight / bytesPerUnit))
+ while (MAX_METER <= (totalWeight / bytesPerUnit)) {
bytesPerUnit <<= 10;
+ }
return topPaths;
}
}
+ static int getAdjustedWeight(ObjectToPack o) {
+ // Edge objects and those with reused deltas do not need to be
+ // compressed. For compression calculations, ignore their weights.
+ if (o.isEdge() || o.doNotAttemptDelta()) {
+ return 0;
+ }
+ return o.getWeight();
+ }
+
static final class WeightedPath implements Comparable<WeightedPath> {
final long weight;
final Slice slice;
@@ -229,8 +246,9 @@ final class DeltaTask implements Callable<Object> {
public int compareTo(WeightedPath o) {
int cmp = Long.signum(weight - o.weight);
- if (cmp != 0)
+ if (cmp != 0) {
return cmp;
+ }
return slice.beginIndex - o.slice.beginIndex;
}
}
@@ -250,7 +268,7 @@ final class DeltaTask implements Callable<Object> {
}
private final Block block;
- private final LinkedList<Slice> slices;
+ final LinkedList<Slice> slices;
private ObjectReader or;
private DeltaWindow dw;
@@ -278,17 +296,19 @@ final class DeltaTask implements Callable<Object> {
DeltaWindow w;
for (;;) {
synchronized (this) {
- if (slices.isEmpty())
+ if (slices.isEmpty()) {
break;
+ }
w = initWindow(slices.removeFirst());
}
runWindow(w);
}
- while ((w = block.stealWork(this)) != null)
+ while ((w = block.stealWork(this)) != null) {
runWindow(w);
+ }
} finally {
block.pm.endWorker();
- or.release();
+ or.close();
or = null;
}
return null;
@@ -315,8 +335,9 @@ final class DeltaTask implements Callable<Object> {
}
synchronized Slice remaining() {
- if (!slices.isEmpty())
+ if (!slices.isEmpty()) {
return slices.getLast();
+ }
DeltaWindow d = dw;
return d != null ? d.remaining() : null;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectReuseAsIs.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectReuseAsIs.java
index 00b6b65..2e5d599 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectReuseAsIs.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectReuseAsIs.java
@@ -209,16 +209,11 @@ public interface ObjectReuseAsIs {
* stream to append the pack onto.
* @param pack
* the cached pack to send.
- * @param validate
- * if true the representation must be validated and not be
- * corrupt before being reused. If false, validation may be
- * skipped as it will be performed elsewhere in the processing
- * pipeline.
* @throws IOException
* the pack cannot be read, or stream did not accept a write.
*/
- public abstract void copyPackAsIs(PackOutputStream out, CachedPack pack,
- boolean validate) throws IOException;
+ public abstract void copyPackAsIs(PackOutputStream out, CachedPack pack)
+ throws IOException;
/**
* Obtain the available cached packs that match the bitmap and update
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectToPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectToPack.java
index 33cb6af..a089657 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectToPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/ObjectToPack.java
@@ -390,15 +390,15 @@ public class ObjectToPack extends PackedObjectInfo {
if (isEdge())
buf.append(" edge");
if (getDeltaDepth() > 0)
- buf.append(" depth=" + getDeltaDepth());
+ buf.append(" depth=").append(getDeltaDepth());
if (isDeltaRepresentation()) {
if (getDeltaBase() != null)
- buf.append(" base=inpack:" + getDeltaBase().name());
+ buf.append(" base=inpack:").append(getDeltaBase().name());
else
- buf.append(" base=edge:" + getDeltaBaseId().name());
+ buf.append(" base=edge:").append(getDeltaBaseId().name());
}
if (isWritten())
- buf.append(" offset=" + getOffset());
+ buf.append(" offset=").append(getOffset());
buf.append("]");
return buf.toString();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
index 1649347..525f9ae 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
@@ -80,6 +80,7 @@ import java.util.zip.CheckedOutputStream;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
+import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.LargeObjectException;
@@ -99,6 +100,7 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdOwnerMap;
+import org.eclipse.jgit.lib.ObjectIdSet;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.ProgressMonitor;
@@ -114,6 +116,9 @@ import org.eclipse.jgit.revwalk.RevSort;
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.storage.pack.PackConfig;
+import org.eclipse.jgit.storage.pack.PackStatistics;
+import org.eclipse.jgit.transport.ObjectCountCallback;
+import org.eclipse.jgit.transport.WriteAbortedException;
import org.eclipse.jgit.util.BlockList;
import org.eclipse.jgit.util.TemporaryBuffer;
@@ -133,12 +138,14 @@ import org.eclipse.jgit.util.TemporaryBuffer;
* order of objects in pack</li>
* </ul>
* <p>
- * Typical usage consists of creating instance intended for some pack,
- * configuring options, preparing the list of objects by calling
- * {@link #preparePack(Iterator)} or
- * {@link #preparePack(ProgressMonitor, Collection, Collection)}, and finally
- * producing the stream with
- * {@link #writePack(ProgressMonitor, ProgressMonitor, OutputStream)}.
+ * Typical usage consists of creating an instance, configuring options,
+ * preparing the list of objects by calling {@link #preparePack(Iterator)} or
+ * {@link #preparePack(ProgressMonitor, Set, Set)}, and streaming with
+ * {@link #writePack(ProgressMonitor, ProgressMonitor, OutputStream)}. If the
+ * pack is being stored as a file the matching index can be written out after
+ * writing the pack by {@link #writeIndex(OutputStream)}. An optional bitmap
+ * index can be made by calling {@link #prepareBitmapIndex(ProgressMonitor)}
+ * followed by {@link #writeBitmapIndex(OutputStream)}.
* </p>
* <p>
* Class provide set of configurable options and {@link ProgressMonitor}
@@ -147,25 +154,17 @@ import org.eclipse.jgit.util.TemporaryBuffer;
* relies only on deltas and objects reuse.
* </p>
* <p>
- * This class is not thread safe, it is intended to be used in one thread, with
- * one instance per created pack. Subsequent calls to writePack result in
- * undefined behavior.
+ * This class is not thread safe. It is intended to be used in one thread as a
+ * single pass to produce one pack. Invoking methods multiple times or out of
+ * order is not supported as internal data structures are destroyed during
+ * certain phases to save memory when packing large repositories.
* </p>
*/
-public class PackWriter {
+public class PackWriter implements AutoCloseable {
private static final int PACK_VERSION_GENERATED = 2;
- /** A collection of object ids. */
- public interface ObjectIdSet {
- /**
- * Returns true if the objectId is contained within the collection.
- *
- * @param objectId
- * the objectId to find
- * @return whether the collection contains the objectId.
- */
- boolean contains(AnyObjectId objectId);
- }
+ /** Empty set of objects for {@code preparePack()}. */
+ public static Set<ObjectId> NONE = Collections.emptySet();
private static final Map<WeakReference<PackWriter>, Boolean> instances =
new ConcurrentHashMap<WeakReference<PackWriter>, Boolean>();
@@ -212,7 +211,7 @@ public class PackWriter {
}
@SuppressWarnings("unchecked")
- private final BlockList<ObjectToPack> objectsLists[] = new BlockList[OBJ_TAG + 1];
+ BlockList<ObjectToPack> objectsLists[] = new BlockList[OBJ_TAG + 1];
{
objectsLists[OBJ_COMMIT] = new BlockList<ObjectToPack>();
objectsLists[OBJ_TREE] = new BlockList<ObjectToPack>();
@@ -220,7 +219,7 @@ public class PackWriter {
objectsLists[OBJ_TAG] = new BlockList<ObjectToPack>();
}
- private final ObjectIdOwnerMap<ObjectToPack> objectsMap = new ObjectIdOwnerMap<ObjectToPack>();
+ private ObjectIdOwnerMap<ObjectToPack> objectsMap = new ObjectIdOwnerMap<ObjectToPack>();
// edge objects for thin packs
private List<ObjectToPack> edgeObjects = new BlockList<ObjectToPack>();
@@ -243,15 +242,15 @@ public class PackWriter {
/** {@link #reader} recast to the reuse interface, if it supports it. */
private final ObjectReuseAsIs reuseSupport;
- private final PackConfig config;
+ final PackConfig config;
- private final Statistics stats;
+ private final PackStatistics.Accumulator stats;
private final MutableState state;
private final WeakReference<PackWriter> selfRef;
- private Statistics.ObjectType typeStats;
+ private PackStatistics.ObjectType.Accumulator typeStats;
private List<ObjectToPack> sortedByName;
@@ -289,11 +288,13 @@ public class PackWriter {
private CRC32 crc32;
+ private ObjectCountCallback callback;
+
/**
* Create writer for specified repository.
* <p>
* Objects for packing are specified in {@link #preparePack(Iterator)} or
- * {@link #preparePack(ProgressMonitor, Collection, Collection)}.
+ * {@link #preparePack(ProgressMonitor, Set, Set)}.
*
* @param repo
* repository where objects are stored.
@@ -306,7 +307,7 @@ public class PackWriter {
* Create a writer to load objects from the specified reader.
* <p>
* Objects for packing are specified in {@link #preparePack(Iterator)} or
- * {@link #preparePack(ProgressMonitor, Collection, Collection)}.
+ * {@link #preparePack(ProgressMonitor, Set, Set)}.
*
* @param reader
* reader to read from the repository with.
@@ -319,7 +320,7 @@ public class PackWriter {
* Create writer for specified repository.
* <p>
* Objects for packing are specified in {@link #preparePack(Iterator)} or
- * {@link #preparePack(ProgressMonitor, Collection, Collection)}.
+ * {@link #preparePack(ProgressMonitor, Set, Set)}.
*
* @param repo
* repository where objects are stored.
@@ -334,7 +335,7 @@ public class PackWriter {
* Create writer with a specified configuration.
* <p>
* Objects for packing are specified in {@link #preparePack(Iterator)} or
- * {@link #preparePack(ProgressMonitor, Collection, Collection)}.
+ * {@link #preparePack(ProgressMonitor, Set, Set)}.
*
* @param config
* configuration for the pack writer.
@@ -352,13 +353,42 @@ public class PackWriter {
deltaBaseAsOffset = config.isDeltaBaseAsOffset();
reuseDeltas = config.isReuseDeltas();
reuseValidate = true; // be paranoid by default
- stats = new Statistics();
+ stats = new PackStatistics.Accumulator();
state = new MutableState();
selfRef = new WeakReference<PackWriter>(this);
instances.put(selfRef, Boolean.TRUE);
}
/**
+ * Set the {@code ObjectCountCallback}.
+ * <p>
+ * It should be set before calling
+ * {@link #writePack(ProgressMonitor, ProgressMonitor, OutputStream)}.
+ *
+ * @param callback
+ * the callback to set
+ *
+ * @return this object for chaining.
+ * @since 4.1
+ */
+ public PackWriter setObjectCountCallback(ObjectCountCallback callback) {
+ this.callback = callback;
+ return this;
+ }
+
+ /**
+ * Records the set of shallow commits in the client.
+ *
+ * @param clientShallowCommits
+ * the shallow commits in the client
+ * @since 4.1
+ */
+ public void setClientShallowCommits(Set<ObjectId> clientShallowCommits) {
+ stats.clientShallowCommits = Collections
+ .unmodifiableSet(new HashSet<ObjectId>(clientShallowCommits));
+ }
+
+ /**
* Check whether writer can store delta base as an offset (new style
* reducing pack size) or should store it as an object id (legacy style,
* compatible with old readers).
@@ -495,7 +525,7 @@ public class PackWriter {
/**
* @return true to ignore objects that are uninteresting and also not found
* on local disk; false to throw a {@link MissingObjectException}
- * out of {@link #preparePack(ProgressMonitor, Collection, Collection)} if an
+ * out of {@link #preparePack(ProgressMonitor, Set, Set)} if an
* uninteresting object is not in the source repository. By default,
* true, permitting gracefully ignoring of uninteresting objects.
*/
@@ -571,12 +601,12 @@ public class PackWriter {
/**
* Returns the object ids in the pack file that was created by this writer.
- *
+ * <p>
* This method can only be invoked after
* {@link #writePack(ProgressMonitor, ProgressMonitor, OutputStream)} has
* been invoked and completed successfully.
*
- * @return number of objects in pack.
+ * @return set of objects in pack.
* @throws IOException
* a cached pack cannot supply its object ids.
*/
@@ -586,17 +616,20 @@ public class PackWriter {
throw new IOException(
JGitText.get().cachedPacksPreventsListingObjects);
- ObjectIdOwnerMap<ObjectIdOwnerMap.Entry> objs = new ObjectIdOwnerMap<
- ObjectIdOwnerMap.Entry>();
+ if (writeBitmaps != null) {
+ return writeBitmaps.getObjectSet();
+ }
+
+ ObjectIdOwnerMap<ObjectIdOwnerMap.Entry> r = new ObjectIdOwnerMap<>();
for (BlockList<ObjectToPack> objList : objectsLists) {
if (objList != null) {
for (ObjectToPack otp : objList)
- objs.add(new ObjectIdOwnerMap.Entry(otp) {
+ r.add(new ObjectIdOwnerMap.Entry(otp) {
// A new entry that copies the ObjectId
});
}
}
- return objs;
+ return r;
}
/**
@@ -641,7 +674,7 @@ public class PackWriter {
* @throws IOException
* when some I/O problem occur during reading objects.
*/
- public void preparePack(final Iterator<RevObject> objectsSource)
+ public void preparePack(@NonNull Iterator<RevObject> objectsSource)
throws IOException {
while (objectsSource.hasNext()) {
addObject(objectsSource.next());
@@ -664,96 +697,18 @@ public class PackWriter {
* progress during object enumeration.
* @param want
* collection of objects to be marked as interesting (start
- * points of graph traversal).
+ * points of graph traversal). Must not be {@code null}.
* @param have
* collection of objects to be marked as uninteresting (end
- * points of graph traversal).
- * @throws IOException
- * when some I/O problem occur during reading objects.
- * @deprecated to be removed in 2.0; use the Set version of this method.
- */
- @Deprecated
- public void preparePack(ProgressMonitor countingMonitor,
- final Collection<? extends ObjectId> want,
- final Collection<? extends ObjectId> have) throws IOException {
- preparePack(countingMonitor, ensureSet(want), ensureSet(have));
- }
-
- /**
- * Prepare the list of objects to be written to the pack stream.
- * <p>
- * Basing on these 2 sets, another set of objects to put in a pack file is
- * created: this set consists of all objects reachable (ancestors) from
- * interesting objects, except uninteresting objects and their ancestors.
- * This method uses class {@link ObjectWalk} extensively to find out that
- * appropriate set of output objects and their optimal order in output pack.
- * Order is consistent with general git in-pack rules: sort by object type,
- * recency, path and delta-base first.
- * </p>
- *
- * @param countingMonitor
- * progress during object enumeration.
- * @param walk
- * ObjectWalk to perform enumeration.
- * @param interestingObjects
- * collection of objects to be marked as interesting (start
- * points of graph traversal).
- * @param uninterestingObjects
- * collection of objects to be marked as uninteresting (end
- * points of graph traversal).
+ * points of graph traversal). Pass {@link #NONE} if all objects
+ * reachable from {@code want} are desired, such as when serving
+ * a clone.
* @throws IOException
* when some I/O problem occur during reading objects.
- * @deprecated to be removed in 2.0; use the Set version of this method.
*/
- @Deprecated
public void preparePack(ProgressMonitor countingMonitor,
- ObjectWalk walk,
- final Collection<? extends ObjectId> interestingObjects,
- final Collection<? extends ObjectId> uninterestingObjects)
- throws IOException {
- preparePack(countingMonitor, walk,
- ensureSet(interestingObjects),
- ensureSet(uninterestingObjects));
- }
-
- @SuppressWarnings("unchecked")
- private static Set<ObjectId> ensureSet(Collection<? extends ObjectId> objs) {
- Set<ObjectId> set;
- if (objs instanceof Set<?>)
- set = (Set<ObjectId>) objs;
- else if (objs == null)
- set = Collections.emptySet();
- else
- set = new HashSet<ObjectId>(objs);
- return set;
- }
-
- /**
- * Prepare the list of objects to be written to the pack stream.
- * <p>
- * Basing on these 2 sets, another set of objects to put in a pack file is
- * created: this set consists of all objects reachable (ancestors) from
- * interesting objects, except uninteresting objects and their ancestors.
- * This method uses class {@link ObjectWalk} extensively to find out that
- * appropriate set of output objects and their optimal order in output pack.
- * Order is consistent with general git in-pack rules: sort by object type,
- * recency, path and delta-base first.
- * </p>
- *
- * @param countingMonitor
- * progress during object enumeration.
- * @param want
- * collection of objects to be marked as interesting (start
- * points of graph traversal).
- * @param have
- * collection of objects to be marked as uninteresting (end
- * points of graph traversal).
- * @throws IOException
- * when some I/O problem occur during reading objects.
- */
- public void preparePack(ProgressMonitor countingMonitor,
- Set<? extends ObjectId> want,
- Set<? extends ObjectId> have) throws IOException {
+ @NonNull Set<? extends ObjectId> want,
+ @NonNull Set<? extends ObjectId> have) throws IOException {
ObjectWalk ow;
if (shallowPack)
ow = new DepthWalk.ObjectWalk(reader, depth);
@@ -780,17 +735,19 @@ public class PackWriter {
* ObjectWalk to perform enumeration.
* @param interestingObjects
* collection of objects to be marked as interesting (start
- * points of graph traversal).
+ * points of graph traversal). Must not be {@code null}.
* @param uninterestingObjects
* collection of objects to be marked as uninteresting (end
- * points of graph traversal).
+ * points of graph traversal). Pass {@link #NONE} if all objects
+ * reachable from {@code want} are desired, such as when serving
+ * a clone.
* @throws IOException
* when some I/O problem occur during reading objects.
*/
public void preparePack(ProgressMonitor countingMonitor,
- ObjectWalk walk,
- final Set<? extends ObjectId> interestingObjects,
- final Set<? extends ObjectId> uninterestingObjects)
+ @NonNull ObjectWalk walk,
+ @NonNull Set<? extends ObjectId> interestingObjects,
+ @NonNull Set<? extends ObjectId> uninterestingObjects)
throws IOException {
if (countingMonitor == null)
countingMonitor = NullProgressMonitor.INSTANCE;
@@ -864,10 +821,11 @@ public class PackWriter {
/**
* Create an index file to match the pack file just written.
* <p>
- * This method can only be invoked after
- * {@link #writePack(ProgressMonitor, ProgressMonitor, OutputStream)} has
- * been invoked and completed successfully. Writing a corresponding index is
- * an optional feature that not all pack users may require.
+ * Called after
+ * {@link #writePack(ProgressMonitor, ProgressMonitor, OutputStream)}.
+ * <p>
+ * Writing an index is only required for local pack storage. Packs sent on
+ * the network do not need to create an index.
*
* @param indexStream
* output for the index data. Caller is responsible for closing
@@ -889,10 +847,7 @@ public class PackWriter {
/**
* Create a bitmap index file to match the pack file just written.
* <p>
- * This method can only be invoked after
- * {@link #prepareBitmapIndex(ProgressMonitor)} has been invoked and
- * completed successfully. Writing a corresponding bitmap index is an
- * optional feature that not all pack users may require.
+ * Called after {@link #prepareBitmapIndex(ProgressMonitor)}.
*
* @param bitmapIndexStream
* output for the bitmap index data. Caller is responsible for
@@ -966,14 +921,13 @@ public class PackWriter {
/**
* Write the prepared pack to the supplied stream.
* <p>
- * At first, this method collects and sorts objects to pack, then deltas
- * search is performed if set up accordingly, finally pack stream is
- * written.
- * </p>
+ * Called after {@link #preparePack(ProgressMonitor, ObjectWalk, Set, Set)}
+ * or {@link #preparePack(ProgressMonitor, Set, Set)}.
+ * <p>
+ * Performs delta search if enabled and writes the pack stream.
* <p>
* All reused objects data checksum (Adler32/CRC32) is computed and
* validated against existing checksum.
- * </p>
*
* @param compressMonitor
* progress monitor to report object compression work.
@@ -986,6 +940,9 @@ public class PackWriter {
* an error occurred reading a local object's data to include in
* the pack, or writing compressed object data to the output
* stream.
+ * @throws WriteAbortedException
+ * the write operation is aborted by {@link ObjectCountCallback}
+ * .
*/
public void writePack(ProgressMonitor compressMonitor,
ProgressMonitor writeMonitor, OutputStream packStream)
@@ -1027,6 +984,8 @@ public class PackWriter {
long objCnt = getObjectCount();
stats.totalObjects = objCnt;
+ if (callback != null)
+ callback.setObjectCount(objCnt);
beginPhase(PackingPhase.WRITING, writeMonitor, objCnt);
long writeStart = System.currentTimeMillis();
try {
@@ -1035,7 +994,7 @@ public class PackWriter {
writeObjects(out);
if (!edgeObjects.isEmpty() || !cachedPacks.isEmpty()) {
- for (Statistics.ObjectType typeStat : stats.objectTypes) {
+ for (PackStatistics.ObjectType.Accumulator typeStat : stats.objectTypes) {
if (typeStat == null)
continue;
stats.thinPackBytes += typeStat.bytes;
@@ -1048,7 +1007,7 @@ public class PackWriter {
stats.reusedObjects += pack.getObjectCount();
stats.reusedDeltas += deltaCnt;
stats.totalDeltas += deltaCnt;
- reuseSupport.copyPackAsIs(out, pack, reuseValidate);
+ reuseSupport.copyPackAsIs(out, pack);
}
writeChecksum(out);
out.flush();
@@ -1056,7 +1015,7 @@ public class PackWriter {
stats.timeWriting = System.currentTimeMillis() - writeStart;
stats.depth = depth;
- for (Statistics.ObjectType typeStat : stats.objectTypes) {
+ for (PackStatistics.ObjectType.Accumulator typeStat : stats.objectTypes) {
if (typeStat == null)
continue;
typeStat.cntDeltas += typeStat.reusedDeltas;
@@ -1067,17 +1026,17 @@ public class PackWriter {
}
stats.totalBytes = out.length();
- reader.release();
+ reader.close();
endPhase(writeMonitor);
}
/**
* @return description of what this PackWriter did in order to create the
- * final pack stream. The object is only available to callers after
- * {@link #writePack(ProgressMonitor, ProgressMonitor, OutputStream)}
+ * final pack stream. This should only be invoked after the calls to
+ * create the pack/index/bitmap have completed.
*/
- public Statistics getStatistics() {
- return stats;
+ public PackStatistics getStatistics() {
+ return new PackStatistics(stats);
}
/** @return snapshot of the current state of this PackWriter. */
@@ -1085,9 +1044,14 @@ public class PackWriter {
return state.snapshot();
}
- /** Release all resources used by this writer. */
- public void release() {
- reader.release();
+ /**
+ * Release all resources used by this writer.
+ *
+ * @since 4.0
+ */
+ @Override
+ public void close() {
+ reader.close();
if (myDeflater != null) {
myDeflater.end();
myDeflater = null;
@@ -1339,8 +1303,7 @@ public class PackWriter {
long totalWeight = 0;
for (int i = 0; i < cnt; i++) {
ObjectToPack o = list[i];
- if (!o.isEdge() && !o.doNotAttemptDelta())
- totalWeight += o.getWeight();
+ totalWeight += DeltaTask.getAdjustedWeight(o);
}
long bytesPerUnit = 1;
@@ -1585,6 +1548,8 @@ public class PackWriter {
if (zbuf != null) {
out.writeHeader(otp, otp.getCachedSize());
out.write(zbuf);
+ typeStats.cntDeltas++;
+ typeStats.deltaBytes += out.length() - otp.getOffset();
return;
}
}
@@ -1640,22 +1605,15 @@ public class PackWriter {
out.write(packcsum);
}
- private void findObjectsToPack(final ProgressMonitor countingMonitor,
- final ObjectWalk walker, final Set<? extends ObjectId> want,
- Set<? extends ObjectId> have)
- throws MissingObjectException, IOException,
- IncorrectObjectTypeException {
+ private void findObjectsToPack(@NonNull ProgressMonitor countingMonitor,
+ @NonNull ObjectWalk walker, @NonNull Set<? extends ObjectId> want,
+ @NonNull Set<? extends ObjectId> have) throws IOException {
final long countingStart = System.currentTimeMillis();
beginPhase(PackingPhase.COUNTING, countingMonitor, ProgressMonitor.UNKNOWN);
- if (have == null)
- have = Collections.emptySet();
-
stats.interestingObjects = Collections.unmodifiableSet(new HashSet<ObjectId>(want));
stats.uninterestingObjects = Collections.unmodifiableSet(new HashSet<ObjectId>(have));
- walker.setRetainBody(false);
-
canBuildBitmaps = config.isBuildBitmaps()
&& !shallowPack
&& have.isEmpty()
@@ -1668,6 +1626,7 @@ public class PackWriter {
findObjectsToPackUsingBitmaps(bitmapWalker, want, have);
endPhase(countingMonitor);
stats.timeCounting = System.currentTimeMillis() - countingStart;
+ stats.bitmapIndexMisses = bitmapWalker.getCountOfBitmapIndexMisses();
return;
}
}
@@ -1752,6 +1711,7 @@ public class PackWriter {
final int maxBases = config.getDeltaSearchWindowSize();
Set<RevTree> baseTrees = new HashSet<RevTree>();
BlockList<RevCommit> commits = new BlockList<RevCommit>();
+ Set<ObjectId> roots = new HashSet<>();
RevCommit c;
while ((c = walker.next()) != null) {
if (exclude(c))
@@ -1763,8 +1723,12 @@ public class PackWriter {
}
commits.add(c);
+ if (c.getParentCount() == 0) {
+ roots.add(c.copy());
+ }
countingMonitor.update(1);
}
+ stats.rootCommits = Collections.unmodifiableSet(roots);
if (shallowPack) {
for (RevCommit cmit : commits) {
@@ -1840,6 +1804,7 @@ public class PackWriter {
countingMonitor.update((int) pack.getObjectCount());
endPhase(countingMonitor);
stats.timeCounting = System.currentTimeMillis() - countingStart;
+ stats.bitmapIndexMisses = -1;
}
private void findObjectsToPackUsingBitmaps(
@@ -1853,7 +1818,7 @@ public class PackWriter {
false);
BitmapBuilder needBitmap = wantBitmap.andNot(haveBitmap);
- if (useCachedPacks && reuseSupport != null
+ if (useCachedPacks && reuseSupport != null && !reuseValidate
&& (excludeInPacks == null || excludeInPacks.length == 0))
cachedPacks.addAll(
reuseSupport.getCachedPacksAndUpdate(needBitmap));
@@ -2008,14 +1973,17 @@ public class PackWriter {
}
/**
- * Prepares the bitmaps to be written to the pack index. Bitmaps can be used
- * to speed up fetches and clones by storing the entire object graph at
- * selected commits.
- *
- * This method can only be invoked after
- * {@link #writePack(ProgressMonitor, ProgressMonitor, OutputStream)} has
- * been invoked and completed successfully. Writing a corresponding bitmap
- * index is an optional feature that not all pack users may require.
+ * Prepares the bitmaps to be written to the bitmap index file.
+ * <p>
+ * Bitmaps can be used to speed up fetches and clones by storing the entire
+ * object graph at selected commits. Writing a bitmap index is an optional
+ * feature that not all pack users may require.
+ * <p>
+ * Called after {@link #writeIndex(OutputStream)}.
+ * <p>
+ * To reduce memory internal state is cleared during this method, rendering
+ * the PackWriter instance useless for anything further than a call to write
+ * out the new bitmaps with {@link #writeBitmapIndex(OutputStream)}.
*
* @param pm
* progress monitor to report bitmap building work.
@@ -2031,13 +1999,19 @@ public class PackWriter {
if (pm == null)
pm = NullProgressMonitor.INSTANCE;
- writeBitmaps = new PackBitmapIndexBuilder(sortByName());
+ int numCommits = objectsLists[OBJ_COMMIT].size();
+ List<ObjectToPack> byName = sortByName();
+ sortedByName = null;
+ objectsLists = null;
+ objectsMap = null;
+ writeBitmaps = new PackBitmapIndexBuilder(byName);
+ byName = null;
+
PackWriterBitmapPreparer bitmapPreparer = new PackWriterBitmapPreparer(
- reader, writeBitmaps, pm, stats.interestingObjects);
+ reader, writeBitmaps, pm, stats.interestingObjects, config);
- int numCommits = objectsLists[OBJ_COMMIT].size();
Collection<PackWriterBitmapPreparer.BitmapCommit> selectedCommits =
- bitmapPreparer.doCommitSelection(numCommits);
+ bitmapPreparer.selectCommits(numCommits);
beginPhase(PackingPhase.BUILDING_BITMAPS, pm, selectedCommits.size());
@@ -2077,28 +2051,36 @@ public class PackWriter {
return true;
}
- /** Summary of how PackWriter created the pack. */
+ /**
+ * Summary of how PackWriter created the pack.
+ *
+ * @deprecated Use {@link PackStatistics} instead.
+ */
+ @Deprecated
public static class Statistics {
/** Statistics about a single class of object. */
public static class ObjectType {
- long cntObjects;
-
- long cntDeltas;
-
- long reusedObjects;
+ // All requests are forwarded to this object.
+ private PackStatistics.ObjectType objectType;
- long reusedDeltas;
-
- long bytes;
-
- long deltaBytes;
+ /**
+ * Wraps an
+ * {@link org.eclipse.jgit.storage.pack.PackStatistics.ObjectType}
+ * instance to maintain backwards compatibility with existing API.
+ *
+ * @param type
+ * the wrapped instance
+ */
+ public ObjectType(PackStatistics.ObjectType type) {
+ objectType = type;
+ }
/**
* @return total number of objects output. This total includes the
* value of {@link #getDeltas()}.
*/
public long getObjects() {
- return cntObjects;
+ return objectType.getObjects();
}
/**
@@ -2106,7 +2088,7 @@ public class PackWriter {
* actual number of deltas if a cached pack was reused.
*/
public long getDeltas() {
- return cntDeltas;
+ return objectType.getDeltas();
}
/**
@@ -2115,7 +2097,7 @@ public class PackWriter {
* {@link #getReusedDeltas()}.
*/
public long getReusedObjects() {
- return reusedObjects;
+ return objectType.getReusedObjects();
}
/**
@@ -2126,7 +2108,7 @@ public class PackWriter {
* was reused.
*/
public long getReusedDeltas() {
- return reusedDeltas;
+ return objectType.getReusedDeltas();
}
/**
@@ -2135,7 +2117,7 @@ public class PackWriter {
* also includes all of {@link #getDeltaBytes()}.
*/
public long getBytes() {
- return bytes;
+ return objectType.getBytes();
}
/**
@@ -2143,52 +2125,22 @@ public class PackWriter {
* object headers for the delta objects.
*/
public long getDeltaBytes() {
- return deltaBytes;
+ return objectType.getDeltaBytes();
}
}
- Set<ObjectId> interestingObjects;
-
- Set<ObjectId> uninterestingObjects;
-
- Collection<CachedPack> reusedPacks;
-
- int depth;
-
- int deltaSearchNonEdgeObjects;
-
- int deltasFound;
-
- long totalObjects;
-
- long totalDeltas;
-
- long reusedObjects;
-
- long reusedDeltas;
-
- long totalBytes;
-
- long thinPackBytes;
-
- long timeCounting;
-
- long timeSearchingForReuse;
+ // All requests are forwarded to this object.
+ private PackStatistics statistics;
- long timeSearchingForSizes;
-
- long timeCompressing;
-
- long timeWriting;
-
- ObjectType[] objectTypes;
-
- {
- objectTypes = new ObjectType[5];
- objectTypes[OBJ_COMMIT] = new ObjectType();
- objectTypes[OBJ_TREE] = new ObjectType();
- objectTypes[OBJ_BLOB] = new ObjectType();
- objectTypes[OBJ_TAG] = new ObjectType();
+ /**
+ * Wraps a {@link PackStatistics} object to maintain backwards
+ * compatibility with existing API.
+ *
+ * @param stats
+ * the wrapped PackStatitics object
+ */
+ public Statistics(PackStatistics stats) {
+ statistics = stats;
}
/**
@@ -2197,7 +2149,7 @@ public class PackWriter {
* test.
*/
public Set<ObjectId> getInterestingObjects() {
- return interestingObjects;
+ return statistics.getInterestingObjects();
}
/**
@@ -2206,7 +2158,7 @@ public class PackWriter {
* has these objects.
*/
public Set<ObjectId> getUninterestingObjects() {
- return uninterestingObjects;
+ return statistics.getUninterestingObjects();
}
/**
@@ -2214,7 +2166,7 @@ public class PackWriter {
* in the output, if any were selected for reuse.
*/
public Collection<CachedPack> getReusedPacks() {
- return reusedPacks;
+ return statistics.getReusedPacks();
}
/**
@@ -2222,7 +2174,7 @@ public class PackWriter {
* delta search process in order to find a potential delta base.
*/
public int getDeltaSearchNonEdgeObjects() {
- return deltaSearchNonEdgeObjects;
+ return statistics.getDeltaSearchNonEdgeObjects();
}
/**
@@ -2231,7 +2183,7 @@ public class PackWriter {
* {@link #getDeltaSearchNonEdgeObjects()}.
*/
public int getDeltasFound() {
- return deltasFound;
+ return statistics.getDeltasFound();
}
/**
@@ -2239,7 +2191,18 @@ public class PackWriter {
* of {@link #getTotalDeltas()}.
*/
public long getTotalObjects() {
- return totalObjects;
+ return statistics.getTotalObjects();
+ }
+
+ /**
+ * @return the count of objects that needed to be discovered through an
+ * object walk because they were not found in bitmap indices.
+ * Returns -1 if no bitmap indices were found.
+ *
+ * @since 4.0
+ */
+ public long getBitmapIndexMisses() {
+ return statistics.getBitmapIndexMisses();
}
/**
@@ -2247,7 +2210,7 @@ public class PackWriter {
* actual number of deltas if a cached pack was reused.
*/
public long getTotalDeltas() {
- return totalDeltas;
+ return statistics.getTotalDeltas();
}
/**
@@ -2255,7 +2218,7 @@ public class PackWriter {
* the output. This count includes {@link #getReusedDeltas()}.
*/
public long getReusedObjects() {
- return reusedObjects;
+ return statistics.getReusedObjects();
}
/**
@@ -2265,7 +2228,7 @@ public class PackWriter {
* actual number of reused deltas if a cached pack was reused.
*/
public long getReusedDeltas() {
- return reusedDeltas;
+ return statistics.getReusedDeltas();
}
/**
@@ -2273,7 +2236,7 @@ public class PackWriter {
* header, trailer, thin pack, and reused cached pack(s).
*/
public long getTotalBytes() {
- return totalBytes;
+ return statistics.getTotalBytes();
}
/**
@@ -2285,7 +2248,7 @@ public class PackWriter {
* pack header or trailer.
*/
public long getThinPackBytes() {
- return thinPackBytes;
+ return statistics.getThinPackBytes();
}
/**
@@ -2294,17 +2257,17 @@ public class PackWriter {
* @return information about this type of object in the pack.
*/
public ObjectType byObjectType(int typeCode) {
- return objectTypes[typeCode];
+ return new ObjectType(statistics.byObjectType(typeCode));
}
/** @return true if the resulting pack file was a shallow pack. */
public boolean isShallow() {
- return depth > 0;
+ return statistics.isShallow();
}
/** @return depth (in commits) the pack includes if shallow. */
public int getDepth() {
- return depth;
+ return statistics.getDepth();
}
/**
@@ -2313,7 +2276,7 @@ public class PackWriter {
* that occur when a cached pack is selected for reuse.
*/
public long getTimeCounting() {
- return timeCounting;
+ return statistics.getTimeCounting();
}
/**
@@ -2322,7 +2285,7 @@ public class PackWriter {
* can be assumed to already have.
*/
public long getTimeSearchingForReuse() {
- return timeSearchingForReuse;
+ return statistics.getTimeSearchingForReuse();
}
/**
@@ -2332,7 +2295,7 @@ public class PackWriter {
* together and improve delta compression ratios.
*/
public long getTimeSearchingForSizes() {
- return timeSearchingForSizes;
+ return statistics.getTimeSearchingForSizes();
}
/**
@@ -2342,7 +2305,7 @@ public class PackWriter {
* delta compression.
*/
public long getTimeCompressing() {
- return timeCompressing;
+ return statistics.getTimeCompressing();
}
/**
@@ -2352,16 +2315,12 @@ public class PackWriter {
* value.
*/
public long getTimeWriting() {
- return timeWriting;
+ return statistics.getTimeWriting();
}
/** @return total time spent processing this pack. */
public long getTimeTotal() {
- return timeCounting
- + timeSearchingForReuse
- + timeSearchingForSizes
- + timeCompressing
- + timeWriting;
+ return statistics.getTimeTotal();
}
/**
@@ -2369,14 +2328,12 @@ public class PackWriter {
* {@code getTotalBytes() / (getTimeWriting() / 1000.0)}.
*/
public double getTransferRate() {
- return getTotalBytes() / (getTimeWriting() / 1000.0);
+ return statistics.getTransferRate();
}
/** @return formatted message string for display to clients. */
public String getMessage() {
- return MessageFormat.format(JGitText.get().packWriterStatistics, //
- Long.valueOf(totalObjects), Long.valueOf(totalDeltas), //
- Long.valueOf(reusedObjects), Long.valueOf(reusedDeltas));
+ return statistics.getMessage();
}
}
@@ -2409,11 +2366,14 @@ public class PackWriter {
State snapshot() {
long objCnt = 0;
- objCnt += objectsLists[OBJ_COMMIT].size();
- objCnt += objectsLists[OBJ_TREE].size();
- objCnt += objectsLists[OBJ_BLOB].size();
- objCnt += objectsLists[OBJ_TAG].size();
- // Exclude CachedPacks.
+ BlockList<ObjectToPack>[] lists = objectsLists;
+ if (lists != null) {
+ objCnt += lists[OBJ_COMMIT].size();
+ objCnt += lists[OBJ_TREE].size();
+ objCnt += lists[OBJ_BLOB].size();
+ objCnt += lists[OBJ_TAG].size();
+ // Exclude CachedPacks.
+ }
long bytesUsed = OBJECT_TO_PACK_SIZE * objCnt;
PackingPhase curr = phase;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java
index fca9063..77311ab 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java
@@ -44,6 +44,7 @@
package org.eclipse.jgit.internal.storage.pack;
import static org.eclipse.jgit.internal.storage.file.PackBitmapIndex.FLAG_REUSE;
+import static org.eclipse.jgit.revwalk.RevFlag.SEEN;
import java.io.IOException;
import java.util.ArrayList;
@@ -55,34 +56,44 @@ import java.util.Iterator;
import java.util.List;
import java.util.Set;
-import com.googlecode.javaewah.EWAHCompressedBitmap;
-
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.file.BitmapIndexImpl;
+import org.eclipse.jgit.internal.storage.file.BitmapIndexImpl.CompressedBitmap;
import org.eclipse.jgit.internal.storage.file.PackBitmapIndex;
import org.eclipse.jgit.internal.storage.file.PackBitmapIndexBuilder;
import org.eclipse.jgit.internal.storage.file.PackBitmapIndexRemapper;
+import org.eclipse.jgit.internal.storage.pack.PackWriterBitmapWalker.AddUnseenToBitmapFilter;
import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.ProgressMonitor;
-import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
import org.eclipse.jgit.revwalk.ObjectWalk;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.revwalk.filter.RevFilter;
+import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.util.BlockList;
+import org.eclipse.jgit.util.SystemReader;
-/** Helper class for the PackWriter to select commits for pack index bitmaps. */
+import com.googlecode.javaewah.EWAHCompressedBitmap;
+
+/**
+ * Helper class for the {@link PackWriter} to select commits for which to build
+ * pack index bitmaps.
+ */
class PackWriterBitmapPreparer {
- private static final Comparator<BitmapBuilder> BUILDER_BY_CARDINALITY_DSC =
- new Comparator<BitmapBuilder>() {
- public int compare(BitmapBuilder a, BitmapBuilder b) {
- return Integer.signum(b.cardinality() - a.cardinality());
+ private static final int DAY_IN_SECONDS = 24 * 60 * 60;
+
+ private static final Comparator<BitmapBuilderEntry> ORDER_BY_CARDINALITY = new Comparator<BitmapBuilderEntry>() {
+ public int compare(BitmapBuilderEntry a, BitmapBuilderEntry b) {
+ return Integer.signum(a.getBuilder().cardinality()
+ - b.getBuilder().cardinality());
}
};
@@ -93,12 +104,18 @@ class PackWriterBitmapPreparer {
private final BitmapIndexImpl commitBitmapIndex;
private final PackBitmapIndexRemapper bitmapRemapper;
private final BitmapIndexImpl bitmapIndex;
- private final int minCommits = 100;
- private final int maxCommits = 5000;
+
+ private final int contiguousCommitCount;
+ private final int recentCommitCount;
+ private final int recentCommitSpan;
+ private final int distantCommitSpan;
+ private final int excessiveBranchCount;
+ private final long inactiveBranchTimestamp;
PackWriterBitmapPreparer(ObjectReader reader,
PackBitmapIndexBuilder writeBitmaps, ProgressMonitor pm,
- Set<? extends ObjectId> want) throws IOException {
+ Set<? extends ObjectId> want, PackConfig config)
+ throws IOException {
this.reader = reader;
this.writeBitmaps = writeBitmaps;
this.pm = pm;
@@ -107,207 +124,379 @@ class PackWriterBitmapPreparer {
this.bitmapRemapper = PackBitmapIndexRemapper.newPackBitmapIndex(
reader.getBitmapIndex(), writeBitmaps);
this.bitmapIndex = new BitmapIndexImpl(bitmapRemapper);
+ this.contiguousCommitCount = config.getBitmapContiguousCommitCount();
+ this.recentCommitCount = config.getBitmapRecentCommitCount();
+ this.recentCommitSpan = config.getBitmapRecentCommitSpan();
+ this.distantCommitSpan = config.getBitmapDistantCommitSpan();
+ this.excessiveBranchCount = config.getBitmapExcessiveBranchCount();
+ long now = SystemReader.getInstance().getCurrentTime();
+ long ageInSeconds = config.getBitmapInactiveBranchAgeInDays()
+ * DAY_IN_SECONDS;
+ this.inactiveBranchTimestamp = (now / 1000) - ageInSeconds;
}
- Collection<BitmapCommit> doCommitSelection(int expectedNumCommits)
- throws MissingObjectException, IncorrectObjectTypeException,
- IOException {
+ /**
+ * Returns the commit objects for which bitmap indices should be built.
+ *
+ * @param expectedCommitCount
+ * count of commits in the pack
+ * @return commit objects for which bitmap indices should be built
+ * @throws IncorrectObjectTypeException
+ * if any of the processed objects is not a commit
+ * @throws IOException
+ * on errors reading pack or index files
+ * @throws MissingObjectException
+ * if an expected object is missing
+ */
+ Collection<BitmapCommit> selectCommits(int expectedCommitCount)
+ throws IncorrectObjectTypeException, IOException,
+ MissingObjectException {
+ /*
+ * Thinking of bitmap indices as a cache, if we find bitmaps at or at a
+ * close ancestor to 'old' and 'new' when calculating old..new, then all
+ * objects can be calculated with minimal graph walking. A distribution
+ * that favors creating bitmaps for the most recent commits maximizes
+ * the cache hits for clients that are close to HEAD, which is the
+ * majority of calculations performed.
+ */
pm.beginTask(JGitText.get().selectingCommits, ProgressMonitor.UNKNOWN);
RevWalk rw = new RevWalk(reader);
- WalkResult result = findPaths(rw, expectedNumCommits);
+ rw.setRetainBody(false);
+ CommitSelectionHelper selectionHelper = setupTipCommitBitmaps(rw,
+ expectedCommitCount);
pm.endTask();
- int totCommits = result.commitsByOldest.length - result.commitStartPos;
+ int totCommits = selectionHelper.getCommitCount();
BlockList<BitmapCommit> selections = new BlockList<BitmapCommit>(
- totCommits / minCommits + 1);
- for (BitmapCommit reuse : result.reuse)
+ totCommits / recentCommitSpan + 1);
+ for (BitmapCommit reuse : selectionHelper.reusedCommits) {
selections.add(reuse);
+ }
if (totCommits == 0) {
- for (AnyObjectId id : result.peeledWant)
+ for (AnyObjectId id : selectionHelper.peeledWants) {
selections.add(new BitmapCommit(id, false, 0));
+ }
return selections;
}
pm.beginTask(JGitText.get().selectingCommits, totCommits);
-
- for (BitmapBuilder bitmapableCommits : result.paths) {
- int cardinality = bitmapableCommits.cardinality();
-
- List<List<BitmapCommit>> running = new ArrayList<
- List<BitmapCommit>>();
+ int totalWants = selectionHelper.peeledWants.size();
+
+ for (BitmapBuilderEntry entry : selectionHelper.tipCommitBitmaps) {
+ BitmapBuilder bitmap = entry.getBuilder();
+ int cardinality = bitmap.cardinality();
+
+ // Within this branch, keep ordered lists of commits representing
+ // chains in its history, where each chain is a "sub-branch".
+ // Ordering commits by these chains makes for fewer differences
+ // between consecutive selected commits, which in turn provides
+ // better compression/on the run-length encoding of the XORs between
+ // them.
+ List<List<BitmapCommit>> chains =
+ new ArrayList<List<BitmapCommit>>();
+
+ // Mark the current branch as inactive if its tip commit isn't
+ // recent and there are an excessive number of branches, to
+ // prevent memory bloat of computing too many bitmaps for stale
+ // branches.
+ boolean isActiveBranch = true;
+ if (totalWants > excessiveBranchCount
+ && !isRecentCommit(entry.getCommit())) {
+ isActiveBranch = false;
+ }
// Insert bitmaps at the offsets suggested by the
- // nextSelectionDistance() heuristic.
+ // nextSelectionDistance() heuristic. Only reuse bitmaps created
+ // for more distant commits.
int index = -1;
- int nextIn = nextSelectionDistance(0, cardinality);
- int nextFlg = nextIn == maxCommits ? PackBitmapIndex.FLAG_REUSE : 0;
- boolean mustPick = nextIn == 0;
- for (RevCommit c : result) {
- if (!bitmapableCommits.contains(c))
+ int nextIn = nextSpan(cardinality);
+ int nextFlg = nextIn == distantCommitSpan
+ ? PackBitmapIndex.FLAG_REUSE : 0;
+
+ // For the current branch, iterate through all commits from oldest
+ // to newest.
+ for (RevCommit c : selectionHelper) {
+ // Optimization: if we have found all the commits for this
+ // branch, stop searching
+ int distanceFromTip = cardinality - index - 1;
+ if (distanceFromTip == 0) {
+ break;
+ }
+
+ // Ignore commits that are not in this branch
+ if (!bitmap.contains(c)) {
continue;
+ }
index++;
nextIn--;
pm.update(1);
- // Always pick the items in want and prefer merge commits.
- if (result.peeledWant.remove(c)) {
- if (nextIn > 0)
+ // Always pick the items in wants, prefer merge commits.
+ if (selectionHelper.peeledWants.remove(c)) {
+ if (nextIn > 0) {
nextFlg = 0;
- } else if (!mustPick && ((nextIn > 0)
- || (c.getParentCount() <= 1 && nextIn > -minCommits))) {
- continue;
+ }
+ } else {
+ boolean stillInSpan = nextIn >= 0;
+ boolean isMergeCommit = c.getParentCount() > 1;
+ // Force selection if:
+ // a) we have exhausted the window looking for merges
+ // b) we are in the top commits of an active branch
+ // c) we are at a branch tip
+ boolean mustPick = (nextIn <= -recentCommitSpan)
+ || (isActiveBranch
+ && (distanceFromTip <= contiguousCommitCount))
+ || (distanceFromTip == 1); // most recent commit
+ if (!mustPick && (stillInSpan || !isMergeCommit)) {
+ continue;
+ }
}
+ // This commit is selected.
+ // Calculate where to look for the next one.
int flags = nextFlg;
- nextIn = nextSelectionDistance(index, cardinality);
- nextFlg = nextIn == maxCommits ? PackBitmapIndex.FLAG_REUSE : 0;
- mustPick = nextIn == 0;
+ nextIn = nextSpan(distanceFromTip);
+ nextFlg = nextIn == distantCommitSpan
+ ? PackBitmapIndex.FLAG_REUSE : 0;
BitmapBuilder fullBitmap = commitBitmapIndex.newBitmapBuilder();
rw.reset();
rw.markStart(c);
- for (AnyObjectId objectId : result.reuse)
- rw.markUninteresting(rw.parseCommit(objectId));
- rw.setRevFilter(
- PackWriterBitmapWalker.newRevFilter(null, fullBitmap));
+ rw.setRevFilter(new AddUnseenToBitmapFilter(
+ selectionHelper.reusedCommitsBitmap, fullBitmap));
while (rw.next() != null) {
- // Work is done in the RevFilter.
+ // The RevFilter adds the reachable commits from this
+ // selected commit to fullBitmap.
}
- List<List<BitmapCommit>> matches = new ArrayList<
- List<BitmapCommit>>();
- for (List<BitmapCommit> list : running) {
- BitmapCommit last = list.get(list.size() - 1);
- if (fullBitmap.contains(last))
- matches.add(list);
+ // Sort the commits by independent chains in this branch's
+ // history, yielding better compression when building bitmaps.
+ List<BitmapCommit> longestAncestorChain = null;
+ for (List<BitmapCommit> chain : chains) {
+ BitmapCommit mostRecentCommit = chain.get(chain.size() - 1);
+ if (fullBitmap.contains(mostRecentCommit)) {
+ if (longestAncestorChain == null
+ || longestAncestorChain.size() < chain.size()) {
+ longestAncestorChain = chain;
+ }
+ }
}
- List<BitmapCommit> match;
- if (matches.isEmpty()) {
- match = new ArrayList<BitmapCommit>();
- running.add(match);
- } else {
- match = matches.get(0);
- // Append to longest
- for (List<BitmapCommit> list : matches) {
- if (list.size() > match.size())
- match = list;
- }
+ if (longestAncestorChain == null) {
+ longestAncestorChain = new ArrayList<BitmapCommit>();
+ chains.add(longestAncestorChain);
}
- match.add(new BitmapCommit(c, !match.isEmpty(), flags));
+ longestAncestorChain.add(new BitmapCommit(
+ c, !longestAncestorChain.isEmpty(), flags));
writeBitmaps.addBitmap(c, fullBitmap, 0);
}
- for (List<BitmapCommit> list : running)
- selections.addAll(list);
+ for (List<BitmapCommit> chain : chains) {
+ selections.addAll(chain);
+ }
}
writeBitmaps.clearBitmaps(); // Remove the temporary commit bitmaps.
// Add the remaining peeledWant
- for (AnyObjectId remainingWant : result.peeledWant)
+ for (AnyObjectId remainingWant : selectionHelper.peeledWants) {
selections.add(new BitmapCommit(remainingWant, false, 0));
+ }
pm.endTask();
return selections;
}
- private WalkResult findPaths(RevWalk rw, int expectedNumCommits)
- throws MissingObjectException, IOException {
- BitmapBuilder reuseBitmap = commitBitmapIndex.newBitmapBuilder();
- List<BitmapCommit> reuse = new ArrayList<BitmapCommit>();
+ private boolean isRecentCommit(RevCommit revCommit) {
+ return revCommit.getCommitTime() > inactiveBranchTimestamp;
+ }
+
+ /**
+ * A RevFilter that excludes the commits named in a bitmap from the walk.
+ * <p>
+ * If a commit is in {@code bitmap} then that commit is not emitted by the
+ * walk and its parents are marked as SEEN so the walk can skip them. The
+ * bitmaps passed in have the property that the parents of any commit in
+ * {@code bitmap} are also in {@code bitmap}, so marking the parents as
+ * SEEN speeds up the RevWalk by saving it from walking down blind alleys
+ * and does not change the commits emitted.
+ */
+ private static class NotInBitmapFilter extends RevFilter {
+ private final BitmapBuilder bitmap;
+
+ NotInBitmapFilter(BitmapBuilder bitmap) {
+ this.bitmap = bitmap;
+ }
+
+ @Override
+ public final boolean include(RevWalk rw, RevCommit c) {
+ if (!bitmap.contains(c)) {
+ return true;
+ }
+ for (RevCommit p : c.getParents()) {
+ p.add(SEEN);
+ }
+ return false;
+ }
+
+ @Override
+ public final NotInBitmapFilter clone() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public final boolean requiresCommitBody() {
+ return false;
+ }
+ }
+
+ /**
+ * For each of the {@code want}s, which represent the tip commit of each
+ * branch, set up an initial {@link BitmapBuilder}. Reuse previously built
+ * bitmaps if possible.
+ *
+ * @param rw
+ * a {@link RevWalk} to find reachable objects in this repository
+ * @param expectedCommitCount
+ * expected count of commits. The actual count may be less due to
+ * unreachable garbage.
+ * @return a {@link CommitSelectionHelper} containing bitmaps for the tip
+ * commits
+ * @throws IncorrectObjectTypeException
+ * if any of the processed objects is not a commit
+ * @throws IOException
+ * on errors reading pack or index files
+ * @throws MissingObjectException
+ * if an expected object is missing
+ */
+ private CommitSelectionHelper setupTipCommitBitmaps(RevWalk rw,
+ int expectedCommitCount) throws IncorrectObjectTypeException,
+ IOException, MissingObjectException {
+ BitmapBuilder reuse = commitBitmapIndex.newBitmapBuilder();
+ List<BitmapCommit> reuseCommits = new ArrayList<BitmapCommit>();
for (PackBitmapIndexRemapper.Entry entry : bitmapRemapper) {
- if ((entry.getFlags() & FLAG_REUSE) != FLAG_REUSE)
+ // More recent commits did not have the reuse flag set, so skip them
+ if ((entry.getFlags() & FLAG_REUSE) != FLAG_REUSE) {
continue;
-
+ }
RevObject ro = rw.peel(rw.parseAny(entry));
- if (ro instanceof RevCommit) {
- RevCommit rc = (RevCommit) ro;
- reuse.add(new BitmapCommit(rc, false, entry.getFlags()));
- rw.markUninteresting(rc);
+ if (!(ro instanceof RevCommit)) {
+ continue;
+ }
+ RevCommit rc = (RevCommit) ro;
+ reuseCommits.add(new BitmapCommit(rc, false, entry.getFlags()));
+ if (!reuse.contains(rc)) {
EWAHCompressedBitmap bitmap = bitmapRemapper.ofObjectType(
bitmapRemapper.getBitmap(rc), Constants.OBJ_COMMIT);
- writeBitmaps.addBitmap(rc, bitmap, 0);
- reuseBitmap.add(rc, Constants.OBJ_COMMIT);
+ reuse.or(new CompressedBitmap(bitmap, commitBitmapIndex));
}
}
- writeBitmaps.clearBitmaps(); // Remove temporary bitmaps
- // Do a RevWalk by commit time descending. Keep track of all the paths
- // from the wants.
- List<BitmapBuilder> paths = new ArrayList<BitmapBuilder>(want.size());
+ // Add branch tips that are not represented in old bitmap indices. Set
+ // up the RevWalk to walk the new commits not in the old packs.
+ List<BitmapBuilderEntry> tipCommitBitmaps = new ArrayList<BitmapBuilderEntry>(
+ want.size());
Set<RevCommit> peeledWant = new HashSet<RevCommit>(want.size());
for (AnyObjectId objectId : want) {
RevObject ro = rw.peel(rw.parseAny(objectId));
- if (ro instanceof RevCommit && !reuseBitmap.contains(ro)) {
- RevCommit rc = (RevCommit) ro;
- peeledWant.add(rc);
- rw.markStart(rc);
-
- BitmapBuilder bitmap = commitBitmapIndex.newBitmapBuilder();
- bitmap.or(reuseBitmap);
- bitmap.add(rc, Constants.OBJ_COMMIT);
- paths.add(bitmap);
+ if (!(ro instanceof RevCommit) || reuse.contains(ro)) {
+ continue;
}
+
+ RevCommit rc = (RevCommit) ro;
+ peeledWant.add(rc);
+ rw.markStart(rc);
+
+ BitmapBuilder bitmap = commitBitmapIndex.newBitmapBuilder();
+ bitmap.addObject(rc, Constants.OBJ_COMMIT);
+ tipCommitBitmaps.add(new BitmapBuilderEntry(rc, bitmap));
}
- // Update the paths from the wants and create a list of commits in
- // reverse iteration order.
- RevCommit[] commits = new RevCommit[expectedNumCommits];
+ // Create a list of commits in reverse order (older to newer).
+ // For each branch that contains the commit, mark its parents as being
+ // in the bitmap.
+ rw.setRevFilter(new NotInBitmapFilter(reuse));
+ RevCommit[] commits = new RevCommit[expectedCommitCount];
int pos = commits.length;
RevCommit rc;
- while ((rc = rw.next()) != null) {
+ while ((rc = rw.next()) != null && pos > 0) {
commits[--pos] = rc;
- for (BitmapBuilder path : paths) {
- if (path.contains(rc)) {
- for (RevCommit c : rc.getParents())
- path.add(c, Constants.OBJ_COMMIT);
+ for (BitmapBuilderEntry entry : tipCommitBitmaps) {
+ BitmapBuilder bitmap = entry.getBuilder();
+ if (!bitmap.contains(rc)) {
+ continue;
+ }
+ for (RevCommit c : rc.getParents()) {
+ if (reuse.contains(c)) {
+ continue;
+ }
+ bitmap.addObject(c, Constants.OBJ_COMMIT);
}
}
-
pm.update(1);
}
- // Remove the reused bitmaps from the paths
- if (!reuse.isEmpty())
- for (BitmapBuilder bitmap : paths)
- bitmap.andNot(reuseBitmap);
-
- // Sort the paths
- List<BitmapBuilder> distinctPaths = new ArrayList<BitmapBuilder>(paths.size());
- while (!paths.isEmpty()) {
- Collections.sort(paths, BUILDER_BY_CARDINALITY_DSC);
- BitmapBuilder largest = paths.remove(0);
- distinctPaths.add(largest);
+ // Sort the tip commit bitmaps. Find the one containing the most
+ // commits, remove those commits from the remaining bitmaps, resort and
+ // repeat.
+ List<BitmapBuilderEntry> orderedTipCommitBitmaps = new ArrayList<>(
+ tipCommitBitmaps.size());
+ while (!tipCommitBitmaps.isEmpty()) {
+ BitmapBuilderEntry largest =
+ Collections.max(tipCommitBitmaps, ORDER_BY_CARDINALITY);
+ tipCommitBitmaps.remove(largest);
+ orderedTipCommitBitmaps.add(largest);
// Update the remaining paths, by removing the objects from
// the path that was just added.
- for (int i = paths.size() - 1; i >= 0; i--)
- paths.get(i).andNot(largest);
+ for (int i = tipCommitBitmaps.size() - 1; i >= 0; i--) {
+ tipCommitBitmaps.get(i).getBuilder()
+ .andNot(largest.getBuilder());
+ }
}
- return new WalkResult(peeledWant, commits, pos, distinctPaths, reuse);
+ return new CommitSelectionHelper(peeledWant, commits, pos,
+ orderedTipCommitBitmaps, reuse, reuseCommits);
}
- private int nextSelectionDistance(int idx, int cardinality) {
- if (idx > cardinality)
+ /*-
+ * Returns the desired distance to the next bitmap based on the distance
+ * from the tip commit. Only differentiates recent from distant spans,
+ * selectCommits() handles the contiguous commits at the tip for active
+ * or inactive branches.
+ *
+ * A graph of this function looks like this, where
+ * the X axis is the distance from the tip commit and the Y axis is the
+ * bitmap selection distance.
+ *
+ * 5000 ____...
+ * /
+ * /
+ * /
+ * /
+ * 100 _____/
+ * 0 20100 25000
+ *
+ * Linear scaling between 20100 and 25000 prevents spans >100 for distances
+ * <20000 (otherwise, a span of 5000 would be returned for a distance of
+ * 21000, and the range 16000-20000 would have no selections).
+ */
+ int nextSpan(int distanceFromTip) {
+ if (distanceFromTip < 0) {
throw new IllegalArgumentException();
- int idxFromStart = cardinality - idx;
- int mustRegionEnd = 100;
- if (idxFromStart <= mustRegionEnd)
- return 0;
+ }
// Commits more toward the start will have more bitmaps.
- int minRegionEnd = 20000;
- if (idxFromStart <= minRegionEnd)
- return Math.min(idxFromStart - mustRegionEnd, minCommits);
+ if (distanceFromTip <= recentCommitCount) {
+ return recentCommitSpan;
+ }
- // Commits more toward the end will have fewer.
- int next = Math.min(idxFromStart - minRegionEnd, maxCommits);
- return Math.max(next, minCommits);
+ int next = Math.min(distanceFromTip - recentCommitCount,
+ distantCommitSpan);
+ return Math.max(next, recentCommitSpan);
}
PackWriterBitmapWalker newBitmapWalker() {
@@ -315,12 +504,14 @@ class PackWriterBitmapPreparer {
new ObjectWalk(reader), bitmapIndex, null);
}
+ /**
+ * A commit object for which a bitmap index should be built.
+ */
static final class BitmapCommit extends ObjectId {
private final boolean reuseWalker;
private final int flags;
- private BitmapCommit(
- AnyObjectId objectId, boolean reuseWalker, int flags) {
+ BitmapCommit(AnyObjectId objectId, boolean reuseWalker, int flags) {
super(objectId);
this.reuseWalker = reuseWalker;
this.flags = flags;
@@ -335,24 +526,62 @@ class PackWriterBitmapPreparer {
}
}
- private static final class WalkResult implements Iterable<RevCommit> {
- private final Set<? extends ObjectId> peeledWant;
- private final RevCommit[] commitsByOldest;
- private final int commitStartPos;
- private final List<BitmapBuilder> paths;
- private final Iterable<BitmapCommit> reuse;
+ /**
+ * A POJO representing a Pair<RevCommit, BitmapBuidler>.
+ */
+ private static final class BitmapBuilderEntry {
+ private final RevCommit commit;
+ private final BitmapBuilder builder;
+
+ BitmapBuilderEntry(RevCommit commit, BitmapBuilder builder) {
+ this.commit = commit;
+ this.builder = builder;
+ }
- private WalkResult(Set<? extends ObjectId> peeledWant,
+ RevCommit getCommit() {
+ return commit;
+ }
+
+ BitmapBuilder getBuilder() {
+ return builder;
+ }
+ }
+
+ /**
+ * Container for state used in the first phase of selecting commits, which
+ * walks all of the reachable commits via the branch tips (
+ * {@code peeledWants}), stores them in {@code commitsByOldest}, and sets up
+ * bitmaps for each branch tip ({@code tipCommitBitmaps}).
+ * {@code commitsByOldest} is initialized with an expected size of all
+ * commits, but may be smaller if some commits are unreachable, in which
+ * case {@code commitStartPos} will contain a positive offset to the root
+ * commit.
+ */
+ private static final class CommitSelectionHelper implements Iterable<RevCommit> {
+ final Set<? extends ObjectId> peeledWants;
+ final List<BitmapBuilderEntry> tipCommitBitmaps;
+
+ final BitmapBuilder reusedCommitsBitmap;
+ final Iterable<BitmapCommit> reusedCommits;
+ final RevCommit[] commitsByOldest;
+ final int commitStartPos;
+
+ CommitSelectionHelper(Set<? extends ObjectId> peeledWant,
RevCommit[] commitsByOldest, int commitStartPos,
- List<BitmapBuilder> paths, Iterable<BitmapCommit> reuse) {
- this.peeledWant = peeledWant;
+ List<BitmapBuilderEntry> bitmapEntries,
+ BitmapBuilder reusedCommitsBitmap,
+ Iterable<BitmapCommit> reuse) {
+ this.peeledWants = peeledWant;
this.commitsByOldest = commitsByOldest;
this.commitStartPos = commitStartPos;
- this.paths = paths;
- this.reuse = reuse;
+ this.tipCommitBitmaps = bitmapEntries;
+ this.reusedCommitsBitmap = reusedCommitsBitmap;
+ this.reusedCommits = reuse;
}
public Iterator<RevCommit> iterator() {
+ // Member variables referenced by this iterator will have synthetic
+ // accessors generated for them if they are made private.
return new Iterator<RevCommit>() {
int pos = commitStartPos;
@@ -369,5 +598,9 @@ class PackWriterBitmapPreparer {
}
};
}
+
+ int getCommitCount() {
+ return commitsByOldest.length - commitStartPos;
+ }
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapWalker.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapWalker.java
index 63a91cd..d9ac9ef 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapWalker.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapWalker.java
@@ -71,6 +71,8 @@ final class PackWriterBitmapWalker {
private final ProgressMonitor pm;
+ private long countOfBitmapIndexMisses;
+
PackWriterBitmapWalker(
ObjectWalk walker, BitmapIndex bitmapIndex, ProgressMonitor pm) {
this.walker = walker;
@@ -78,6 +80,10 @@ final class PackWriterBitmapWalker {
this.pm = (pm == null) ? NullProgressMonitor.INSTANCE : pm;
}
+ long getCountOfBitmapIndexMisses() {
+ return countOfBitmapIndexMisses;
+ }
+
BitmapBuilder findObjects(Set<? extends ObjectId> start, BitmapBuilder seen, boolean ignoreMissingStart)
throws MissingObjectException, IncorrectObjectTypeException,
IOException {
@@ -104,17 +110,30 @@ final class PackWriterBitmapWalker {
}
if (marked) {
- walker.setRevFilter(newRevFilter(seen, bitmapResult));
+ if (seen == null) {
+ walker.setRevFilter(new AddToBitmapFilter(bitmapResult));
+ } else {
+ walker.setRevFilter(
+ new AddUnseenToBitmapFilter(seen, bitmapResult));
+ }
while (walker.next() != null) {
// Iterate through all of the commits. The BitmapRevFilter does
// the work.
+ //
+ // filter.include returns true for commits that do not have
+ // a bitmap in bitmapIndex and are not reachable from a
+ // bitmap in bitmapIndex encountered earlier in the walk.
+ // Thus the number of commits returned by next() measures how
+ // much history was traversed without being able to make use
+ // of bitmaps.
pm.update(1);
+ countOfBitmapIndexMisses++;
}
RevObject ro;
while ((ro = walker.nextObject()) != null) {
- bitmapResult.add(ro, ro.getType());
+ bitmapResult.addObject(ro, ro.getType());
pm.update(1);
}
}
@@ -126,40 +145,100 @@ final class PackWriterBitmapWalker {
walker.reset();
}
- static RevFilter newRevFilter(
- final BitmapBuilder seen, final BitmapBuilder bitmapResult) {
- if (seen != null) {
- return new BitmapRevFilter() {
- protected boolean load(RevCommit cmit) {
- if (seen.contains(cmit))
- return false;
- return bitmapResult.add(cmit, Constants.OBJ_COMMIT);
- }
- };
+ /**
+ * A RevFilter that adds the visited commits to {@code bitmap} as a side
+ * effect.
+ * <p>
+ * When the walk hits a commit that is part of {@code bitmap}'s
+ * BitmapIndex, that entire bitmap is ORed into {@code bitmap} and the
+ * commit and its parents are marked as SEEN so that the walk does not
+ * have to visit its ancestors. This ensures the walk is very short if
+ * there is good bitmap coverage.
+ */
+ static class AddToBitmapFilter extends RevFilter {
+ private final BitmapBuilder bitmap;
+
+ AddToBitmapFilter(BitmapBuilder bitmap) {
+ this.bitmap = bitmap;
}
- return new BitmapRevFilter() {
- @Override
- protected boolean load(RevCommit cmit) {
- return bitmapResult.add(cmit, Constants.OBJ_COMMIT);
+
+ @Override
+ public final boolean include(RevWalk walker, RevCommit cmit) {
+ Bitmap visitedBitmap;
+
+ if (bitmap.contains(cmit)) {
+ // already included
+ } else if ((visitedBitmap = bitmap.getBitmapIndex()
+ .getBitmap(cmit)) != null) {
+ bitmap.or(visitedBitmap);
+ } else {
+ bitmap.addObject(cmit, Constants.OBJ_COMMIT);
+ return true;
}
- };
+
+ for (RevCommit p : cmit.getParents()) {
+ p.add(RevFlag.SEEN);
+ }
+ return false;
+ }
+
+ @Override
+ public final RevFilter clone() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public final boolean requiresCommitBody() {
+ return false;
+ }
}
- static abstract class BitmapRevFilter extends RevFilter {
- protected abstract boolean load(RevCommit cmit);
+ /**
+ * A RevFilter that adds the visited commits to {@code bitmap} as a side
+ * effect.
+ * <p>
+ * When the walk hits a commit that is part of {@code bitmap}'s
+ * BitmapIndex, that entire bitmap is ORed into {@code bitmap} and the
+ * commit and its parents are marked as SEEN so that the walk does not
+ * have to visit its ancestors. This ensures the walk is very short if
+ * there is good bitmap coverage.
+ * <p>
+ * Commits named in {@code seen} are considered already seen. If one is
+ * encountered, that commit and its parents will be marked with the SEEN
+ * flag to prevent the walk from visiting its ancestors.
+ */
+ static class AddUnseenToBitmapFilter extends RevFilter {
+ private final BitmapBuilder seen;
+ private final BitmapBuilder bitmap;
+
+ AddUnseenToBitmapFilter(BitmapBuilder seen, BitmapBuilder bitmapResult) {
+ this.seen = seen;
+ this.bitmap = bitmapResult;
+ }
@Override
public final boolean include(RevWalk walker, RevCommit cmit) {
- if (load(cmit))
+ Bitmap visitedBitmap;
+
+ if (seen.contains(cmit) || bitmap.contains(cmit)) {
+ // already seen or included
+ } else if ((visitedBitmap = bitmap.getBitmapIndex()
+ .getBitmap(cmit)) != null) {
+ bitmap.or(visitedBitmap);
+ } else {
+ bitmap.addObject(cmit, Constants.OBJ_COMMIT);
return true;
- for (RevCommit p : cmit.getParents())
+ }
+
+ for (RevCommit p : cmit.getParents()) {
p.add(RevFlag.SEEN);
+ }
return false;
}
@Override
public final RevFilter clone() {
- return this;
+ throw new UnsupportedOperationException();
}
@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsCachedPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/AlwaysFailUpdate.java
similarity index 63%
copy from org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsCachedPack.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/AlwaysFailUpdate.java
index 3da5184..12ef873 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsCachedPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/AlwaysFailUpdate.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, Google Inc.
+ * Copyright (C) 2016, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -41,45 +41,58 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.internal.storage.dfs;
+package org.eclipse.jgit.internal.storage.reftree;
import java.io.IOException;
-import org.eclipse.jgit.internal.storage.pack.CachedPack;
-import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
-import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
-import org.eclipse.jgit.internal.storage.pack.StoredObjectRepresentation;
+import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
-/** A DfsPackFile available for reuse as-is. */
-public class DfsCachedPack extends CachedPack {
- private final DfsPackFile pack;
+/** Update that always rejects with {@code LOCK_FAILURE}. */
+class AlwaysFailUpdate extends RefUpdate {
+ private final RefTreeDatabase refdb;
- DfsCachedPack(DfsPackFile pack) {
- this.pack = pack;
+ AlwaysFailUpdate(RefTreeDatabase refdb, String name) {
+ super(new ObjectIdRef.Unpeeled(Ref.Storage.NEW, name, null));
+ this.refdb = refdb;
+ setCheckConflicting(false);
}
- /** @return the description of the pack. */
- public DfsPackDescription getPackDescription() {
- return pack.getPackDescription();
+ @Override
+ protected RefDatabase getRefDatabase() {
+ return refdb;
+ }
+
+ @Override
+ protected Repository getRepository() {
+ return refdb.getRepository();
}
@Override
- public long getObjectCount() throws IOException {
- return getPackDescription().getObjectCount();
+ protected boolean tryLock(boolean deref) throws IOException {
+ return false;
}
@Override
- public long getDeltaCount() throws IOException {
- return getPackDescription().getDeltaCount();
+ protected void unlock() {
+ // No locks are held here.
}
@Override
- public boolean hasObject(ObjectToPack obj, StoredObjectRepresentation rep) {
- return ((DfsObjectRepresentation) rep).pack == pack;
+ protected Result doUpdate(Result desiredResult) {
+ return Result.LOCK_FAILURE;
}
- void copyAsIs(PackOutputStream out, boolean validate, DfsReader ctx)
- throws IOException {
- pack.copyPackAsIs(out, validate, ctx);
+ @Override
+ protected Result doDelete(Result desiredResult) {
+ return Result.LOCK_FAILURE;
+ }
+
+ @Override
+ protected Result doLink(String target) {
+ return Result.LOCK_FAILURE;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/Command.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/Command.java
new file mode 100644
index 0000000..dd08375
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/Command.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.reftree;
+
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.Constants.encode;
+import static org.eclipse.jgit.lib.FileMode.TYPE_GITLINK;
+import static org.eclipse.jgit.lib.FileMode.TYPE_SYMLINK;
+import static org.eclipse.jgit.lib.Ref.Storage.NETWORK;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevTag;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
+import org.eclipse.jgit.transport.ReceiveCommand.Result;
+
+/**
+ * Command to create, update or delete an entry inside a {@link RefTree}.
+ * <p>
+ * Unlike {@link ReceiveCommand} (which can only update a reference to an
+ * {@link ObjectId}), a RefTree Command can also create, modify or delete
+ * symbolic references to a target reference.
+ * <p>
+ * RefTree Commands may wrap a {@code ReceiveCommand} to allow callers to
+ * process an existing ReceiveCommand against a RefTree.
+ * <p>
+ * Commands should be passed into {@link RefTree#apply(java.util.Collection)}
+ * for processing.
+ */
+public class Command {
+ /**
+ * Set unprocessed commands as failed due to transaction aborted.
+ * <p>
+ * If a command is still {@link Result#NOT_ATTEMPTED} it will be set to
+ * {@link Result#REJECTED_OTHER_REASON}. If {@code why} is non-null its
+ * contents will be used as the message for the first command status.
+ *
+ * @param commands
+ * commands to mark as failed.
+ * @param why
+ * optional message to set on the first aborted command.
+ */
+ public static void abort(Iterable<Command> commands, @Nullable String why) {
+ if (why == null || why.isEmpty()) {
+ why = JGitText.get().transactionAborted;
+ }
+ for (Command c : commands) {
+ if (c.getResult() == NOT_ATTEMPTED) {
+ c.setResult(REJECTED_OTHER_REASON, why);
+ why = JGitText.get().transactionAborted;
+ }
+ }
+ }
+
+ private final Ref oldRef;
+ private final Ref newRef;
+ private final ReceiveCommand cmd;
+ private Result result;
+
+ /**
+ * Create a command to create, update or delete a reference.
+ * <p>
+ * At least one of {@code oldRef} or {@code newRef} must be supplied.
+ *
+ * @param oldRef
+ * expected value. Null if the ref should not exist.
+ * @param newRef
+ * desired value, must be peeled if not null and not symbolic.
+ * Null to delete the ref.
+ */
+ public Command(@Nullable Ref oldRef, @Nullable Ref newRef) {
+ this.oldRef = oldRef;
+ this.newRef = newRef;
+ this.cmd = null;
+ this.result = NOT_ATTEMPTED;
+
+ if (oldRef == null && newRef == null) {
+ throw new IllegalArgumentException();
+ }
+ if (newRef != null && !newRef.isPeeled() && !newRef.isSymbolic()) {
+ throw new IllegalArgumentException();
+ }
+ if (oldRef != null && newRef != null
+ && !oldRef.getName().equals(newRef.getName())) {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ /**
+ * Construct a RefTree command wrapped around a ReceiveCommand.
+ *
+ * @param rw
+ * walk instance to peel the {@code newId}.
+ * @param cmd
+ * command received from a push client.
+ * @throws MissingObjectException
+ * {@code oldId} or {@code newId} is missing.
+ * @throws IOException
+ * {@code oldId} or {@code newId} cannot be peeled.
+ */
+ public Command(RevWalk rw, ReceiveCommand cmd)
+ throws MissingObjectException, IOException {
+ this.oldRef = toRef(rw, cmd.getOldId(), cmd.getRefName(), false);
+ this.newRef = toRef(rw, cmd.getNewId(), cmd.getRefName(), true);
+ this.cmd = cmd;
+ }
+
+ static Ref toRef(RevWalk rw, ObjectId id, String name,
+ boolean mustExist) throws MissingObjectException, IOException {
+ if (ObjectId.zeroId().equals(id)) {
+ return null;
+ }
+
+ try {
+ RevObject o = rw.parseAny(id);
+ if (o instanceof RevTag) {
+ RevObject p = rw.peel(o);
+ return new ObjectIdRef.PeeledTag(NETWORK, name, id, p.copy());
+ }
+ return new ObjectIdRef.PeeledNonTag(NETWORK, name, id);
+ } catch (MissingObjectException e) {
+ if (mustExist) {
+ throw e;
+ }
+ return new ObjectIdRef.Unpeeled(NETWORK, name, id);
+ }
+ }
+
+ /** @return name of the reference affected by this command. */
+ public String getRefName() {
+ if (cmd != null) {
+ return cmd.getRefName();
+ } else if (newRef != null) {
+ return newRef.getName();
+ }
+ return oldRef.getName();
+ }
+
+ /**
+ * Set the result of this command.
+ *
+ * @param result
+ * the command result.
+ */
+ public void setResult(Result result) {
+ setResult(result, null);
+ }
+
+ /**
+ * Set the result of this command.
+ *
+ * @param result
+ * the command result.
+ * @param why
+ * optional message explaining the result status.
+ */
+ public void setResult(Result result, @Nullable String why) {
+ if (cmd != null) {
+ cmd.setResult(result, why);
+ } else {
+ this.result = result;
+ }
+ }
+
+ /** @return result of executing this command. */
+ public Result getResult() {
+ return cmd != null ? cmd.getResult() : result;
+ }
+
+ /** @return optional message explaining command failure. */
+ @Nullable
+ public String getMessage() {
+ return cmd != null ? cmd.getMessage() : null;
+ }
+
+ /**
+ * Old peeled reference.
+ *
+ * @return the old reference; null if the command is creating the reference.
+ */
+ @Nullable
+ public Ref getOldRef() {
+ return oldRef;
+ }
+
+ /**
+ * New peeled reference.
+ *
+ * @return the new reference; null if the command is deleting the reference.
+ */
+ @Nullable
+ public Ref getNewRef() {
+ return newRef;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder();
+ append(s, oldRef, "CREATE"); //$NON-NLS-1$
+ s.append(' ');
+ append(s, newRef, "DELETE"); //$NON-NLS-1$
+ s.append(' ').append(getRefName());
+ s.append(' ').append(getResult());
+ if (getMessage() != null) {
+ s.append(' ').append(getMessage());
+ }
+ return s.toString();
+ }
+
+ private static void append(StringBuilder s, Ref r, String nullName) {
+ if (r == null) {
+ s.append(nullName);
+ } else if (r.isSymbolic()) {
+ s.append(r.getTarget().getName());
+ } else {
+ ObjectId id = r.getObjectId();
+ if (id != null) {
+ s.append(id.name());
+ }
+ }
+ }
+
+ /**
+ * Check the entry is consistent with either the old or the new ref.
+ *
+ * @param entry
+ * current entry; null if the entry does not exist.
+ * @return true if entry matches {@link #getOldRef()} or
+ * {@link #getNewRef()}; otherwise false.
+ */
+ boolean checkRef(@Nullable DirCacheEntry entry) {
+ if (entry != null && entry.getRawMode() == 0) {
+ entry = null;
+ }
+ return check(entry, oldRef) || check(entry, newRef);
+ }
+
+ private static boolean check(@Nullable DirCacheEntry cur,
+ @Nullable Ref exp) {
+ if (cur == null) {
+ // Does not exist, ok if oldRef does not exist.
+ return exp == null;
+ } else if (exp == null) {
+ // Expected to not exist, but currently exists, fail.
+ return false;
+ }
+
+ if (exp.isSymbolic()) {
+ String dst = exp.getTarget().getName();
+ return cur.getRawMode() == TYPE_SYMLINK
+ && cur.getObjectId().equals(symref(dst));
+ }
+
+ return cur.getRawMode() == TYPE_GITLINK
+ && cur.getObjectId().equals(exp.getObjectId());
+ }
+
+ static ObjectId symref(String s) {
+ @SuppressWarnings("resource")
+ ObjectInserter.Formatter fmt = new ObjectInserter.Formatter();
+ return fmt.idFor(OBJ_BLOB, encode(s));
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTree.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTree.java
new file mode 100644
index 0000000..85690c8
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTree.java
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.reftree;
+
+import static org.eclipse.jgit.lib.Constants.HEAD;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.Constants.R_REFS;
+import static org.eclipse.jgit.lib.Constants.encode;
+import static org.eclipse.jgit.lib.FileMode.GITLINK;
+import static org.eclipse.jgit.lib.FileMode.SYMLINK;
+import static org.eclipse.jgit.lib.FileMode.TYPE_GITLINK;
+import static org.eclipse.jgit.lib.FileMode.TYPE_SYMLINK;
+import static org.eclipse.jgit.lib.Ref.Storage.NEW;
+import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
+import static org.eclipse.jgit.lib.RefDatabase.MAX_SYMBOLIC_REF_DEPTH;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.LOCK_FAILURE;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheBuilder;
+import org.eclipse.jgit.dircache.DirCacheEditor;
+import org.eclipse.jgit.dircache.DirCacheEditor.DeletePath;
+import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.DirCacheNameConflictException;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.SymbolicRef;
+import org.eclipse.jgit.revwalk.RevTree;
+import org.eclipse.jgit.util.RawParseUtils;
+
+/**
+ * Tree of references in the reference graph.
+ * <p>
+ * The root corresponds to the {@code "refs/"} subdirectory, for example the
+ * default reference {@code "refs/heads/master"} is stored at path
+ * {@code "heads/master"} in a {@code RefTree}.
+ * <p>
+ * Normal references are stored as {@link FileMode#GITLINK} tree entries. The
+ * ObjectId in the tree entry is the ObjectId the reference refers to.
+ * <p>
+ * Symbolic references are stored as {@link FileMode#SYMLINK} entries, with the
+ * blob storing the name of the target reference.
+ * <p>
+ * Annotated tags also store the peeled object using a {@code GITLINK} entry
+ * with the suffix <code>" ^"</code> (space carrot), for example
+ * {@code "tags/v1.0"} stores the annotated tag object, while
+ * <code>"tags/v1.0 ^"</code> stores the commit the tag annotates.
+ * <p>
+ * {@code HEAD} is a special case and stored as {@code "..HEAD"}.
+ */
+public class RefTree {
+ /** Suffix applied to GITLINK to indicate its the peeled value of a tag. */
+ public static final String PEELED_SUFFIX = " ^"; //$NON-NLS-1$
+ static final String ROOT_DOTDOT = ".."; //$NON-NLS-1$
+
+ /**
+ * Create an empty reference tree.
+ *
+ * @return a new empty reference tree.
+ */
+ public static RefTree newEmptyTree() {
+ return new RefTree(DirCache.newInCore());
+ }
+
+ /**
+ * Load a reference tree.
+ *
+ * @param reader
+ * reader to scan the reference tree with.
+ * @param tree
+ * the tree to read.
+ * @return the ref tree read from the commit.
+ * @throws IOException
+ * the repository cannot be accessed through the reader.
+ * @throws CorruptObjectException
+ * a tree object is corrupt and cannot be read.
+ * @throws IncorrectObjectTypeException
+ * a tree object wasn't actually a tree.
+ * @throws MissingObjectException
+ * a reference tree object doesn't exist.
+ */
+ public static RefTree read(ObjectReader reader, RevTree tree)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ CorruptObjectException, IOException {
+ return new RefTree(DirCache.read(reader, tree));
+ }
+
+ private DirCache contents;
+ private Map<ObjectId, String> pendingBlobs;
+
+ private RefTree(DirCache dc) {
+ this.contents = dc;
+ }
+
+ /**
+ * Read one reference.
+ * <p>
+ * References are always returned peeled ({@link Ref#isPeeled()} is true).
+ * If the reference points to an annotated tag, the returned reference will
+ * be peeled and contain {@link Ref#getPeeledObjectId()}.
+ * <p>
+ * If the reference is a symbolic reference and the chain depth is less than
+ * {@link org.eclipse.jgit.lib.RefDatabase#MAX_SYMBOLIC_REF_DEPTH} the
+ * returned reference is resolved. If the chain depth is longer, the
+ * symbolic reference is returned without resolving.
+ *
+ * @param reader
+ * to access objects necessary to read the requested reference.
+ * @param name
+ * name of the reference to read.
+ * @return the reference; null if it does not exist.
+ * @throws IOException
+ * cannot read a symbolic reference target.
+ */
+ @Nullable
+ public Ref exactRef(ObjectReader reader, String name) throws IOException {
+ Ref r = readRef(reader, name);
+ if (r == null) {
+ return null;
+ } else if (r.isSymbolic()) {
+ return resolve(reader, r, 0);
+ }
+
+ DirCacheEntry p = contents.getEntry(peeledPath(name));
+ if (p != null && p.getRawMode() == TYPE_GITLINK) {
+ return new ObjectIdRef.PeeledTag(PACKED, r.getName(),
+ r.getObjectId(), p.getObjectId());
+ }
+ return r;
+ }
+
+ private Ref readRef(ObjectReader reader, String name) throws IOException {
+ DirCacheEntry e = contents.getEntry(refPath(name));
+ return e != null ? toRef(reader, e, name) : null;
+ }
+
+ private Ref toRef(ObjectReader reader, DirCacheEntry e, String name)
+ throws IOException {
+ int mode = e.getRawMode();
+ if (mode == TYPE_GITLINK) {
+ ObjectId id = e.getObjectId();
+ return new ObjectIdRef.PeeledNonTag(PACKED, name, id);
+ }
+
+ if (mode == TYPE_SYMLINK) {
+ ObjectId id = e.getObjectId();
+ String n = pendingBlobs != null ? pendingBlobs.get(id) : null;
+ if (n == null) {
+ byte[] bin = reader.open(id, OBJ_BLOB).getCachedBytes();
+ n = RawParseUtils.decode(bin);
+ }
+ Ref dst = new ObjectIdRef.Unpeeled(NEW, n, null);
+ return new SymbolicRef(name, dst);
+ }
+
+ return null; // garbage file or something; not a reference.
+ }
+
+ private Ref resolve(ObjectReader reader, Ref ref, int depth)
+ throws IOException {
+ if (ref.isSymbolic() && depth < MAX_SYMBOLIC_REF_DEPTH) {
+ Ref r = readRef(reader, ref.getTarget().getName());
+ if (r == null) {
+ return ref;
+ }
+ Ref dst = resolve(reader, r, depth + 1);
+ return new SymbolicRef(ref.getName(), dst);
+ }
+ return ref;
+ }
+
+ /**
+ * Attempt a batch of commands against this RefTree.
+ * <p>
+ * The batch is applied atomically, either all commands apply at once, or
+ * they all reject and the RefTree is left unmodified.
+ * <p>
+ * On success (when this method returns {@code true}) the command results
+ * are left as-is (probably {@code NOT_ATTEMPTED}). Result fields are set
+ * only when this method returns {@code false} to indicate failure.
+ *
+ * @param cmdList
+ * to apply. All commands should still have result NOT_ATTEMPTED.
+ * @return true if the commands applied; false if they were rejected.
+ */
+ public boolean apply(Collection<Command> cmdList) {
+ try {
+ DirCacheEditor ed = contents.editor();
+ for (Command cmd : cmdList) {
+ if (!isValidRef(cmd)) {
+ cmd.setResult(REJECTED_OTHER_REASON,
+ JGitText.get().funnyRefname);
+ Command.abort(cmdList, null);
+ return false;
+ }
+ apply(ed, cmd);
+ }
+ ed.finish();
+ return true;
+ } catch (DirCacheNameConflictException e) {
+ String r1 = refName(e.getPath1());
+ String r2 = refName(e.getPath2());
+ for (Command cmd : cmdList) {
+ if (r1.equals(cmd.getRefName())
+ || r2.equals(cmd.getRefName())) {
+ cmd.setResult(LOCK_FAILURE);
+ break;
+ }
+ }
+ Command.abort(cmdList, null);
+ return false;
+ } catch (LockFailureException e) {
+ Command.abort(cmdList, null);
+ return false;
+ }
+ }
+
+ private static boolean isValidRef(Command cmd) {
+ String n = cmd.getRefName();
+ return HEAD.equals(n) || Repository.isValidRefName(n);
+ }
+
+ private void apply(DirCacheEditor ed, final Command cmd) {
+ String path = refPath(cmd.getRefName());
+ Ref oldRef = cmd.getOldRef();
+ final Ref newRef = cmd.getNewRef();
+
+ if (newRef == null) {
+ checkRef(contents.getEntry(path), cmd);
+ ed.add(new DeletePath(path));
+ cleanupPeeledRef(ed, oldRef);
+ return;
+ }
+
+ if (newRef.isSymbolic()) {
+ final String dst = newRef.getTarget().getName();
+ ed.add(new PathEdit(path) {
+ @Override
+ public void apply(DirCacheEntry ent) {
+ checkRef(ent, cmd);
+ ObjectId id = Command.symref(dst);
+ ent.setFileMode(SYMLINK);
+ ent.setObjectId(id);
+ if (pendingBlobs == null) {
+ pendingBlobs = new HashMap<>(4);
+ }
+ pendingBlobs.put(id, dst);
+ }
+ }.setReplace(false));
+ cleanupPeeledRef(ed, oldRef);
+ return;
+ }
+
+ ed.add(new PathEdit(path) {
+ @Override
+ public void apply(DirCacheEntry ent) {
+ checkRef(ent, cmd);
+ ent.setFileMode(GITLINK);
+ ent.setObjectId(newRef.getObjectId());
+ }
+ }.setReplace(false));
+
+ if (newRef.getPeeledObjectId() != null) {
+ ed.add(new PathEdit(peeledPath(newRef.getName())) {
+ @Override
+ public void apply(DirCacheEntry ent) {
+ ent.setFileMode(GITLINK);
+ ent.setObjectId(newRef.getPeeledObjectId());
+ }
+ }.setReplace(false));
+ } else {
+ cleanupPeeledRef(ed, oldRef);
+ }
+ }
+
+ private static void checkRef(@Nullable DirCacheEntry ent, Command cmd) {
+ if (!cmd.checkRef(ent)) {
+ cmd.setResult(LOCK_FAILURE);
+ throw new LockFailureException();
+ }
+ }
+
+ private static void cleanupPeeledRef(DirCacheEditor ed, Ref ref) {
+ if (ref != null && !ref.isSymbolic()
+ && (!ref.isPeeled() || ref.getPeeledObjectId() != null)) {
+ ed.add(new DeletePath(peeledPath(ref.getName())));
+ }
+ }
+
+ /**
+ * Convert a path name in a RefTree to the reference name known by Git.
+ *
+ * @param path
+ * name read from the RefTree structure, for example
+ * {@code "heads/master"}.
+ * @return reference name for the path, {@code "refs/heads/master"}.
+ */
+ public static String refName(String path) {
+ if (path.startsWith(ROOT_DOTDOT)) {
+ return path.substring(2);
+ }
+ return R_REFS + path;
+ }
+
+ static String refPath(String name) {
+ if (name.startsWith(R_REFS)) {
+ return name.substring(R_REFS.length());
+ }
+ return ROOT_DOTDOT + name;
+ }
+
+ private static String peeledPath(String name) {
+ return refPath(name) + PEELED_SUFFIX;
+ }
+
+ /**
+ * Write this reference tree.
+ *
+ * @param inserter
+ * inserter to use when writing trees to the object database.
+ * Caller is responsible for flushing the inserter before trying
+ * to read the objects, or exposing them through a reference.
+ * @return the top level tree.
+ * @throws IOException
+ * a tree could not be written.
+ */
+ public ObjectId writeTree(ObjectInserter inserter) throws IOException {
+ if (pendingBlobs != null) {
+ for (String s : pendingBlobs.values()) {
+ inserter.insert(OBJ_BLOB, encode(s));
+ }
+ pendingBlobs = null;
+ }
+ return contents.writeTree(inserter);
+ }
+
+ /** @return a deep copy of this RefTree. */
+ public RefTree copy() {
+ RefTree r = new RefTree(DirCache.newInCore());
+ DirCacheBuilder b = r.contents.builder();
+ for (int i = 0; i < contents.getEntryCount(); i++) {
+ b.add(new DirCacheEntry(contents.getEntry(i)));
+ }
+ b.finish();
+ if (pendingBlobs != null) {
+ r.pendingBlobs = new HashMap<>(pendingBlobs);
+ }
+ return r;
+ }
+
+ private static class LockFailureException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeBatch.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeBatch.java
new file mode 100644
index 0000000..a55a9f5
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeBatch.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.reftree;
+
+import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_NONFASTFORWARD;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
+import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE;
+import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE_NONFASTFORWARD;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
+
+/** Batch update a {@link RefTreeDatabase}. */
+class RefTreeBatch extends BatchRefUpdate {
+ private final RefTreeDatabase refdb;
+ private Ref src;
+ private ObjectId parentCommitId;
+ private ObjectId parentTreeId;
+ private RefTree tree;
+ private PersonIdent author;
+ private ObjectId newCommitId;
+
+ RefTreeBatch(RefTreeDatabase refdb) {
+ super(refdb);
+ this.refdb = refdb;
+ }
+
+ @Override
+ public void execute(RevWalk rw, ProgressMonitor monitor)
+ throws IOException {
+ List<Command> todo = new ArrayList<>(getCommands().size());
+ for (ReceiveCommand c : getCommands()) {
+ if (!isAllowNonFastForwards()) {
+ if (c.getType() == UPDATE) {
+ c.updateType(rw);
+ }
+ if (c.getType() == UPDATE_NONFASTFORWARD) {
+ c.setResult(REJECTED_NONFASTFORWARD);
+ ReceiveCommand.abort(getCommands());
+ return;
+ }
+ }
+ todo.add(new Command(rw, c));
+ }
+ init(rw);
+ execute(rw, todo);
+ }
+
+ void init(RevWalk rw) throws IOException {
+ src = refdb.getBootstrap().exactRef(refdb.getTxnCommitted());
+ if (src != null && src.getObjectId() != null) {
+ RevCommit c = rw.parseCommit(src.getObjectId());
+ parentCommitId = c;
+ parentTreeId = c.getTree();
+ tree = RefTree.read(rw.getObjectReader(), c.getTree());
+ } else {
+ parentCommitId = ObjectId.zeroId();
+ parentTreeId = new ObjectInserter.Formatter()
+ .idFor(OBJ_TREE, new byte[] {});
+ tree = RefTree.newEmptyTree();
+ }
+ }
+
+ @Nullable
+ Ref exactRef(ObjectReader reader, String name) throws IOException {
+ return tree.exactRef(reader, name);
+ }
+
+ /**
+ * Execute an update from {@link RefTreeUpdate} or {@link RefTreeRename}.
+ *
+ * @param rw
+ * current RevWalk handling the update or rename.
+ * @param todo
+ * commands to execute. Must never be a bootstrap reference name.
+ * @throws IOException
+ * the storage system is unable to read or write data.
+ */
+ void execute(RevWalk rw, List<Command> todo) throws IOException {
+ for (Command c : todo) {
+ if (c.getResult() != NOT_ATTEMPTED) {
+ Command.abort(todo, null);
+ return;
+ }
+ if (refdb.conflictsWithBootstrap(c.getRefName())) {
+ c.setResult(REJECTED_OTHER_REASON, MessageFormat
+ .format(JGitText.get().invalidRefName, c.getRefName()));
+ Command.abort(todo, null);
+ return;
+ }
+ }
+
+ if (apply(todo) && newCommitId != null) {
+ commit(rw, todo);
+ }
+ }
+
+ private boolean apply(List<Command> todo) throws IOException {
+ if (!tree.apply(todo)) {
+ // apply set rejection information on commands.
+ return false;
+ }
+
+ Repository repo = refdb.getRepository();
+ try (ObjectInserter ins = repo.newObjectInserter()) {
+ CommitBuilder b = new CommitBuilder();
+ b.setTreeId(tree.writeTree(ins));
+ if (parentTreeId.equals(b.getTreeId())) {
+ for (Command c : todo) {
+ c.setResult(OK);
+ }
+ return true;
+ }
+ if (!parentCommitId.equals(ObjectId.zeroId())) {
+ b.setParentId(parentCommitId);
+ }
+
+ author = getRefLogIdent();
+ if (author == null) {
+ author = new PersonIdent(repo);
+ }
+ b.setAuthor(author);
+ b.setCommitter(author);
+ b.setMessage(getRefLogMessage());
+ newCommitId = ins.insert(b);
+ ins.flush();
+ }
+ return true;
+ }
+
+ private void commit(RevWalk rw, List<Command> todo) throws IOException {
+ ReceiveCommand commit = new ReceiveCommand(
+ parentCommitId, newCommitId,
+ refdb.getTxnCommitted());
+ updateBootstrap(rw, commit);
+
+ if (commit.getResult() == OK) {
+ for (Command c : todo) {
+ c.setResult(OK);
+ }
+ } else {
+ Command.abort(todo, commit.getResult().name());
+ }
+ }
+
+ private void updateBootstrap(RevWalk rw, ReceiveCommand commit)
+ throws IOException {
+ BatchRefUpdate u = refdb.getBootstrap().newBatchUpdate();
+ u.setAllowNonFastForwards(true);
+ u.setPushCertificate(getPushCertificate());
+ if (isRefLogDisabled()) {
+ u.disableRefLog();
+ } else {
+ u.setRefLogIdent(author);
+ u.setRefLogMessage(getRefLogMessage(), false);
+ }
+ u.addCommand(commit);
+ u.execute(rw, NullProgressMonitor.INSTANCE);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabase.java
new file mode 100644
index 0000000..dc60311
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeDatabase.java
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.reftree;
+
+import static org.eclipse.jgit.lib.Ref.Storage.LOOSE;
+import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Ref.Storage;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.RefRename;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.SymbolicRef;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevTag;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.util.RefList;
+import org.eclipse.jgit.util.RefMap;
+
+/**
+ * Reference database backed by a {@link RefTree}.
+ * <p>
+ * The storage for RefTreeDatabase has two parts. The main part is a native Git
+ * tree object stored under the {@code refs/txn} namespace. To avoid cycles,
+ * references to {@code refs/txn} are not stored in that tree object, but
+ * instead in a "bootstrap" layer, which is a separate {@link RefDatabase} such
+ * as {@link org.eclipse.jgit.internal.storage.file.RefDirectory} using local
+ * reference files inside of {@code $GIT_DIR/refs}.
+ */
+public class RefTreeDatabase extends RefDatabase {
+ private final Repository repo;
+ private final RefDatabase bootstrap;
+ private final String txnCommitted;
+
+ @Nullable
+ private final String txnNamespace;
+ private volatile Scanner.Result refs;
+
+ /**
+ * Create a RefTreeDb for a repository.
+ *
+ * @param repo
+ * the repository using references in this database.
+ * @param bootstrap
+ * bootstrap reference database storing the references that
+ * anchor the {@link RefTree}.
+ */
+ public RefTreeDatabase(Repository repo, RefDatabase bootstrap) {
+ Config cfg = repo.getConfig();
+ String committed = cfg.getString("reftree", null, "committedRef"); //$NON-NLS-1$ //$NON-NLS-2$
+ if (committed == null || committed.isEmpty()) {
+ committed = "refs/txn/committed"; //$NON-NLS-1$
+ }
+
+ this.repo = repo;
+ this.bootstrap = bootstrap;
+ this.txnNamespace = initNamespace(committed);
+ this.txnCommitted = committed;
+ }
+
+ /**
+ * Create a RefTreeDb for a repository.
+ *
+ * @param repo
+ * the repository using references in this database.
+ * @param bootstrap
+ * bootstrap reference database storing the references that
+ * anchor the {@link RefTree}.
+ * @param txnCommitted
+ * name of the bootstrap reference holding the committed RefTree.
+ */
+ public RefTreeDatabase(Repository repo, RefDatabase bootstrap,
+ String txnCommitted) {
+ this.repo = repo;
+ this.bootstrap = bootstrap;
+ this.txnNamespace = initNamespace(txnCommitted);
+ this.txnCommitted = txnCommitted;
+ }
+
+ private static String initNamespace(String committed) {
+ int s = committed.lastIndexOf('/');
+ if (s < 0) {
+ return null;
+ }
+ return committed.substring(0, s + 1); // Keep trailing '/'.
+ }
+
+ Repository getRepository() {
+ return repo;
+ }
+
+ /**
+ * @return the bootstrap reference database, which must be used to access
+ * {@link #getTxnCommitted()}, {@link #getTxnNamespace()}.
+ */
+ public RefDatabase getBootstrap() {
+ return bootstrap;
+ }
+
+ /** @return name of bootstrap reference anchoring committed RefTree. */
+ public String getTxnCommitted() {
+ return txnCommitted;
+ }
+
+ /**
+ * @return namespace used by bootstrap layer, e.g. {@code refs/txn/}.
+ * Always ends in {@code '/'}.
+ */
+ @Nullable
+ public String getTxnNamespace() {
+ return txnNamespace;
+ }
+
+ @Override
+ public void create() throws IOException {
+ bootstrap.create();
+ }
+
+ @Override
+ public boolean performsAtomicTransactions() {
+ return true;
+ }
+
+ @Override
+ public void refresh() {
+ bootstrap.refresh();
+ }
+
+ @Override
+ public void close() {
+ refs = null;
+ bootstrap.close();
+ }
+
+ @Override
+ public Ref getRef(String name) throws IOException {
+ return findRef(getRefs(ALL), name);
+ }
+
+ @Override
+ public Ref exactRef(String name) throws IOException {
+ if (conflictsWithBootstrap(name)) {
+ return null;
+ }
+
+ boolean partial = false;
+ Ref src = bootstrap.exactRef(txnCommitted);
+ Scanner.Result c = refs;
+ if (c == null || !c.refTreeId.equals(idOf(src))) {
+ c = Scanner.scanRefTree(repo, src, prefixOf(name), false);
+ partial = true;
+ }
+
+ Ref r = c.all.get(name);
+ if (r != null && r.isSymbolic()) {
+ r = c.sym.get(name);
+ if (partial && r.getObjectId() == null) {
+ // Attempting exactRef("HEAD") with partial scan will leave
+ // an unresolved symref as its target e.g. refs/heads/master
+ // was not read by the partial scan. Scan everything instead.
+ return getRefs(ALL).get(name);
+ }
+ }
+ return r;
+ }
+
+ private static String prefixOf(String name) {
+ int s = name.lastIndexOf('/');
+ if (s >= 0) {
+ return name.substring(0, s);
+ }
+ return ""; //$NON-NLS-1$
+ }
+
+ @Override
+ public Map<String, Ref> getRefs(String prefix) throws IOException {
+ if (!prefix.isEmpty() && prefix.charAt(prefix.length() - 1) != '/') {
+ return new HashMap<>(0);
+ }
+
+ Ref src = bootstrap.exactRef(txnCommitted);
+ Scanner.Result c = refs;
+ if (c == null || !c.refTreeId.equals(idOf(src))) {
+ c = Scanner.scanRefTree(repo, src, prefix, true);
+ if (prefix.isEmpty()) {
+ refs = c;
+ }
+ }
+ return new RefMap(prefix, RefList.<Ref> emptyList(), c.all, c.sym);
+ }
+
+ private static ObjectId idOf(@Nullable Ref src) {
+ return src != null && src.getObjectId() != null
+ ? src.getObjectId()
+ : ObjectId.zeroId();
+ }
+
+ @Override
+ public List<Ref> getAdditionalRefs() throws IOException {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public Ref peel(Ref ref) throws IOException {
+ Ref i = ref.getLeaf();
+ ObjectId id = i.getObjectId();
+ if (i.isPeeled() || id == null) {
+ return ref;
+ }
+ try (RevWalk rw = new RevWalk(repo)) {
+ RevObject obj = rw.parseAny(id);
+ if (obj instanceof RevTag) {
+ ObjectId p = rw.peel(obj).copy();
+ i = new ObjectIdRef.PeeledTag(PACKED, i.getName(), id, p);
+ } else {
+ i = new ObjectIdRef.PeeledNonTag(PACKED, i.getName(), id);
+ }
+ }
+ return recreate(ref, i);
+ }
+
+ private static Ref recreate(Ref old, Ref leaf) {
+ if (old.isSymbolic()) {
+ Ref dst = recreate(old.getTarget(), leaf);
+ return new SymbolicRef(old.getName(), dst);
+ }
+ return leaf;
+ }
+
+ @Override
+ public boolean isNameConflicting(String name) throws IOException {
+ return conflictsWithBootstrap(name)
+ || !getConflictingNames(name).isEmpty();
+ }
+
+ @Override
+ public BatchRefUpdate newBatchUpdate() {
+ return new RefTreeBatch(this);
+ }
+
+ @Override
+ public RefUpdate newUpdate(String name, boolean detach) throws IOException {
+ if (conflictsWithBootstrap(name)) {
+ return new AlwaysFailUpdate(this, name);
+ }
+
+ Ref r = exactRef(name);
+ if (r == null) {
+ r = new ObjectIdRef.Unpeeled(Storage.NEW, name, null);
+ }
+
+ boolean detaching = detach && r.isSymbolic();
+ if (detaching) {
+ r = new ObjectIdRef.Unpeeled(LOOSE, name, r.getObjectId());
+ }
+
+ RefTreeUpdate u = new RefTreeUpdate(this, r);
+ if (detaching) {
+ u.setDetachingSymbolicRef();
+ }
+ return u;
+ }
+
+ @Override
+ public RefRename newRename(String fromName, String toName)
+ throws IOException {
+ RefUpdate from = newUpdate(fromName, true);
+ RefUpdate to = newUpdate(toName, true);
+ return new RefTreeRename(this, from, to);
+ }
+
+ boolean conflictsWithBootstrap(String name) {
+ if (txnNamespace != null && name.startsWith(txnNamespace)) {
+ return true;
+ } else if (txnCommitted.equals(name)) {
+ return true;
+ } else if (name.length() > txnCommitted.length()
+ && name.charAt(txnCommitted.length()) == '/'
+ && name.startsWith(txnCommitted)) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeNames.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeNames.java
new file mode 100644
index 0000000..239a745
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeNames.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.reftree;
+
+import static org.eclipse.jgit.lib.RefDatabase.ALL;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
+
+/** Magic reference name logic for RefTrees. */
+public class RefTreeNames {
+ /**
+ * Suffix used on a {@link RefTreeDatabase#getTxnNamespace()} for user data.
+ * <p>
+ * A {@link RefTreeDatabase}'s namespace may include a subspace (e.g.
+ * {@code "refs/txn/stage/"}) containing commit objects from the usual user
+ * portion of the repository (e.g. {@code "refs/heads/"}). These should be
+ * packed by the garbage collector alongside other user content rather than
+ * with the RefTree.
+ */
+ private static final String STAGE = "stage/"; //$NON-NLS-1$
+
+ /**
+ * Determine if the reference is likely to be a RefTree.
+ *
+ * @param refdb
+ * database instance.
+ * @param ref
+ * reference name.
+ * @return {@code true} if the reference is a RefTree.
+ */
+ public static boolean isRefTree(RefDatabase refdb, String ref) {
+ if (refdb instanceof RefTreeDatabase) {
+ RefTreeDatabase b = (RefTreeDatabase) refdb;
+ if (ref.equals(b.getTxnCommitted())) {
+ return true;
+ }
+
+ String namespace = b.getTxnNamespace();
+ if (namespace != null
+ && ref.startsWith(namespace)
+ && !ref.startsWith(namespace + STAGE)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Snapshot all references from a RefTreeDatabase and its bootstrap.
+ * <p>
+ * There may be name conflicts with multiple {@link Ref} objects containing
+ * the same name in the returned collection.
+ *
+ * @param refdb
+ * database instance.
+ * @return all known references.
+ * @throws IOException
+ * references cannot be enumerated.
+ */
+ public static Collection<Ref> allRefs(RefDatabase refdb)
+ throws IOException {
+ Collection<Ref> refs = refdb.getRefs(ALL).values();
+ if (!(refdb instanceof RefTreeDatabase)) {
+ return refs;
+ }
+
+ RefDatabase bootstrap = ((RefTreeDatabase) refdb).getBootstrap();
+ Collection<Ref> br = bootstrap.getRefs(ALL).values();
+ List<Ref> all = new ArrayList<>(refs.size() + br.size());
+ all.addAll(refs);
+ all.addAll(br);
+ return all;
+ }
+
+ private RefTreeNames() {
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListNotesCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeRename.java
similarity index 50%
copy from org.eclipse.jgit/src/org/eclipse/jgit/api/ListNotesCommand.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeRename.java
index 84fa355..5fd7ecd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ListNotesCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeRename.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, Chris Aniszczyk <caniszczyk at gmail.com>
+ * Copyright (C) 2016, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -40,81 +40,82 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.api;
+
+package org.eclipse.jgit.internal.storage.reftree;
+
+import static org.eclipse.jgit.lib.Constants.HEAD;
+import static org.eclipse.jgit.lib.RefUpdate.Result.REJECTED;
+import static org.eclipse.jgit.lib.RefUpdate.Result.RENAMED;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
-import org.eclipse.jgit.api.errors.GitAPIException;
-import org.eclipse.jgit.api.errors.JGitInternalException;
-import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdRef;
import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.notes.Note;
-import org.eclipse.jgit.notes.NoteMap;
-import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.lib.RefRename;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.RefUpdate.Result;
+import org.eclipse.jgit.lib.SymbolicRef;
import org.eclipse.jgit.revwalk.RevWalk;
-/**
- * List object notes.
- *
- * @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-notes.html"
- * >Git documentation about Notes</a>
- */
-public class ListNotesCommand extends GitCommand<List<Note>> {
+/** Single reference rename to {@link RefTreeDatabase}. */
+class RefTreeRename extends RefRename {
+ private final RefTreeDatabase refdb;
- private String notesRef = Constants.R_NOTES_COMMITS;
-
- /**
- * @param repo
- */
- protected ListNotesCommand(Repository repo) {
- super(repo);
+ RefTreeRename(RefTreeDatabase refdb, RefUpdate src, RefUpdate dst) {
+ super(src, dst);
+ this.refdb = refdb;
}
- /**
- * @return the requested notes
- */
- public List<Note> call() throws GitAPIException {
- checkCallable();
- List<Note> notes = new ArrayList<Note>();
- RevWalk walk = new RevWalk(repo);
- NoteMap map = NoteMap.newEmptyMap();
- try {
- Ref ref = repo.getRef(notesRef);
- // if we have a notes ref, use it
- if (ref != null) {
- RevCommit notesCommit = walk.parseCommit(ref.getObjectId());
- map = NoteMap.read(walk.getObjectReader(), notesCommit);
+ @Override
+ protected Result doRename() throws IOException {
+ try (RevWalk rw = new RevWalk(refdb.getRepository())) {
+ RefTreeBatch batch = new RefTreeBatch(refdb);
+ batch.setRefLogIdent(getRefLogIdent());
+ batch.setRefLogMessage(getRefLogMessage(), false);
+ batch.init(rw);
+
+ Ref head = batch.exactRef(rw.getObjectReader(), HEAD);
+ Ref oldRef = batch.exactRef(rw.getObjectReader(), source.getName());
+ if (oldRef == null) {
+ return REJECTED;
}
- Iterator<Note> i = map.iterator();
- while (i.hasNext())
- notes.add(i.next());
- } catch (IOException e) {
- throw new JGitInternalException(e.getMessage(), e);
- } finally {
- walk.release();
+ Ref newRef = asNew(oldRef);
+ List<Command> mv = new ArrayList<>(3);
+ mv.add(new Command(oldRef, null));
+ mv.add(new Command(null, newRef));
+ if (head != null && head.isSymbolic()
+ && head.getTarget().getName().equals(oldRef.getName())) {
+ mv.add(new Command(
+ head,
+ new SymbolicRef(head.getName(), newRef)));
+ }
+ batch.execute(rw, mv);
+ return RefTreeUpdate.translate(mv.get(1).getResult(), RENAMED);
}
-
- return notes;
}
- /**
- * @param notesRef
- * the ref to read notes from. Note, the default value of
- * {@link Constants#R_NOTES_COMMITS} will be used if nothing is
- * set
- * @return {@code this}
- *
- * @see Constants#R_NOTES_COMMITS
- */
- public ListNotesCommand setNotesRef(String notesRef) {
- checkCallable();
- this.notesRef = notesRef;
- return this;
- }
+ private Ref asNew(Ref src) {
+ String name = destination.getName();
+ if (src.isSymbolic()) {
+ return new SymbolicRef(name, src.getTarget());
+ }
+ ObjectId peeled = src.getPeeledObjectId();
+ if (peeled != null) {
+ return new ObjectIdRef.PeeledTag(
+ src.getStorage(),
+ name,
+ src.getObjectId(),
+ peeled);
+ }
+
+ return new ObjectIdRef.PeeledNonTag(
+ src.getStorage(),
+ name,
+ src.getObjectId());
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeUpdate.java
new file mode 100644
index 0000000..8829c11
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/RefTreeUpdate.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.reftree;
+
+import static org.eclipse.jgit.lib.Ref.Storage.LOOSE;
+import static org.eclipse.jgit.lib.Ref.Storage.NEW;
+
+import java.io.IOException;
+import java.util.Collections;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.SymbolicRef;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevTag;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
+
+/** Single reference update to {@link RefTreeDatabase}. */
+class RefTreeUpdate extends RefUpdate {
+ private final RefTreeDatabase refdb;
+ private RevWalk rw;
+ private RefTreeBatch batch;
+ private Ref oldRef;
+
+ RefTreeUpdate(RefTreeDatabase refdb, Ref ref) {
+ super(ref);
+ this.refdb = refdb;
+ setCheckConflicting(false); // Done automatically by doUpdate.
+ }
+
+ @Override
+ protected RefDatabase getRefDatabase() {
+ return refdb;
+ }
+
+ @Override
+ protected Repository getRepository() {
+ return refdb.getRepository();
+ }
+
+ @Override
+ protected boolean tryLock(boolean deref) throws IOException {
+ rw = new RevWalk(getRepository());
+ batch = new RefTreeBatch(refdb);
+ batch.init(rw);
+ oldRef = batch.exactRef(rw.getObjectReader(), getName());
+ if (oldRef != null && oldRef.getObjectId() != null) {
+ setOldObjectId(oldRef.getObjectId());
+ } else if (oldRef == null && getExpectedOldObjectId() != null) {
+ setOldObjectId(ObjectId.zeroId());
+ }
+ return true;
+ }
+
+ @Override
+ protected void unlock() {
+ batch = null;
+ if (rw != null) {
+ rw.close();
+ rw = null;
+ }
+ }
+
+ @Override
+ protected Result doUpdate(Result desiredResult) throws IOException {
+ return run(newRef(getName(), getNewObjectId()), desiredResult);
+ }
+
+ private Ref newRef(String name, ObjectId id)
+ throws MissingObjectException, IOException {
+ RevObject o = rw.parseAny(id);
+ if (o instanceof RevTag) {
+ RevObject p = rw.peel(o);
+ return new ObjectIdRef.PeeledTag(LOOSE, name, id, p.copy());
+ }
+ return new ObjectIdRef.PeeledNonTag(LOOSE, name, id);
+ }
+
+ @Override
+ protected Result doDelete(Result desiredResult) throws IOException {
+ return run(null, desiredResult);
+ }
+
+ @Override
+ protected Result doLink(String target) throws IOException {
+ Ref dst = new ObjectIdRef.Unpeeled(NEW, target, null);
+ SymbolicRef n = new SymbolicRef(getName(), dst);
+ Result desiredResult = getRef().getStorage() == NEW
+ ? Result.NEW
+ : Result.FORCED;
+ return run(n, desiredResult);
+ }
+
+ private Result run(@Nullable Ref newRef, Result desiredResult)
+ throws IOException {
+ Command c = new Command(oldRef, newRef);
+ batch.setRefLogIdent(getRefLogIdent());
+ batch.setRefLogMessage(getRefLogMessage(), isRefLogIncludingResult());
+ batch.execute(rw, Collections.singletonList(c));
+ return translate(c.getResult(), desiredResult);
+ }
+
+ static Result translate(ReceiveCommand.Result r, Result desiredResult) {
+ switch (r) {
+ case OK:
+ return desiredResult;
+
+ case LOCK_FAILURE:
+ return Result.LOCK_FAILURE;
+
+ case NOT_ATTEMPTED:
+ return Result.NOT_ATTEMPTED;
+
+ case REJECTED_MISSING_OBJECT:
+ return Result.IO_FAILURE;
+
+ case REJECTED_CURRENT_BRANCH:
+ return Result.REJECTED_CURRENT_BRANCH;
+
+ case REJECTED_OTHER_REASON:
+ case REJECTED_NOCREATE:
+ case REJECTED_NODELETE:
+ case REJECTED_NONFASTFORWARD:
+ default:
+ return Result.REJECTED;
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/Scanner.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/Scanner.java
new file mode 100644
index 0000000..d383abf
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftree/Scanner.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.reftree;
+
+import static org.eclipse.jgit.lib.RefDatabase.MAX_SYMBOLIC_REF_DEPTH;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.Constants.R_REFS;
+import static org.eclipse.jgit.lib.Constants.encode;
+import static org.eclipse.jgit.lib.FileMode.TYPE_GITLINK;
+import static org.eclipse.jgit.lib.FileMode.TYPE_SYMLINK;
+import static org.eclipse.jgit.lib.FileMode.TYPE_TREE;
+import static org.eclipse.jgit.lib.Ref.Storage.NEW;
+import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.SymbolicRef;
+import org.eclipse.jgit.revwalk.RevTree;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.AbstractTreeIterator;
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.util.Paths;
+import org.eclipse.jgit.util.RawParseUtils;
+import org.eclipse.jgit.util.RefList;
+
+/** A tree parser that extracts references from a {@link RefTree}. */
+class Scanner {
+ private static final int MAX_SYMLINK_BYTES = 10 << 10;
+ private static final byte[] BINARY_R_REFS = encode(R_REFS);
+ private static final byte[] REFS_DOT_DOT = encode("refs/.."); //$NON-NLS-1$
+
+ static class Result {
+ final ObjectId refTreeId;
+ final RefList<Ref> all;
+ final RefList<Ref> sym;
+
+ Result(ObjectId id, RefList<Ref> all, RefList<Ref> sym) {
+ this.refTreeId = id;
+ this.all = all;
+ this.sym = sym;
+ }
+ }
+
+ /**
+ * Scan a {@link RefTree} and parse entries into {@link Ref} instances.
+ *
+ * @param repo
+ * source repository containing the commit and tree objects that
+ * make up the RefTree.
+ * @param src
+ * bootstrap reference such as {@code refs/txn/committed} to read
+ * the reference tree tip from. The current ObjectId will be
+ * included in {@link Result#refTreeId}.
+ * @param prefix
+ * if non-empty a reference prefix to scan only a subdirectory.
+ * For example {@code prefix = "refs/heads/"} will limit the scan
+ * to only the {@code "heads"} directory of the RefTree, avoiding
+ * other directories like {@code "tags"}. Empty string reads all
+ * entries in the RefTree.
+ * @param recursive
+ * if true recurse into subdirectories of the reference tree;
+ * false to read only one level. Callers may use false during an
+ * implementation of {@code exactRef(String)} where only one
+ * reference is needed out of a specific subtree.
+ * @return sorted list of references after parsing.
+ * @throws IOException
+ * tree cannot be accessed from the repository.
+ */
+ static Result scanRefTree(Repository repo, @Nullable Ref src, String prefix,
+ boolean recursive) throws IOException {
+ RefList.Builder<Ref> all = new RefList.Builder<>();
+ RefList.Builder<Ref> sym = new RefList.Builder<>();
+
+ ObjectId srcId;
+ if (src != null && src.getObjectId() != null) {
+ try (ObjectReader reader = repo.newObjectReader()) {
+ srcId = src.getObjectId();
+ scan(reader, srcId, prefix, recursive, all, sym);
+ }
+ } else {
+ srcId = ObjectId.zeroId();
+ }
+
+ RefList<Ref> aList = all.toRefList();
+ for (int idx = 0; idx < sym.size();) {
+ Ref s = sym.get(idx);
+ Ref r = resolve(s, 0, aList);
+ if (r != null) {
+ sym.set(idx++, r);
+ } else {
+ // Remove broken symbolic reference, they don't exist.
+ sym.remove(idx);
+ int rm = aList.find(s.getName());
+ if (0 <= rm) {
+ aList = aList.remove(rm);
+ }
+ }
+ }
+ return new Result(srcId, aList, sym.toRefList());
+ }
+
+ private static void scan(ObjectReader reader, AnyObjectId srcId,
+ String prefix, boolean recursive,
+ RefList.Builder<Ref> all, RefList.Builder<Ref> sym)
+ throws IncorrectObjectTypeException, IOException {
+ CanonicalTreeParser p = createParserAtPath(reader, srcId, prefix);
+ if (p == null) {
+ return;
+ }
+
+ while (!p.eof()) {
+ int mode = p.getEntryRawMode();
+ if (mode == TYPE_TREE) {
+ if (recursive) {
+ p = p.createSubtreeIterator(reader);
+ } else {
+ p = p.next();
+ }
+ continue;
+ }
+
+ if (!curElementHasPeelSuffix(p)) {
+ Ref r = toRef(reader, mode, p);
+ if (r != null) {
+ all.add(r);
+ if (r.isSymbolic()) {
+ sym.add(r);
+ }
+ }
+ } else if (mode == TYPE_GITLINK) {
+ peel(all, p);
+ }
+ p = p.next();
+ }
+ }
+
+ private static CanonicalTreeParser createParserAtPath(ObjectReader reader,
+ AnyObjectId srcId, String prefix) throws IOException {
+ ObjectId root = toTree(reader, srcId);
+ if (prefix.isEmpty()) {
+ return new CanonicalTreeParser(BINARY_R_REFS, reader, root);
+ }
+
+ String dir = RefTree.refPath(Paths.stripTrailingSeparator(prefix));
+ TreeWalk tw = TreeWalk.forPath(reader, dir, root);
+ if (tw == null || !tw.isSubtree()) {
+ return null;
+ }
+
+ ObjectId id = tw.getObjectId(0);
+ return new CanonicalTreeParser(encode(prefix), reader, id);
+ }
+
+ private static Ref resolve(Ref ref, int depth, RefList<Ref> refs)
+ throws IOException {
+ if (!ref.isSymbolic()) {
+ return ref;
+ } else if (MAX_SYMBOLIC_REF_DEPTH <= depth) {
+ return null;
+ }
+
+ Ref r = refs.get(ref.getTarget().getName());
+ if (r == null) {
+ return ref;
+ }
+
+ Ref dst = resolve(r, depth + 1, refs);
+ if (dst == null) {
+ return null;
+ }
+ return new SymbolicRef(ref.getName(), dst);
+ }
+
+ @SuppressWarnings("resource")
+ private static RevTree toTree(ObjectReader reader, AnyObjectId id)
+ throws IOException {
+ return new RevWalk(reader).parseTree(id);
+ }
+
+ private static boolean curElementHasPeelSuffix(AbstractTreeIterator itr) {
+ int n = itr.getEntryPathLength();
+ byte[] c = itr.getEntryPathBuffer();
+ return n > 2 && c[n - 2] == ' ' && c[n - 1] == '^';
+ }
+
+ private static void peel(RefList.Builder<Ref> all, CanonicalTreeParser p) {
+ String name = refName(p, true);
+ for (int idx = all.size() - 1; 0 <= idx; idx--) {
+ Ref r = all.get(idx);
+ int cmp = r.getName().compareTo(name);
+ if (cmp == 0) {
+ all.set(idx, new ObjectIdRef.PeeledTag(PACKED, r.getName(),
+ r.getObjectId(), p.getEntryObjectId()));
+ break;
+ } else if (cmp < 0) {
+ // Stray peeled name without matching base name; skip entry.
+ break;
+ }
+ }
+ }
+
+ private static Ref toRef(ObjectReader reader, int mode,
+ CanonicalTreeParser p) throws IOException {
+ if (mode == TYPE_GITLINK) {
+ String name = refName(p, false);
+ ObjectId id = p.getEntryObjectId();
+ return new ObjectIdRef.PeeledNonTag(PACKED, name, id);
+
+ } else if (mode == TYPE_SYMLINK) {
+ ObjectId id = p.getEntryObjectId();
+ byte[] bin = reader.open(id, OBJ_BLOB)
+ .getCachedBytes(MAX_SYMLINK_BYTES);
+ String dst = RawParseUtils.decode(bin);
+ Ref trg = new ObjectIdRef.Unpeeled(NEW, dst, null);
+ String name = refName(p, false);
+ return new SymbolicRef(name, trg);
+ }
+ return null;
+ }
+
+ private static String refName(CanonicalTreeParser p, boolean peel) {
+ byte[] buf = p.getEntryPathBuffer();
+ int len = p.getEntryPathLength();
+ if (peel) {
+ len -= 2;
+ }
+ int ptr = 0;
+ if (RawParseUtils.match(buf, ptr, REFS_DOT_DOT) > 0) {
+ ptr = 7;
+ }
+ return RawParseUtils.decode(buf, ptr, len);
+ }
+
+ private Scanner() {
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
index eecbc22..670f9a9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
@@ -109,7 +109,8 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re
int pathStart = 8;
int lineEnd = RawParseUtils.nextLF(content, pathStart);
- if (content[lineEnd - 1] == '\n')
+ while (content[lineEnd - 1] == '\n' ||
+ (content[lineEnd - 1] == '\r' && SystemReader.getInstance().isWindows()))
lineEnd--;
if (lineEnd == pathStart)
throw new IOException(MessageFormat.format(
@@ -565,14 +566,16 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re
* based on other options. If insufficient information is available, an
* exception is thrown to the caller.
*
- * @return a repository matching this configuration.
+ * @return a repository matching this configuration. The caller is
+ * responsible to close the repository instance when it is no longer
+ * needed.
* @throws IllegalArgumentException
* insufficient parameters were set.
* @throws IOException
* the repository could not be accessed to configure the rest of
* the builder's parameters.
*/
- @SuppressWarnings("unchecked")
+ @SuppressWarnings({ "unchecked", "resource" })
public R build() throws IOException {
R repo = (R) new FileRepository(setup());
if (isMustExist() && !repo.getObjectDatabase().exists())
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchRefUpdate.java
index b369d0d..d7e9308 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchRefUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchRefUpdate.java
@@ -59,6 +59,7 @@ import java.util.List;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.PushCertificate;
import org.eclipse.jgit.transport.ReceiveCommand;
/**
@@ -85,6 +86,9 @@ public class BatchRefUpdate {
/** Should the result value be appended to {@link #refLogMessage}. */
private boolean refLogIncludeResult;
+ /** Push certificate associated with this update. */
+ private PushCertificate pushCert;
+
/**
* Initialize a new batch update.
*
@@ -195,6 +199,33 @@ public class BatchRefUpdate {
return refLogMessage == null;
}
+ /**
+ * Set a push certificate associated with this update.
+ * <p>
+ * This usually includes commands to update the refs in this batch, but is not
+ * required to.
+ *
+ * @param cert
+ * push certificate, may be null.
+ * @since 4.1
+ */
+ public void setPushCertificate(PushCertificate cert) {
+ pushCert = cert;
+ }
+
+ /**
+ * Set the push certificate associated with this update.
+ * <p>
+ * This usually includes commands to update the refs in this batch, but is not
+ * required to.
+ *
+ * @return push certificate, may be null.
+ * @since 4.1
+ */
+ protected PushCertificate getPushCertificate() {
+ return pushCert;
+ }
+
/** @return commands this update will process. */
public List<ReceiveCommand> getCommands() {
return Collections.unmodifiableList(commands);
@@ -377,6 +408,7 @@ public class BatchRefUpdate {
ru.setRefLogIdent(refLogIdent);
ru.setRefLogMessage(refLogMessage, refLogIncludeResult);
}
+ ru.setPushCertificate(pushCert);
switch (cmd.getType()) {
case DELETE:
if (!ObjectId.zeroId().equals(cmd.getOldId()))
@@ -394,4 +426,20 @@ public class BatchRefUpdate {
return ru;
}
}
+
+ @Override
+ public String toString() {
+ StringBuilder r = new StringBuilder();
+ r.append(getClass().getSimpleName()).append('[');
+ if (commands.isEmpty())
+ return r.append(']').toString();
+
+ r.append('\n');
+ for (ReceiveCommand cmd : commands) {
+ r.append(" "); //$NON-NLS-1$
+ r.append(cmd);
+ r.append(" (").append(cmd.getResult()).append(")\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ return r.append(']').toString();
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java
index 3c0e2c1..9ddff25 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java
@@ -125,7 +125,9 @@ public interface BitmapIndex {
* @param type
* the Git object type. See {@link Constants}.
* @return true if the value was not contained or able to be loaded.
+ * @deprecated use {@link #or} or {@link #addObject} instead.
*/
+ @Deprecated
boolean add(AnyObjectId objectId, int type);
/**
@@ -138,6 +140,18 @@ public interface BitmapIndex {
boolean contains(AnyObjectId objectId);
/**
+ * Adds the id to the bitmap.
+ *
+ * @param objectId
+ * the object ID
+ * @param type
+ * the Git object type. See {@link Constants}.
+ * @return the current builder.
+ * @since 4.2
+ */
+ BitmapBuilder addObject(AnyObjectId objectId, int type);
+
+ /**
* Remove the id from the bitmap.
*
* @param objectId
@@ -190,5 +204,14 @@ public interface BitmapIndex {
/** @return the number of elements in the bitmap. */
int cardinality();
+
+ /**
+ * Get the BitmapIndex for this BitmapBuilder.
+ *
+ * @return the BitmapIndex for this BitmapBuilder
+ *
+ * @since 4.2
+ */
+ BitmapIndex getBitmapIndex();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BlobBasedConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BlobBasedConfig.java
index a0197d0..7d52991 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BlobBasedConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BlobBasedConfig.java
@@ -79,7 +79,15 @@ public class BlobBasedConfig extends Config {
public BlobBasedConfig(Config base, final byte[] blob)
throws ConfigInvalidException {
super(base);
- fromText(RawParseUtils.decode(blob));
+ final String decoded;
+ if (blob.length >= 3 && blob[0] == (byte) 0xEF
+ && blob[1] == (byte) 0xBB && blob[2] == (byte) 0xBF) {
+ decoded = RawParseUtils.decode(RawParseUtils.UTF8_CHARSET,
+ blob, 3, blob.length);
+ } else {
+ decoded = RawParseUtils.decode(blob);
+ }
+ fromText(decoded);
}
/**
@@ -104,11 +112,8 @@ public class BlobBasedConfig extends Config {
private static byte[] read(Repository db, AnyObjectId blobId)
throws MissingObjectException, IncorrectObjectTypeException,
IOException {
- ObjectReader or = db.newObjectReader();
- try {
+ try (ObjectReader or = db.newObjectReader()) {
return read(or, blobId);
- } finally {
- or.release();
}
}
@@ -146,15 +151,12 @@ public class BlobBasedConfig extends Config {
private static byte[] read(Repository db, AnyObjectId treeish, String path)
throws MissingObjectException, IncorrectObjectTypeException,
IOException {
- ObjectReader or = db.newObjectReader();
- try {
+ try (ObjectReader or = db.newObjectReader()) {
TreeWalk tree = TreeWalk.forPath(or, path, asTree(or, treeish));
if (tree == null)
throw new FileNotFoundException(MessageFormat.format(JGitText
.get().entryNotFoundByPath, path));
return read(or, tree.getObjectId(0));
- } finally {
- or.release();
}
}
@@ -168,6 +170,8 @@ public class BlobBasedConfig extends Config {
&& ((RevCommit) treeish).getTree() != null)
return ((RevCommit) treeish).getTree();
- return new RevWalk(or).parseTree(treeish).getId();
+ try (RevWalk rw = new RevWalk(or)) {
+ return rw.parseTree(treeish).getId();
+ }
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
index 22337e8..e48386d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
@@ -1200,8 +1200,6 @@ public class Config {
for (;;) {
int c = in.read();
if (c < 0) {
- if (value.length() == 0)
- throw new ConfigInvalidException(JGitText.get().unexpectedEndOfConfigFile);
break;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
index 8a2080b..a89bcee 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -299,4 +299,16 @@ public class ConfigConstants {
* @since 3.3
*/
public static final String CONFIG_KEY_PRUNE = "prune";
+
+ /**
+ * The "streamBuffer" key
+ * @since 4.0
+ */
+ public static final String CONFIG_KEY_STREAM_BUFFER = "streamBuffer";
+
+ /**
+ * The "streamRatio" key
+ * @since 4.0
+ */
+ public static final String CONFIG_KEY_STREAM_RATIO = "streamRatio";
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
index ed0ed04..d30edaf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
@@ -273,6 +273,13 @@ public final class Constants {
public static final String INFO_EXCLUDE = "info/exclude";
/**
+ * Attributes-override-file
+ *
+ * @since 4.2
+ */
+ public static final String INFO_ATTRIBUTES = "info/attributes";
+
+ /**
* The system property that contains the system user name
*
* @since 3.6
@@ -363,6 +370,27 @@ public final class Constants {
*/
public static final String DOT_GIT_ATTRIBUTES = ".gitattributes";
+ /**
+ * Key for filters in .gitattributes
+ *
+ * @since 4.2
+ */
+ public static final String ATTR_FILTER = "filter";
+
+ /**
+ * clean command name, used to call filter driver
+ *
+ * @since 4.2
+ */
+ public static final String ATTR_FILTER_TYPE_CLEAN = "clean";
+
+ /**
+ * smudge command name, used to call filter driver
+ *
+ * @since 4.2
+ */
+ public static final String ATTR_FILTER_TYPE_SMUDGE = "smudge";
+
/** Name of the ignore file */
public static final String DOT_GIT_IGNORE = ".gitignore";
@@ -618,6 +646,14 @@ public final class Constants {
*/
public static final String ORIG_HEAD = "ORIG_HEAD";
+ /**
+ * Name of the file in which git commands and hooks store and read the
+ * message prepared for the upcoming commit.
+ *
+ * @since 4.0
+ */
+ public static final String COMMIT_EDITMSG = "COMMIT_EDITMSG";
+
/** objectid for the empty blob */
public static final ObjectId EMPTY_BLOB_ID = ObjectId
.fromString("e69de29bb2d1d6434b8b29ae775ad8c2e48c5391");
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileTreeEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileTreeEntry.java
deleted file mode 100644
index 6811417..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileTreeEntry.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg at dewire.com>
- * Copyright (C) 2006-2007, Shawn O. Pearce <spearce at spearce.org>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.IOException;
-
-/**
- * A representation of a file (blob) object in a {@link Tree}.
- *
- * @deprecated To look up information about a single path, use
- * {@link org.eclipse.jgit.treewalk.TreeWalk#forPath(Repository, String, org.eclipse.jgit.revwalk.RevTree)}.
- * To lookup information about multiple paths at once, use a
- * {@link org.eclipse.jgit.treewalk.TreeWalk} and obtain the current entry's
- * information from its getter methods.
- */
- at Deprecated
-public class FileTreeEntry extends TreeEntry {
- private FileMode mode;
-
- /**
- * Constructor for a File (blob) object.
- *
- * @param parent
- * The {@link Tree} holding this object (or null)
- * @param id
- * the SHA-1 of the blob (or null for a yet unhashed file)
- * @param nameUTF8
- * raw object name in the parent tree
- * @param execute
- * true if the executable flag is set
- */
- public FileTreeEntry(final Tree parent, final ObjectId id,
- final byte[] nameUTF8, final boolean execute) {
- super(parent, id, nameUTF8);
- setExecutable(execute);
- }
-
- public FileMode getMode() {
- return mode;
- }
-
- /**
- * @return true if this file is executable
- */
- public boolean isExecutable() {
- return getMode().equals(FileMode.EXECUTABLE_FILE);
- }
-
- /**
- * @param execute set/reset the executable flag
- */
- public void setExecutable(final boolean execute) {
- mode = execute ? FileMode.EXECUTABLE_FILE : FileMode.REGULAR_FILE;
- }
-
- /**
- * @return an {@link ObjectLoader} that will return the data
- * @throws IOException
- */
- public ObjectLoader openReader() throws IOException {
- return getRepository().open(getId(), Constants.OBJ_BLOB);
- }
-
- public String toString() {
- final StringBuilder r = new StringBuilder();
- r.append(ObjectId.toString(getId()));
- r.append(' ');
- r.append(isExecutable() ? 'X' : 'F');
- r.append(' ');
- r.append(getFullName());
- return r.toString();
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GitlinkTreeEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GitlinkTreeEntry.java
deleted file mode 100644
index 936fd82..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GitlinkTreeEntry.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2009, Jonas Fonseca <fonseca at diku.dk>
- * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg at dewire.com>
- * Copyright (C) 2007, Shawn O. Pearce <spearce at spearce.org>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-/**
- * A tree entry representing a gitlink entry used for submodules.
- *
- * Note. Java cannot really handle these as file system objects.
- *
- * @deprecated To look up information about a single path, use
- * {@link org.eclipse.jgit.treewalk.TreeWalk#forPath(Repository, String, org.eclipse.jgit.revwalk.RevTree)}.
- * To lookup information about multiple paths at once, use a
- * {@link org.eclipse.jgit.treewalk.TreeWalk} and obtain the current entry's
- * information from its getter methods.
- */
- at Deprecated
-public class GitlinkTreeEntry extends TreeEntry {
-
- /**
- * Construct a {@link GitlinkTreeEntry} with the specified name and SHA-1 in
- * the specified parent
- *
- * @param parent
- * @param id
- * @param nameUTF8
- */
- public GitlinkTreeEntry(final Tree parent, final ObjectId id,
- final byte[] nameUTF8) {
- super(parent, id, nameUTF8);
- }
-
- public FileMode getMode() {
- return FileMode.GITLINK;
- }
-
- @Override
- public String toString() {
- final StringBuilder r = new StringBuilder();
- r.append(ObjectId.toString(getId()));
- r.append(" G "); //$NON-NLS-1$
- r.append(getFullName());
- return r.toString();
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
index 1b049f6..9e474f8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
@@ -48,6 +48,7 @@
package org.eclipse.jgit.lib;
import java.io.IOException;
+import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -63,6 +64,7 @@ import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.StopWalkException;
+import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.submodule.SubmoduleWalk;
@@ -72,6 +74,7 @@ import org.eclipse.jgit.treewalk.EmptyTreeIterator;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.WorkingTreeIterator;
+import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
import org.eclipse.jgit.treewalk.filter.IndexDiffFilter;
import org.eclipse.jgit.treewalk.filter.SkipWorkTreeFilter;
@@ -400,116 +403,122 @@ public class IndexDiff {
throws IOException {
dirCache = repository.readDirCache();
- TreeWalk treeWalk = new TreeWalk(repository);
- treeWalk.setRecursive(true);
- // add the trees (tree, dirchache, workdir)
- if (tree != null)
- treeWalk.addTree(tree);
- else
- treeWalk.addTree(new EmptyTreeIterator());
- treeWalk.addTree(new DirCacheIterator(dirCache));
- treeWalk.addTree(initialWorkingTreeIterator);
- Collection<TreeFilter> filters = new ArrayList<TreeFilter>(4);
-
- if (monitor != null) {
- // Get the maximum size of the work tree and index
- // and add some (quite arbitrary)
- if (estIndexSize == 0)
- estIndexSize = dirCache.getEntryCount();
- int total = Math.max(estIndexSize * 10 / 9,
- estWorkTreeSize * 10 / 9);
- monitor.beginTask(title, total);
- filters.add(new ProgressReportingFilter(monitor, total));
- }
+ try (TreeWalk treeWalk = new TreeWalk(repository)) {
+ treeWalk.setOperationType(OperationType.CHECKIN_OP);
+ treeWalk.setRecursive(true);
+ // add the trees (tree, dirchache, workdir)
+ if (tree != null)
+ treeWalk.addTree(tree);
+ else
+ treeWalk.addTree(new EmptyTreeIterator());
+ treeWalk.addTree(new DirCacheIterator(dirCache));
+ treeWalk.addTree(initialWorkingTreeIterator);
+ initialWorkingTreeIterator.setDirCacheIterator(treeWalk, 1);
+ Collection<TreeFilter> filters = new ArrayList<TreeFilter>(4);
+
+ if (monitor != null) {
+ // Get the maximum size of the work tree and index
+ // and add some (quite arbitrary)
+ if (estIndexSize == 0)
+ estIndexSize = dirCache.getEntryCount();
+ int total = Math.max(estIndexSize * 10 / 9,
+ estWorkTreeSize * 10 / 9);
+ monitor.beginTask(title, total);
+ filters.add(new ProgressReportingFilter(monitor, total));
+ }
- if (filter != null)
- filters.add(filter);
- filters.add(new SkipWorkTreeFilter(INDEX));
- indexDiffFilter = new IndexDiffFilter(INDEX, WORKDIR);
- filters.add(indexDiffFilter);
- treeWalk.setFilter(AndTreeFilter.create(filters));
- fileModes.clear();
- while (treeWalk.next()) {
- AbstractTreeIterator treeIterator = treeWalk.getTree(TREE,
- AbstractTreeIterator.class);
- DirCacheIterator dirCacheIterator = treeWalk.getTree(INDEX,
- DirCacheIterator.class);
- WorkingTreeIterator workingTreeIterator = treeWalk.getTree(WORKDIR,
- WorkingTreeIterator.class);
-
- if (dirCacheIterator != null) {
- final DirCacheEntry dirCacheEntry = dirCacheIterator
- .getDirCacheEntry();
- if (dirCacheEntry != null) {
- int stage = dirCacheEntry.getStage();
- if (stage > 0) {
- String path = treeWalk.getPathString();
- addConflict(path, stage);
- continue;
+ if (filter != null)
+ filters.add(filter);
+ filters.add(new SkipWorkTreeFilter(INDEX));
+ indexDiffFilter = new IndexDiffFilter(INDEX, WORKDIR);
+ filters.add(indexDiffFilter);
+ treeWalk.setFilter(AndTreeFilter.create(filters));
+ fileModes.clear();
+ while (treeWalk.next()) {
+ AbstractTreeIterator treeIterator = treeWalk.getTree(TREE,
+ AbstractTreeIterator.class);
+ DirCacheIterator dirCacheIterator = treeWalk.getTree(INDEX,
+ DirCacheIterator.class);
+ WorkingTreeIterator workingTreeIterator = treeWalk
+ .getTree(WORKDIR, WorkingTreeIterator.class);
+
+ if (dirCacheIterator != null) {
+ final DirCacheEntry dirCacheEntry = dirCacheIterator
+ .getDirCacheEntry();
+ if (dirCacheEntry != null) {
+ int stage = dirCacheEntry.getStage();
+ if (stage > 0) {
+ String path = treeWalk.getPathString();
+ addConflict(path, stage);
+ continue;
+ }
}
}
- }
- if (treeIterator != null) {
- if (dirCacheIterator != null) {
- if (!treeIterator.idEqual(dirCacheIterator)
- || treeIterator.getEntryRawMode()
- != dirCacheIterator.getEntryRawMode()) {
- // in repo, in index, content diff => changed
+ if (treeIterator != null) {
+ if (dirCacheIterator != null) {
+ if (!treeIterator.idEqual(dirCacheIterator)
+ || treeIterator
+ .getEntryRawMode() != dirCacheIterator
+ .getEntryRawMode()) {
+ // in repo, in index, content diff => changed
+ if (!isEntryGitLink(treeIterator)
+ || !isEntryGitLink(dirCacheIterator)
+ || ignoreSubmoduleMode != IgnoreSubmoduleMode.ALL)
+ changed.add(treeWalk.getPathString());
+ }
+ } else {
+ // in repo, not in index => removed
if (!isEntryGitLink(treeIterator)
- || !isEntryGitLink(dirCacheIterator)
|| ignoreSubmoduleMode != IgnoreSubmoduleMode.ALL)
- changed.add(treeWalk.getPathString());
+ removed.add(treeWalk.getPathString());
+ if (workingTreeIterator != null)
+ untracked.add(treeWalk.getPathString());
}
} else {
- // in repo, not in index => removed
- if (!isEntryGitLink(treeIterator)
- || ignoreSubmoduleMode != IgnoreSubmoduleMode.ALL)
- removed.add(treeWalk.getPathString());
- if (workingTreeIterator != null)
- untracked.add(treeWalk.getPathString());
- }
- } else {
- if (dirCacheIterator != null) {
- // not in repo, in index => added
- if (!isEntryGitLink(dirCacheIterator)
- || ignoreSubmoduleMode != IgnoreSubmoduleMode.ALL)
- added.add(treeWalk.getPathString());
- } else {
- // not in repo, not in index => untracked
- if (workingTreeIterator != null
- && !workingTreeIterator.isEntryIgnored()) {
- untracked.add(treeWalk.getPathString());
+ if (dirCacheIterator != null) {
+ // not in repo, in index => added
+ if (!isEntryGitLink(dirCacheIterator)
+ || ignoreSubmoduleMode != IgnoreSubmoduleMode.ALL)
+ added.add(treeWalk.getPathString());
+ } else {
+ // not in repo, not in index => untracked
+ if (workingTreeIterator != null
+ && !workingTreeIterator.isEntryIgnored()) {
+ untracked.add(treeWalk.getPathString());
+ }
}
}
- }
- if (dirCacheIterator != null) {
- if (workingTreeIterator == null) {
- // in index, not in workdir => missing
- if (!isEntryGitLink(dirCacheIterator)
- || ignoreSubmoduleMode != IgnoreSubmoduleMode.ALL)
- missing.add(treeWalk.getPathString());
- } else {
- if (workingTreeIterator.isModified(
- dirCacheIterator.getDirCacheEntry(), true,
- treeWalk.getObjectReader())) {
- // in index, in workdir, content differs => modified
- if (!isEntryGitLink(dirCacheIterator) || !isEntryGitLink(workingTreeIterator)
- || (ignoreSubmoduleMode != IgnoreSubmoduleMode.ALL && ignoreSubmoduleMode != IgnoreSubmoduleMode.DIRTY))
- modified.add(treeWalk.getPathString());
+ if (dirCacheIterator != null) {
+ if (workingTreeIterator == null) {
+ // in index, not in workdir => missing
+ if (!isEntryGitLink(dirCacheIterator)
+ || ignoreSubmoduleMode != IgnoreSubmoduleMode.ALL)
+ missing.add(treeWalk.getPathString());
+ } else {
+ if (workingTreeIterator.isModified(
+ dirCacheIterator.getDirCacheEntry(), true,
+ treeWalk.getObjectReader())) {
+ // in index, in workdir, content differs => modified
+ if (!isEntryGitLink(dirCacheIterator)
+ || !isEntryGitLink(workingTreeIterator)
+ || (ignoreSubmoduleMode != IgnoreSubmoduleMode.ALL
+ && ignoreSubmoduleMode != IgnoreSubmoduleMode.DIRTY))
+ modified.add(treeWalk.getPathString());
+ }
}
}
- }
- for (int i = 0; i < treeWalk.getTreeCount(); i++) {
- Set<String> values = fileModes.get(treeWalk.getFileMode(i));
- String path = treeWalk.getPathString();
- if (path != null) {
- if (values == null)
- values = new HashSet<String>();
- values.add(path);
- fileModes.put(treeWalk.getFileMode(i), values);
+ for (int i = 0; i < treeWalk.getTreeCount(); i++) {
+ Set<String> values = fileModes.get(treeWalk.getFileMode(i));
+ String path = treeWalk.getPathString();
+ if (path != null) {
+ if (values == null)
+ values = new HashSet<String>();
+ values.add(path);
+ fileModes.put(treeWalk.getFileMode(i), values);
+ }
}
}
}
@@ -525,9 +534,9 @@ public class IndexDiff {
.equals(localIgnoreSubmoduleMode))
continue;
} catch (ConfigInvalidException e) {
- IOException e1 = new IOException(
- "Found invalid ignore param for submodule "
- + smw.getPath());
+ IOException e1 = new IOException(MessageFormat.format(
+ JGitText.get().invalidIgnoreParamSubmodule,
+ smw.getPath()));
e1.initCause(e);
throw e1;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java
index 8435c9a..0b5efd7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectChecker.java
@@ -44,21 +44,58 @@
package org.eclipse.jgit.lib;
-import static org.eclipse.jgit.util.RawParseUtils.match;
+import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
+import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
+import static org.eclipse.jgit.lib.Constants.OBJ_BAD;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
+import static org.eclipse.jgit.lib.Constants.OBJ_TAG;
+import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.BAD_DATE;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.BAD_EMAIL;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.BAD_OBJECT_SHA1;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.BAD_PARENT_SHA1;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.BAD_TIMEZONE;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.BAD_TREE_SHA1;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.BAD_UTF8;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.DUPLICATE_ENTRIES;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.EMPTY_NAME;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.FULL_PATHNAME;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.HAS_DOT;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.HAS_DOTDOT;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.HAS_DOTGIT;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.MISSING_AUTHOR;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.MISSING_COMMITTER;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.MISSING_EMAIL;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.MISSING_OBJECT;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.MISSING_SPACE_BEFORE_DATE;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.MISSING_TAG_ENTRY;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.MISSING_TREE;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.MISSING_TYPE_ENTRY;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.NULL_SHA1;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.TREE_NOT_SORTED;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.UNKNOWN_TYPE;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.WIN32_BAD_NAME;
+import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.ZERO_PADDED_FILEMODE;
+import static org.eclipse.jgit.util.Paths.compare;
+import static org.eclipse.jgit.util.Paths.compareSameName;
import static org.eclipse.jgit.util.RawParseUtils.nextLF;
import static org.eclipse.jgit.util.RawParseUtils.parseBase10;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import java.text.MessageFormat;
+import java.text.Normalizer;
+import java.util.EnumSet;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.util.MutableInteger;
import org.eclipse.jgit.util.RawParseUtils;
+import org.eclipse.jgit.util.StringUtils;
/**
* Verifies that an object is formatted correctly.
@@ -99,28 +136,150 @@ public class ObjectChecker {
/** Header "tagger " */
public static final byte[] tagger = Constants.encodeASCII("tagger "); //$NON-NLS-1$
- private final MutableObjectId tempId = new MutableObjectId();
+ /**
+ * Potential issues identified by the checker.
+ *
+ * @since 4.2
+ */
+ public enum ErrorType {
+ // @formatter:off
+ // These names match git-core so that fsck section keys also match.
+ /***/ NULL_SHA1,
+ /***/ DUPLICATE_ENTRIES,
+ /***/ TREE_NOT_SORTED,
+ /***/ ZERO_PADDED_FILEMODE,
+ /***/ EMPTY_NAME,
+ /***/ FULL_PATHNAME,
+ /***/ HAS_DOT,
+ /***/ HAS_DOTDOT,
+ /***/ HAS_DOTGIT,
+ /***/ BAD_OBJECT_SHA1,
+ /***/ BAD_PARENT_SHA1,
+ /***/ BAD_TREE_SHA1,
+ /***/ MISSING_AUTHOR,
+ /***/ MISSING_COMMITTER,
+ /***/ MISSING_OBJECT,
+ /***/ MISSING_TREE,
+ /***/ MISSING_TYPE_ENTRY,
+ /***/ MISSING_TAG_ENTRY,
+ /***/ BAD_DATE,
+ /***/ BAD_EMAIL,
+ /***/ BAD_TIMEZONE,
+ /***/ MISSING_EMAIL,
+ /***/ MISSING_SPACE_BEFORE_DATE,
+ /***/ UNKNOWN_TYPE,
+
+ // These are unique to JGit.
+ /***/ WIN32_BAD_NAME,
+ /***/ BAD_UTF8;
+ // @formatter:on
+
+ /** @return camelCaseVersion of the name. */
+ public String getMessageId() {
+ String n = name();
+ StringBuilder r = new StringBuilder(n.length());
+ for (int i = 0; i < n.length(); i++) {
+ char c = n.charAt(i);
+ if (c != '_') {
+ r.append(StringUtils.toLowerCase(c));
+ } else {
+ r.append(n.charAt(++i));
+ }
+ }
+ return r.toString();
+ }
+ }
- private final MutableInteger ptrout = new MutableInteger();
+ private final MutableObjectId tempId = new MutableObjectId();
+ private final MutableInteger bufPtr = new MutableInteger();
- private boolean allowZeroMode;
+ private EnumSet<ErrorType> errors = EnumSet.allOf(ErrorType.class);
+ private ObjectIdSet skipList;
+ private boolean allowInvalidPersonIdent;
private boolean windows;
private boolean macosx;
/**
+ * Enable accepting specific malformed (but not horribly broken) objects.
+ *
+ * @param objects
+ * collection of object names known to be broken in a non-fatal
+ * way that should be ignored by the checker.
+ * @return {@code this}
+ * @since 4.2
+ */
+ public ObjectChecker setSkipList(@Nullable ObjectIdSet objects) {
+ skipList = objects;
+ return this;
+ }
+
+ /**
+ * Configure error types to be ignored across all objects.
+ *
+ * @param ids
+ * error types to ignore. The caller's set is copied.
+ * @return {@code this}
+ * @since 4.2
+ */
+ public ObjectChecker setIgnore(@Nullable Set<ErrorType> ids) {
+ errors = EnumSet.allOf(ErrorType.class);
+ if (ids != null) {
+ errors.removeAll(ids);
+ }
+ return this;
+ }
+
+ /**
+ * Add message type to be ignored across all objects.
+ *
+ * @param id
+ * error type to ignore.
+ * @param ignore
+ * true to ignore this error; false to treat the error as an
+ * error and throw.
+ * @return {@code this}
+ * @since 4.2
+ */
+ public ObjectChecker setIgnore(ErrorType id, boolean ignore) {
+ if (ignore) {
+ errors.remove(id);
+ } else {
+ errors.add(id);
+ }
+ return this;
+ }
+
+ /**
* Enable accepting leading zero mode in tree entries.
* <p>
* Some broken Git libraries generated leading zeros in the mode part of
* tree entries. This is technically incorrect but gracefully allowed by
* git-core. JGit rejects such trees by default, but may need to accept
* them on broken histories.
+ * <p>
+ * Same as {@code setIgnore(ZERO_PADDED_FILEMODE, allow)}.
*
* @param allow allow leading zero mode.
* @return {@code this}.
* @since 3.4
*/
public ObjectChecker setAllowLeadingZeroFileMode(boolean allow) {
- allowZeroMode = allow;
+ return setIgnore(ZERO_PADDED_FILEMODE, allow);
+ }
+
+ /**
+ * Enable accepting invalid author, committer and tagger identities.
+ * <p>
+ * Some broken Git versions/libraries allowed users to create commits and
+ * tags with invalid formatting between the name, email and timestamp.
+ *
+ * @param allow
+ * if true accept invalid person identity strings.
+ * @return {@code this}.
+ * @since 4.0
+ */
+ public ObjectChecker setAllowInvalidPersonIdent(boolean allow) {
+ allowInvalidPersonIdent = allow;
return this;
}
@@ -166,59 +325,117 @@ public class ObjectChecker {
* @throws CorruptObjectException
* if an error is identified.
*/
- public void check(final int objType, final byte[] raw)
+ public void check(int objType, byte[] raw)
+ throws CorruptObjectException {
+ check(idFor(objType, raw), objType, raw);
+ }
+
+ /**
+ * Check an object for parsing errors.
+ *
+ * @param id
+ * identify of the object being checked.
+ * @param objType
+ * type of the object. Must be a valid object type code in
+ * {@link Constants}.
+ * @param raw
+ * the raw data which comprises the object. This should be in the
+ * canonical format (that is the format used to generate the
+ * ObjectId of the object). The array is never modified.
+ * @throws CorruptObjectException
+ * if an error is identified.
+ * @since 4.2
+ */
+ public void check(@Nullable AnyObjectId id, int objType, byte[] raw)
throws CorruptObjectException {
switch (objType) {
- case Constants.OBJ_COMMIT:
- checkCommit(raw);
+ case OBJ_COMMIT:
+ checkCommit(id, raw);
break;
- case Constants.OBJ_TAG:
- checkTag(raw);
+ case OBJ_TAG:
+ checkTag(id, raw);
break;
- case Constants.OBJ_TREE:
- checkTree(raw);
+ case OBJ_TREE:
+ checkTree(id, raw);
break;
- case Constants.OBJ_BLOB:
+ case OBJ_BLOB:
checkBlob(raw);
break;
default:
- throw new CorruptObjectException(MessageFormat.format(
+ report(UNKNOWN_TYPE, id, MessageFormat.format(
JGitText.get().corruptObjectInvalidType2,
Integer.valueOf(objType)));
}
}
- private int id(final byte[] raw, final int ptr) {
+ private boolean checkId(byte[] raw) {
+ int p = bufPtr.value;
try {
- tempId.fromString(raw, ptr);
- return ptr + Constants.OBJECT_ID_STRING_LENGTH;
+ tempId.fromString(raw, p);
} catch (IllegalArgumentException e) {
- return -1;
+ bufPtr.value = nextLF(raw, p);
+ return false;
}
+
+ p += OBJECT_ID_STRING_LENGTH;
+ if (raw[p] == '\n') {
+ bufPtr.value = p + 1;
+ return true;
+ }
+ bufPtr.value = nextLF(raw, p);
+ return false;
}
- private int personIdent(final byte[] raw, int ptr) {
- final int emailB = nextLF(raw, ptr, '<');
- if (emailB == ptr || raw[emailB - 1] != '<')
- return -1;
+ private void checkPersonIdent(byte[] raw, @Nullable AnyObjectId id)
+ throws CorruptObjectException {
+ if (allowInvalidPersonIdent) {
+ bufPtr.value = nextLF(raw, bufPtr.value);
+ return;
+ }
+
+ final int emailB = nextLF(raw, bufPtr.value, '<');
+ if (emailB == bufPtr.value || raw[emailB - 1] != '<') {
+ report(MISSING_EMAIL, id, JGitText.get().corruptObjectMissingEmail);
+ bufPtr.value = nextLF(raw, bufPtr.value);
+ return;
+ }
final int emailE = nextLF(raw, emailB, '>');
- if (emailE == emailB || raw[emailE - 1] != '>')
- return -1;
- if (emailE == raw.length || raw[emailE] != ' ')
- return -1;
+ if (emailE == emailB || raw[emailE - 1] != '>') {
+ report(BAD_EMAIL, id, JGitText.get().corruptObjectBadEmail);
+ bufPtr.value = nextLF(raw, bufPtr.value);
+ return;
+ }
+ if (emailE == raw.length || raw[emailE] != ' ') {
+ report(MISSING_SPACE_BEFORE_DATE, id,
+ JGitText.get().corruptObjectBadDate);
+ bufPtr.value = nextLF(raw, bufPtr.value);
+ return;
+ }
- parseBase10(raw, emailE + 1, ptrout); // when
- ptr = ptrout.value;
- if (emailE + 1 == ptr)
- return -1;
- if (ptr == raw.length || raw[ptr] != ' ')
- return -1;
+ parseBase10(raw, emailE + 1, bufPtr); // when
+ if (emailE + 1 == bufPtr.value || bufPtr.value == raw.length
+ || raw[bufPtr.value] != ' ') {
+ report(BAD_DATE, id, JGitText.get().corruptObjectBadDate);
+ bufPtr.value = nextLF(raw, bufPtr.value);
+ return;
+ }
- parseBase10(raw, ptr + 1, ptrout); // tz offset
- if (ptr + 1 == ptrout.value)
- return -1;
- return ptrout.value;
+ int p = bufPtr.value + 1;
+ parseBase10(raw, p, bufPtr); // tz offset
+ if (p == bufPtr.value) {
+ report(BAD_TIMEZONE, id, JGitText.get().corruptObjectBadTimezone);
+ bufPtr.value = nextLF(raw, bufPtr.value);
+ return;
+ }
+
+ p = bufPtr.value;
+ if (raw[p] == '\n') {
+ bufPtr.value = p + 1;
+ } else {
+ report(BAD_TIMEZONE, id, JGitText.get().corruptObjectBadTimezone);
+ bufPtr.value = nextLF(raw, p);
+ }
}
/**
@@ -229,29 +446,50 @@ public class ObjectChecker {
* @throws CorruptObjectException
* if any error was detected.
*/
- public void checkCommit(final byte[] raw) throws CorruptObjectException {
- int ptr = 0;
+ public void checkCommit(byte[] raw) throws CorruptObjectException {
+ checkCommit(idFor(OBJ_COMMIT, raw), raw);
+ }
- if ((ptr = match(raw, ptr, tree)) < 0)
- throw new CorruptObjectException("no tree header");
- if ((ptr = id(raw, ptr)) < 0 || raw[ptr++] != '\n')
- throw new CorruptObjectException("invalid tree");
+ /**
+ * Check a commit for errors.
+ *
+ * @param id
+ * identity of the object being checked.
+ * @param raw
+ * the commit data. The array is never modified.
+ * @throws CorruptObjectException
+ * if any error was detected.
+ * @since 4.2
+ */
+ public void checkCommit(@Nullable AnyObjectId id, byte[] raw)
+ throws CorruptObjectException {
+ bufPtr.value = 0;
+
+ if (!match(raw, tree)) {
+ report(MISSING_TREE, id, JGitText.get().corruptObjectNotreeHeader);
+ } else if (!checkId(raw)) {
+ report(BAD_TREE_SHA1, id, JGitText.get().corruptObjectInvalidTree);
+ }
- while (match(raw, ptr, parent) >= 0) {
- ptr += parent.length;
- if ((ptr = id(raw, ptr)) < 0 || raw[ptr++] != '\n')
- throw new CorruptObjectException("invalid parent");
+ while (match(raw, parent)) {
+ if (!checkId(raw)) {
+ report(BAD_PARENT_SHA1, id,
+ JGitText.get().corruptObjectInvalidParent);
+ }
}
- if ((ptr = match(raw, ptr, author)) < 0)
- throw new CorruptObjectException("no author");
- if ((ptr = personIdent(raw, ptr)) < 0 || raw[ptr++] != '\n')
- throw new CorruptObjectException("invalid author");
+ if (match(raw, author)) {
+ checkPersonIdent(raw, id);
+ } else {
+ report(MISSING_AUTHOR, id, JGitText.get().corruptObjectNoAuthor);
+ }
- if ((ptr = match(raw, ptr, committer)) < 0)
- throw new CorruptObjectException("no committer");
- if ((ptr = personIdent(raw, ptr)) < 0 || raw[ptr++] != '\n')
- throw new CorruptObjectException("invalid committer");
+ if (match(raw, committer)) {
+ checkPersonIdent(raw, id);
+ } else {
+ report(MISSING_COMMITTER, id,
+ JGitText.get().corruptObjectNoCommitter);
+ }
}
/**
@@ -262,45 +500,47 @@ public class ObjectChecker {
* @throws CorruptObjectException
* if any error was detected.
*/
- public void checkTag(final byte[] raw) throws CorruptObjectException {
- int ptr = 0;
-
- if ((ptr = match(raw, ptr, object)) < 0)
- throw new CorruptObjectException("no object header");
- if ((ptr = id(raw, ptr)) < 0 || raw[ptr++] != '\n')
- throw new CorruptObjectException("invalid object");
-
- if ((ptr = match(raw, ptr, type)) < 0)
- throw new CorruptObjectException("no type header");
- ptr = nextLF(raw, ptr);
-
- if ((ptr = match(raw, ptr, tag)) < 0)
- throw new CorruptObjectException("no tag header");
- ptr = nextLF(raw, ptr);
+ public void checkTag(byte[] raw) throws CorruptObjectException {
+ checkTag(idFor(OBJ_TAG, raw), raw);
+ }
- if ((ptr = match(raw, ptr, tagger)) > 0) {
- if ((ptr = personIdent(raw, ptr)) < 0 || raw[ptr++] != '\n')
- throw new CorruptObjectException("invalid tagger");
+ /**
+ * Check an annotated tag for errors.
+ *
+ * @param id
+ * identity of the object being checked.
+ * @param raw
+ * the tag data. The array is never modified.
+ * @throws CorruptObjectException
+ * if any error was detected.
+ * @since 4.2
+ */
+ public void checkTag(@Nullable AnyObjectId id, byte[] raw)
+ throws CorruptObjectException {
+ bufPtr.value = 0;
+ if (!match(raw, object)) {
+ report(MISSING_OBJECT, id,
+ JGitText.get().corruptObjectNoObjectHeader);
+ } else if (!checkId(raw)) {
+ report(BAD_OBJECT_SHA1, id,
+ JGitText.get().corruptObjectInvalidObject);
}
- }
- private static int lastPathChar(final int mode) {
- return FileMode.TREE.equals(mode) ? '/' : '\0';
- }
+ if (!match(raw, type)) {
+ report(MISSING_TYPE_ENTRY, id,
+ JGitText.get().corruptObjectNoTypeHeader);
+ }
+ bufPtr.value = nextLF(raw, bufPtr.value);
- private static int pathCompare(final byte[] raw, int aPos, final int aEnd,
- final int aMode, int bPos, final int bEnd, final int bMode) {
- while (aPos < aEnd && bPos < bEnd) {
- final int cmp = (raw[aPos++] & 0xff) - (raw[bPos++] & 0xff);
- if (cmp != 0)
- return cmp;
+ if (!match(raw, tag)) {
+ report(MISSING_TAG_ENTRY, id,
+ JGitText.get().corruptObjectNoTagHeader);
}
+ bufPtr.value = nextLF(raw, bufPtr.value);
- if (aPos < aEnd)
- return (raw[aPos] & 0xff) - lastPathChar(bMode);
- if (bPos < bEnd)
- return lastPathChar(aMode) - (raw[bPos] & 0xff);
- return 0;
+ if (match(raw, tagger)) {
+ checkPersonIdent(raw, id);
+ }
}
private static boolean duplicateName(final byte[] raw,
@@ -330,8 +570,9 @@ public class ObjectChecker {
if (nextNamePos + 1 == nextPtr)
return false;
- final int cmp = pathCompare(raw, thisNamePos, thisNameEnd,
- FileMode.TREE.getBits(), nextNamePos, nextPtr - 1, nextMode);
+ int cmp = compareSameName(
+ raw, thisNamePos, thisNameEnd,
+ raw, nextNamePos, nextPtr - 1, nextMode);
if (cmp < 0)
return false;
else if (cmp == 0)
@@ -349,7 +590,23 @@ public class ObjectChecker {
* @throws CorruptObjectException
* if any error was detected.
*/
- public void checkTree(final byte[] raw) throws CorruptObjectException {
+ public void checkTree(byte[] raw) throws CorruptObjectException {
+ checkTree(idFor(OBJ_TREE, raw), raw);
+ }
+
+ /**
+ * Check a canonical formatted tree for errors.
+ *
+ * @param id
+ * identity of the object being checked.
+ * @param raw
+ * the raw tree data. The array is never modified.
+ * @throws CorruptObjectException
+ * if any error was detected.
+ * @since 4.2
+ */
+ public void checkTree(@Nullable AnyObjectId id, byte[] raw)
+ throws CorruptObjectException {
final int sz = raw.length;
int ptr = 0;
int lastNameB = 0, lastNameE = 0, lastMode = 0;
@@ -360,69 +617,118 @@ public class ObjectChecker {
while (ptr < sz) {
int thisMode = 0;
for (;;) {
- if (ptr == sz)
- throw new CorruptObjectException("truncated in mode");
+ if (ptr == sz) {
+ throw new CorruptObjectException(
+ JGitText.get().corruptObjectTruncatedInMode);
+ }
final byte c = raw[ptr++];
if (' ' == c)
break;
- if (c < '0' || c > '7')
- throw new CorruptObjectException("invalid mode character");
- if (thisMode == 0 && c == '0' && !allowZeroMode)
- throw new CorruptObjectException("mode starts with '0'");
+ if (c < '0' || c > '7') {
+ throw new CorruptObjectException(
+ JGitText.get().corruptObjectInvalidModeChar);
+ }
+ if (thisMode == 0 && c == '0') {
+ report(ZERO_PADDED_FILEMODE, id,
+ JGitText.get().corruptObjectInvalidModeStartsZero);
+ }
thisMode <<= 3;
thisMode += c - '0';
}
- if (FileMode.fromBits(thisMode).getObjectType() == Constants.OBJ_BAD)
- throw new CorruptObjectException("invalid mode " + thisMode);
+ if (FileMode.fromBits(thisMode).getObjectType() == OBJ_BAD) {
+ throw new CorruptObjectException(MessageFormat.format(
+ JGitText.get().corruptObjectInvalidMode2,
+ Integer.valueOf(thisMode)));
+ }
final int thisNameB = ptr;
- ptr = scanPathSegment(raw, ptr, sz);
- if (ptr == sz || raw[ptr] != 0)
- throw new CorruptObjectException("truncated in name");
- checkPathSegment2(raw, thisNameB, ptr);
+ ptr = scanPathSegment(raw, ptr, sz, id);
+ if (ptr == sz || raw[ptr] != 0) {
+ throw new CorruptObjectException(
+ JGitText.get().corruptObjectTruncatedInName);
+ }
+ checkPathSegment2(raw, thisNameB, ptr, id);
if (normalized != null) {
- if (!normalized.add(normalize(raw, thisNameB, ptr)))
- throw new CorruptObjectException("duplicate entry names");
- } else if (duplicateName(raw, thisNameB, ptr))
- throw new CorruptObjectException("duplicate entry names");
+ if (!normalized.add(normalize(raw, thisNameB, ptr))) {
+ report(DUPLICATE_ENTRIES, id,
+ JGitText.get().corruptObjectDuplicateEntryNames);
+ }
+ } else if (duplicateName(raw, thisNameB, ptr)) {
+ report(DUPLICATE_ENTRIES, id,
+ JGitText.get().corruptObjectDuplicateEntryNames);
+ }
if (lastNameB != 0) {
- final int cmp = pathCompare(raw, lastNameB, lastNameE,
- lastMode, thisNameB, ptr, thisMode);
- if (cmp > 0)
- throw new CorruptObjectException("incorrectly sorted");
+ int cmp = compare(
+ raw, lastNameB, lastNameE, lastMode,
+ raw, thisNameB, ptr, thisMode);
+ if (cmp > 0) {
+ report(TREE_NOT_SORTED, id,
+ JGitText.get().corruptObjectIncorrectSorting);
+ }
}
lastNameB = thisNameB;
lastNameE = ptr;
lastMode = thisMode;
- ptr += 1 + Constants.OBJECT_ID_LENGTH;
- if (ptr > sz)
- throw new CorruptObjectException("truncated in object id");
+ ptr += 1 + OBJECT_ID_LENGTH;
+ if (ptr > sz) {
+ throw new CorruptObjectException(
+ JGitText.get().corruptObjectTruncatedInObjectId);
+ }
+ if (ObjectId.zeroId().compareTo(raw, ptr - OBJECT_ID_LENGTH) == 0) {
+ report(NULL_SHA1, id, JGitText.get().corruptObjectZeroId);
+ }
}
}
- private int scanPathSegment(byte[] raw, int ptr, int end)
- throws CorruptObjectException {
+ private int scanPathSegment(byte[] raw, int ptr, int end,
+ @Nullable AnyObjectId id) throws CorruptObjectException {
for (; ptr < end; ptr++) {
byte c = raw[ptr];
- if (c == 0)
+ if (c == 0) {
return ptr;
- if (c == '/')
- throw new CorruptObjectException("name contains '/'");
+ }
+ if (c == '/') {
+ report(FULL_PATHNAME, id,
+ JGitText.get().corruptObjectNameContainsSlash);
+ }
if (windows && isInvalidOnWindows(c)) {
- if (c > 31)
+ if (c > 31) {
throw new CorruptObjectException(String.format(
- "name contains '%c'", c));
+ JGitText.get().corruptObjectNameContainsChar,
+ Byte.valueOf(c)));
+ }
throw new CorruptObjectException(String.format(
- "name contains byte 0x%x", c & 0xff));
+ JGitText.get().corruptObjectNameContainsByte,
+ Integer.valueOf(c & 0xff)));
}
}
return ptr;
}
+ @SuppressWarnings("resource")
+ @Nullable
+ private ObjectId idFor(int objType, byte[] raw) {
+ if (skipList != null) {
+ return new ObjectInserter.Formatter().idFor(objType, raw);
+ }
+ return null;
+ }
+
+ private void report(@NonNull ErrorType err, @Nullable AnyObjectId id,
+ String why) throws CorruptObjectException {
+ if (errors.contains(err)
+ && (id == null || skipList == null || !skipList.contains(id))) {
+ if (id != null) {
+ throw new CorruptObjectException(err, id, why);
+ }
+ throw new CorruptObjectException(why);
+ }
+ }
+
/**
* Check tree path entry for validity.
* <p>
@@ -473,67 +779,82 @@ public class ObjectChecker {
*/
public void checkPathSegment(byte[] raw, int ptr, int end)
throws CorruptObjectException {
- int e = scanPathSegment(raw, ptr, end);
+ int e = scanPathSegment(raw, ptr, end, null);
if (e < end && raw[e] == 0)
- throw new CorruptObjectException("name contains byte 0x00");
- checkPathSegment2(raw, ptr, end);
+ throw new CorruptObjectException(
+ JGitText.get().corruptObjectNameContainsNullByte);
+ checkPathSegment2(raw, ptr, end, null);
}
- private void checkPathSegment2(byte[] raw, int ptr, int end)
- throws CorruptObjectException {
- if (ptr == end)
- throw new CorruptObjectException("zero length name");
+ private void checkPathSegment2(byte[] raw, int ptr, int end,
+ @Nullable AnyObjectId id) throws CorruptObjectException {
+ if (ptr == end) {
+ report(EMPTY_NAME, id, JGitText.get().corruptObjectNameZeroLength);
+ return;
+ }
+
if (raw[ptr] == '.') {
switch (end - ptr) {
case 1:
- throw new CorruptObjectException("invalid name '.'");
+ report(HAS_DOT, id, JGitText.get().corruptObjectNameDot);
+ break;
case 2:
- if (raw[ptr + 1] == '.')
- throw new CorruptObjectException("invalid name '..'");
+ if (raw[ptr + 1] == '.') {
+ report(HAS_DOTDOT, id,
+ JGitText.get().corruptObjectNameDotDot);
+ }
break;
case 4:
- if (isGit(raw, ptr + 1))
- throw new CorruptObjectException(String.format(
- "invalid name '%s'",
+ if (isGit(raw, ptr + 1)) {
+ report(HAS_DOTGIT, id, String.format(
+ JGitText.get().corruptObjectInvalidName,
RawParseUtils.decode(raw, ptr, end)));
+ }
break;
default:
- if (end - ptr > 4 && isNormalizedGit(raw, ptr + 1, end))
- throw new CorruptObjectException(String.format(
- "invalid name '%s'",
+ if (end - ptr > 4 && isNormalizedGit(raw, ptr + 1, end)) {
+ report(HAS_DOTGIT, id, String.format(
+ JGitText.get().corruptObjectInvalidName,
RawParseUtils.decode(raw, ptr, end)));
+ }
}
} else if (isGitTilde1(raw, ptr, end)) {
- throw new CorruptObjectException(String.format("invalid name '%s'",
+ report(HAS_DOTGIT, id, String.format(
+ JGitText.get().corruptObjectInvalidName,
RawParseUtils.decode(raw, ptr, end)));
}
-
- if (macosx && isMacHFSGit(raw, ptr, end))
- throw new CorruptObjectException(String.format(
- "invalid name '%s' contains ignorable Unicode characters",
+ if (macosx && isMacHFSGit(raw, ptr, end, id)) {
+ report(HAS_DOTGIT, id, String.format(
+ JGitText.get().corruptObjectInvalidNameIgnorableUnicode,
RawParseUtils.decode(raw, ptr, end)));
+ }
if (windows) {
// Windows ignores space and dot at end of file name.
- if (raw[end - 1] == ' ' || raw[end - 1] == '.')
- throw new CorruptObjectException("invalid name ends with '"
- + ((char) raw[end - 1]) + "'");
- if (end - ptr >= 3)
- checkNotWindowsDevice(raw, ptr, end);
+ if (raw[end - 1] == ' ' || raw[end - 1] == '.') {
+ report(WIN32_BAD_NAME, id, String.format(
+ JGitText.get().corruptObjectInvalidNameEnd,
+ Character.valueOf(((char) raw[end - 1]))));
+ }
+ if (end - ptr >= 3) {
+ checkNotWindowsDevice(raw, ptr, end, id);
+ }
}
}
// Mac's HFS+ folds permutations of ".git" and Unicode ignorable characters
// to ".git" therefore we should prevent such names
- private static boolean isMacHFSGit(byte[] raw, int ptr, int end)
- throws CorruptObjectException {
+ private boolean isMacHFSGit(byte[] raw, int ptr, int end,
+ @Nullable AnyObjectId id) throws CorruptObjectException {
boolean ignorable = false;
byte[] git = new byte[] { '.', 'g', 'i', 't' };
int g = 0;
while (ptr < end) {
switch (raw[ptr]) {
case (byte) 0xe2: // http://www.utf8-chartable.de/unicode-utf8-table.pl?start=8192
- checkTruncatedIgnorableUTF8(raw, ptr, end);
+ if (!checkTruncatedIgnorableUTF8(raw, ptr, end, id)) {
+ return false;
+ }
switch (raw[ptr + 1]) {
case (byte) 0x80:
switch (raw[ptr + 2]) {
@@ -566,10 +887,13 @@ public class ObjectChecker {
default:
return false;
}
+ default:
+ return false;
}
- break;
case (byte) 0xef: // http://www.utf8-chartable.de/unicode-utf8-table.pl?start=65024
- checkTruncatedIgnorableUTF8(raw, ptr, end);
+ if (!checkTruncatedIgnorableUTF8(raw, ptr, end, id)) {
+ return false;
+ }
// U+FEFF 0xefbbbf ZERO WIDTH NO-BREAK SPACE
if ((raw[ptr + 1] == (byte) 0xbb)
&& (raw[ptr + 2] == (byte) 0xbf)) {
@@ -590,12 +914,15 @@ public class ObjectChecker {
return false;
}
- private static void checkTruncatedIgnorableUTF8(byte[] raw, int ptr, int end)
- throws CorruptObjectException {
- if ((ptr + 2) >= end)
- throw new CorruptObjectException(MessageFormat.format(
- "invalid name contains byte sequence ''{0}'' which is not a valid UTF-8 character",
+ private boolean checkTruncatedIgnorableUTF8(byte[] raw, int ptr, int end,
+ @Nullable AnyObjectId id) throws CorruptObjectException {
+ if ((ptr + 2) >= end) {
+ report(BAD_UTF8, id, MessageFormat.format(
+ JGitText.get().corruptObjectInvalidNameInvalidUtf8,
toHexString(raw, ptr, end)));
+ return false;
+ }
+ return true;
}
private static String toHexString(byte[] raw, int ptr, int end) {
@@ -605,30 +932,36 @@ public class ObjectChecker {
return b.toString();
}
- private static void checkNotWindowsDevice(byte[] raw, int ptr, int end)
- throws CorruptObjectException {
+ private void checkNotWindowsDevice(byte[] raw, int ptr, int end,
+ @Nullable AnyObjectId id) throws CorruptObjectException {
switch (toLower(raw[ptr])) {
case 'a': // AUX
if (end - ptr >= 3
&& toLower(raw[ptr + 1]) == 'u'
&& toLower(raw[ptr + 2]) == 'x'
- && (end - ptr == 3 || raw[ptr + 3] == '.'))
- throw new CorruptObjectException("invalid name 'AUX'");
+ && (end - ptr == 3 || raw[ptr + 3] == '.')) {
+ report(WIN32_BAD_NAME, id,
+ JGitText.get().corruptObjectInvalidNameAux);
+ }
break;
case 'c': // CON, COM[1-9]
if (end - ptr >= 3
&& toLower(raw[ptr + 2]) == 'n'
&& toLower(raw[ptr + 1]) == 'o'
- && (end - ptr == 3 || raw[ptr + 3] == '.'))
- throw new CorruptObjectException("invalid name 'CON'");
+ && (end - ptr == 3 || raw[ptr + 3] == '.')) {
+ report(WIN32_BAD_NAME, id,
+ JGitText.get().corruptObjectInvalidNameCon);
+ }
if (end - ptr >= 4
&& toLower(raw[ptr + 2]) == 'm'
&& toLower(raw[ptr + 1]) == 'o'
&& isPositiveDigit(raw[ptr + 3])
- && (end - ptr == 4 || raw[ptr + 4] == '.'))
- throw new CorruptObjectException("invalid name 'COM"
- + ((char) raw[ptr + 3]) + "'");
+ && (end - ptr == 4 || raw[ptr + 4] == '.')) {
+ report(WIN32_BAD_NAME, id, String.format(
+ JGitText.get().corruptObjectInvalidNameCom,
+ Character.valueOf(((char) raw[ptr + 3]))));
+ }
break;
case 'l': // LPT[1-9]
@@ -636,25 +969,31 @@ public class ObjectChecker {
&& toLower(raw[ptr + 1]) == 'p'
&& toLower(raw[ptr + 2]) == 't'
&& isPositiveDigit(raw[ptr + 3])
- && (end - ptr == 4 || raw[ptr + 4] == '.'))
- throw new CorruptObjectException("invalid name 'LPT"
- + ((char) raw[ptr + 3]) + "'");
+ && (end - ptr == 4 || raw[ptr + 4] == '.')) {
+ report(WIN32_BAD_NAME, id, String.format(
+ JGitText.get().corruptObjectInvalidNameLpt,
+ Character.valueOf(((char) raw[ptr + 3]))));
+ }
break;
case 'n': // NUL
if (end - ptr >= 3
&& toLower(raw[ptr + 1]) == 'u'
&& toLower(raw[ptr + 2]) == 'l'
- && (end - ptr == 3 || raw[ptr + 3] == '.'))
- throw new CorruptObjectException("invalid name 'NUL'");
+ && (end - ptr == 3 || raw[ptr + 3] == '.')) {
+ report(WIN32_BAD_NAME, id,
+ JGitText.get().corruptObjectInvalidNameNul);
+ }
break;
case 'p': // PRN
if (end - ptr >= 3
&& toLower(raw[ptr + 1]) == 'r'
&& toLower(raw[ptr + 2]) == 'n'
- && (end - ptr == 3 || raw[ptr + 3] == '.'))
- throw new CorruptObjectException("invalid name 'PRN'");
+ && (end - ptr == 3 || raw[ptr + 3] == '.')) {
+ report(WIN32_BAD_NAME, id,
+ JGitText.get().corruptObjectInvalidNamePrn);
+ }
break;
}
}
@@ -707,6 +1046,15 @@ public class ObjectChecker {
return false;
}
+ private boolean match(byte[] b, byte[] src) {
+ int r = RawParseUtils.match(b, bufPtr.value, src);
+ if (r < 0) {
+ return false;
+ }
+ bufPtr.value = r;
+ return true;
+ }
+
private static char toLower(byte b) {
if ('A' <= b && b <= 'Z')
return (char) (b + ('a' - 'A'));
@@ -731,58 +1079,6 @@ public class ObjectChecker {
private String normalize(byte[] raw, int ptr, int end) {
String n = RawParseUtils.decode(raw, ptr, end).toLowerCase(Locale.US);
- return macosx ? Normalizer.normalize(n) : n;
- }
-
- private static class Normalizer {
- // TODO Simplify invocation to Normalizer after dropping Java 5.
- private static final Method normalize;
- private static final Object nfc;
- static {
- Method method;
- Object formNfc;
- try {
- Class<?> formClazz = Class.forName("java.text.Normalizer$Form"); //$NON-NLS-1$
- formNfc = formClazz.getField("NFC").get(null); //$NON-NLS-1$
- method = Class.forName("java.text.Normalizer") //$NON-NLS-1$
- .getMethod("normalize", CharSequence.class, formClazz); //$NON-NLS-1$
- } catch (ClassNotFoundException e) {
- method = null;
- formNfc = null;
- } catch (NoSuchFieldException e) {
- method = null;
- formNfc = null;
- } catch (NoSuchMethodException e) {
- method = null;
- formNfc = null;
- } catch (SecurityException e) {
- method = null;
- formNfc = null;
- } catch (IllegalArgumentException e) {
- method = null;
- formNfc = null;
- } catch (IllegalAccessException e) {
- method = null;
- formNfc = null;
- }
- normalize = method;
- nfc = formNfc;
- }
-
- static String normalize(String in) {
- if (normalize == null)
- return in;
- try {
- return (String) normalize.invoke(null, in, nfc);
- } catch (IllegalAccessException e) {
- return in;
- } catch (InvocationTargetException e) {
- if (e.getCause() instanceof RuntimeException)
- throw (RuntimeException) e.getCause();
- if (e.getCause() instanceof Error)
- throw (Error) e.getCause();
- return in;
- }
- }
+ return macosx ? Normalizer.normalize(n, Normalizer.Form.NFC) : n;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java
index 0cc51d1..2abd6da 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDatabase.java
@@ -120,11 +120,8 @@ public abstract class ObjectDatabase {
* the object store cannot be accessed.
*/
public boolean has(final AnyObjectId objectId) throws IOException {
- final ObjectReader or = newReader();
- try {
+ try (final ObjectReader or = newReader()) {
return or.has(objectId);
- } finally {
- or.release();
}
}
@@ -172,11 +169,8 @@ public abstract class ObjectDatabase {
public ObjectLoader open(AnyObjectId objectId, int typeHint)
throws MissingObjectException, IncorrectObjectTypeException,
IOException {
- final ObjectReader or = newReader();
- try {
+ try (final ObjectReader or = newReader()) {
return or.open(objectId, typeHint);
- } finally {
- or.release();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectId.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectId.java
index 2f04751..4edb38c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectId.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectId.java
@@ -111,16 +111,16 @@ public class ObjectId extends AnyObjectId implements Serializable {
}
/**
- * Compare to object identifier byte sequences for equality.
+ * Compare two object identifier byte sequences for equality.
*
* @param firstBuffer
* the first buffer to compare against. Must have at least 20
- * bytes from position ai through the end of the buffer.
+ * bytes from position fi through the end of the buffer.
* @param fi
* first offset within firstBuffer to begin testing.
* @param secondBuffer
- * the second buffer to compare against. Must have at least 2
- * bytes from position bi through the end of the buffer.
+ * the second buffer to compare against. Must have at least 20
+ * bytes from position si through the end of the buffer.
* @param si
* first offset within secondBuffer to begin testing.
* @return true if the two identifiers are the same.
@@ -228,8 +228,9 @@ public class ObjectId extends AnyObjectId implements Serializable {
* @return the converted object id.
*/
public static ObjectId fromString(final String str) {
- if (str.length() != Constants.OBJECT_ID_STRING_LENGTH)
- throw new IllegalArgumentException("Invalid id: " + str);
+ if (str.length() != Constants.OBJECT_ID_STRING_LENGTH) {
+ throw new InvalidObjectIdException(str);
+ }
return fromHexString(Constants.encodeASCII(str), 0);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdOwnerMap.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdOwnerMap.java
index c9b483f..442261c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdOwnerMap.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdOwnerMap.java
@@ -67,8 +67,8 @@ import java.util.NoSuchElementException;
* @param <V>
* type of subclass of ObjectId that will be stored in the map.
*/
-public class ObjectIdOwnerMap<V extends ObjectIdOwnerMap.Entry> implements
- Iterable<V> {
+public class ObjectIdOwnerMap<V extends ObjectIdOwnerMap.Entry>
+ implements Iterable<V>, ObjectIdSet {
/** Size of the initial directory, will grow as necessary. */
private static final int INITIAL_DIRECTORY = 1024;
@@ -83,16 +83,16 @@ public class ObjectIdOwnerMap<V extends ObjectIdOwnerMap.Entry> implements
* The low {@link #bits} of the SHA-1 are used to select the segment from
* this directory. Each segment is constant sized at 2^SEGMENT_BITS.
*/
- private V[][] directory;
+ V[][] directory;
/** Total number of objects in this map. */
- private int size;
+ int size;
/** The map doubles in capacity when {@link #size} reaches this target. */
private int grow;
/** Number of low bits used to form the index into {@link #directory}. */
- private int bits;
+ int bits;
/** Low bit mask to index into {@link #directory}, {@code 2^bits-1}. */
private int mask;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java
index f481c77..c286f5e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java
@@ -44,6 +44,9 @@
package org.eclipse.jgit.lib;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+
/** A {@link Ref} that points directly at an {@link ObjectId}. */
public abstract class ObjectIdRef implements Ref {
/** Any reference whose peeled value is not yet known. */
@@ -56,13 +59,15 @@ public abstract class ObjectIdRef implements Ref {
* @param name
* name of this ref.
* @param id
- * current value of the ref. May be null to indicate a ref
- * that does not exist yet.
+ * current value of the ref. May be {@code null} to indicate
+ * a ref that does not exist yet.
*/
- public Unpeeled(Storage st, String name, ObjectId id) {
+ public Unpeeled(@NonNull Storage st, @NonNull String name,
+ @Nullable ObjectId id) {
super(st, name, id);
}
+ @Nullable
public ObjectId getPeeledObjectId() {
return null;
}
@@ -88,11 +93,13 @@ public abstract class ObjectIdRef implements Ref {
* @param p
* the first non-tag object that tag {@code id} points to.
*/
- public PeeledTag(Storage st, String name, ObjectId id, ObjectId p) {
+ public PeeledTag(@NonNull Storage st, @NonNull String name,
+ @Nullable ObjectId id, @NonNull ObjectId p) {
super(st, name, id);
peeledObjectId = p;
}
+ @NonNull
public ObjectId getPeeledObjectId() {
return peeledObjectId;
}
@@ -112,13 +119,15 @@ public abstract class ObjectIdRef implements Ref {
* @param name
* name of this ref.
* @param id
- * current value of the ref. May be null to indicate a ref
- * that does not exist yet.
+ * current value of the ref. May be {@code null} to indicate
+ * a ref that does not exist yet.
*/
- public PeeledNonTag(Storage st, String name, ObjectId id) {
+ public PeeledNonTag(@NonNull Storage st, @NonNull String name,
+ @Nullable ObjectId id) {
super(st, name, id);
}
+ @Nullable
public ObjectId getPeeledObjectId() {
return null;
}
@@ -142,15 +151,17 @@ public abstract class ObjectIdRef implements Ref {
* @param name
* name of this ref.
* @param id
- * current value of the ref. May be null to indicate a ref that
- * does not exist yet.
+ * current value of the ref. May be {@code null} to indicate a
+ * ref that does not exist yet.
*/
- protected ObjectIdRef(Storage st, String name, ObjectId id) {
+ protected ObjectIdRef(@NonNull Storage st, @NonNull String name,
+ @Nullable ObjectId id) {
this.name = name;
this.storage = st;
this.objectId = id;
}
+ @NonNull
public String getName() {
return name;
}
@@ -159,22 +170,27 @@ public abstract class ObjectIdRef implements Ref {
return false;
}
+ @NonNull
public Ref getLeaf() {
return this;
}
+ @NonNull
public Ref getTarget() {
return this;
}
+ @Nullable
public ObjectId getObjectId() {
return objectId;
}
+ @NonNull
public Storage getStorage() {
return storage;
}
+ @NonNull
@Override
public String toString() {
StringBuilder r = new StringBuilder();
diff --git a/org.eclipse.jgit.console/src/org/eclipse/jgit/console/ConsoleText.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdSet.java
similarity index 76%
rename from org.eclipse.jgit.console/src/org/eclipse/jgit/console/ConsoleText.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdSet.java
index 281d937..0b58484 100644
--- a/org.eclipse.jgit.console/src/org/eclipse/jgit/console/ConsoleText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdSet.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, Sasa Zivkov <sasa.zivkov at sap.com>
+ * Copyright (C) 2015, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -41,27 +41,24 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.console;
-
-import org.eclipse.jgit.nls.NLS;
-import org.eclipse.jgit.nls.TranslationBundle;
+package org.eclipse.jgit.lib;
/**
- * Translation bundle for JGit console
+ * Simple set of ObjectIds.
+ * <p>
+ * Usually backed by a read-only data structure such as
+ * {@link org.eclipse.jgit.internal.storage.file.PackIndex}. Mutable types like
+ * {@link ObjectIdOwnerMap} also implement the interface by checking keys.
+ *
+ * @since 4.2
*/
-public class ConsoleText extends TranslationBundle {
-
+public interface ObjectIdSet {
/**
- * @return an instance of this translation bundle
+ * Returns true if the objectId is contained within the collection.
+ *
+ * @param objectId
+ * the objectId to find
+ * @return whether the collection contains the objectId.
*/
- public static ConsoleText get() {
- return NLS.getBundleFor(ConsoleText.class);
- }
-
- // @formatter:off
- /***/ public String answerNo;
- /***/ public String answerYes;
- /***/ public String noSystemConsoleAvailable;
- /***/ public String password;
- /***/ public String usernameFor;
+ boolean contains(AnyObjectId objectId);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdSubclassMap.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdSubclassMap.java
index 69972dc..faed64b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdSubclassMap.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdSubclassMap.java
@@ -60,16 +60,17 @@ import java.util.NoSuchElementException;
* @param <V>
* type of subclass of ObjectId that will be stored in the map.
*/
-public class ObjectIdSubclassMap<V extends ObjectId> implements Iterable<V> {
+public class ObjectIdSubclassMap<V extends ObjectId>
+ implements Iterable<V>, ObjectIdSet {
private static final int INITIAL_TABLE_SIZE = 2048;
- private int size;
+ int size;
private int grow;
private int mask;
- private V[] table;
+ V[] table;
/** Create an empty map. */
public ObjectIdSubclassMap() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectInserter.java
index 5c13ef3..d9da65f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectInserter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectInserter.java
@@ -52,6 +52,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
+import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.transport.PackParser;
/**
@@ -63,10 +64,10 @@ import org.eclipse.jgit.transport.PackParser;
* <p>
* Objects written by an inserter may not be immediately visible for reading
* after the insert method completes. Callers must invoke either
- * {@link #release()} or {@link #flush()} prior to updating references or
+ * {@link #close()} or {@link #flush()} prior to updating references or
* otherwise making the returned ObjectIds visible to other code.
*/
-public abstract class ObjectInserter {
+public abstract class ObjectInserter implements AutoCloseable {
/** An inserter that can be used for formatting and id generation only. */
public static class Formatter extends ObjectInserter {
@Override
@@ -91,7 +92,7 @@ public abstract class ObjectInserter {
}
@Override
- public void release() {
+ public void close() {
// Do nothing.
}
}
@@ -149,8 +150,8 @@ public abstract class ObjectInserter {
delegate().flush();
}
- public void release() {
- delegate().release();
+ public void close() {
+ delegate().close();
}
}
@@ -263,7 +264,7 @@ public abstract class ObjectInserter {
while (length > 0) {
int n = in.read(buf, 0, (int) Math.min(length, buf.length));
if (n < 0)
- throw new EOFException("Unexpected end of input");
+ throw new EOFException(JGitText.get().unexpectedEndOfInput);
md.update(buf, 0, n);
length -= n;
}
@@ -421,6 +422,9 @@ public abstract class ObjectInserter {
* <p>
* An inserter that has been released can be used again, but may need to be
* released after the subsequent usage.
+ *
+ * @since 4.0
*/
- public abstract void release();
+ @Override
+ public abstract void close();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
index 58c1418..77cfb03 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java
@@ -53,9 +53,6 @@ import java.util.Set;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.internal.storage.pack.ObjectReuseAsIs;
-import org.eclipse.jgit.revwalk.ObjectWalk;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
/**
* Reads an {@link ObjectDatabase} for a single thread.
@@ -63,7 +60,7 @@ import org.eclipse.jgit.revwalk.RevWalk;
* Readers that can support efficient reuse of pack encoded objects should also
* implement the companion interface {@link ObjectReuseAsIs}.
*/
-public abstract class ObjectReader {
+public abstract class ObjectReader implements AutoCloseable {
/** Type hint indicating the caller doesn't know the type. */
public static final int OBJ_ANY = -1;
@@ -399,44 +396,6 @@ public abstract class ObjectReader {
}
/**
- * Advice from a {@link RevWalk} that a walk is starting from these roots.
- *
- * @param walk
- * the revision pool that is using this reader.
- * @param roots
- * starting points of the revision walk. The starting points have
- * their headers parsed, but might be missing bodies.
- * @throws IOException
- * the reader cannot initialize itself to support the walk.
- */
- public void walkAdviceBeginCommits(RevWalk walk, Collection<RevCommit> roots)
- throws IOException {
- // Do nothing by default, most readers don't want or need advice.
- }
-
- /**
- * Advice from an {@link ObjectWalk} that trees will be traversed.
- *
- * @param ow
- * the object pool that is using this reader.
- * @param min
- * the first commit whose root tree will be read.
- * @param max
- * the last commit whose root tree will be read.
- * @throws IOException
- * the reader cannot initialize itself to support the walk.
- */
- public void walkAdviceBeginTrees(ObjectWalk ow, RevCommit min, RevCommit max)
- throws IOException {
- // Do nothing by default, most readers don't want or need advice.
- }
-
- /** Advice from that a walk is over. */
- public void walkAdviceEnd() {
- // Do nothing by default, most readers don't want or need advice.
- }
-
- /**
* Advise the reader to avoid unreachable objects.
* <p>
* While enabled the reader will skip over anything previously proven to be
@@ -467,8 +426,9 @@ public abstract class ObjectReader {
* <p>
* A reader that has been released can be used again, but may need to be
* released after the subsequent usage.
+ *
+ * @since 4.0
*/
- public void release() {
- // Do nothing.
- }
+ @Override
+ public abstract void close();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java
index 8f7e3ef..2ecc60c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/PersonIdent.java
@@ -51,6 +51,7 @@ import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
+import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.util.SystemReader;
/**
@@ -62,6 +63,54 @@ import org.eclipse.jgit.util.SystemReader;
public class PersonIdent implements Serializable {
private static final long serialVersionUID = 1L;
+ /**
+ * @param tzOffset
+ * timezone offset as in {@link #getTimeZoneOffset()}.
+ * @return time zone object for the given offset.
+ * @since 4.1
+ */
+ public static TimeZone getTimeZone(int tzOffset) {
+ StringBuilder tzId = new StringBuilder(8);
+ tzId.append("GMT"); //$NON-NLS-1$
+ appendTimezone(tzId, tzOffset);
+ return TimeZone.getTimeZone(tzId.toString());
+ }
+
+ /**
+ * Format a timezone offset.
+ *
+ * @param r
+ * string builder to append to.
+ * @param offset
+ * timezone offset as in {@link #getTimeZoneOffset()}.
+ * @since 4.1
+ */
+ public static void appendTimezone(StringBuilder r, int offset) {
+ final char sign;
+ final int offsetHours;
+ final int offsetMins;
+
+ if (offset < 0) {
+ sign = '-';
+ offset = -offset;
+ } else {
+ sign = '+';
+ }
+
+ offsetHours = offset / 60;
+ offsetMins = offset % 60;
+
+ r.append(sign);
+ if (offsetHours < 10) {
+ r.append('0');
+ }
+ r.append(offsetHours);
+ if (offsetMins < 10) {
+ r.append('0');
+ }
+ r.append(offsetMins);
+ }
+
private final String name;
private final String emailAddress;
@@ -181,10 +230,10 @@ public class PersonIdent implements Serializable {
final long aWhen, final int aTZ) {
if (aName == null)
throw new IllegalArgumentException(
- "Name of PersonIdent must not be null.");
+ JGitText.get().personIdentNameNonNull);
if (aEmailAddress == null)
throw new IllegalArgumentException(
- "E-mail address of PersonIdent must not be null.");
+ JGitText.get().personIdentEmailNonNull);
name = aName;
emailAddress = aEmailAddress;
when = aWhen;
@@ -216,10 +265,7 @@ public class PersonIdent implements Serializable {
* @return this person's declared time zone; null if time zone is unknown.
*/
public TimeZone getTimeZone() {
- StringBuilder tzId = new StringBuilder(8);
- tzId.append("GMT"); //$NON-NLS-1$
- appendTimezone(tzId);
- return TimeZone.getTimeZone(tzId.toString());
+ return getTimeZone(tzOffset);
}
/**
@@ -260,37 +306,10 @@ public class PersonIdent implements Serializable {
r.append("> "); //$NON-NLS-1$
r.append(when / 1000);
r.append(' ');
- appendTimezone(r);
+ appendTimezone(r, tzOffset);
return r.toString();
}
- private void appendTimezone(final StringBuilder r) {
- int offset = tzOffset;
- final char sign;
- final int offsetHours;
- final int offsetMins;
-
- if (offset < 0) {
- sign = '-';
- offset = -offset;
- } else {
- sign = '+';
- }
-
- offsetHours = offset / 60;
- offsetMins = offset % 60;
-
- r.append(sign);
- if (offsetHours < 10) {
- r.append('0');
- }
- r.append(offsetHours);
- if (offsetMins < 10) {
- r.append('0');
- }
- r.append(offsetMins);
- }
-
@SuppressWarnings("nls")
public String toString() {
final StringBuilder r = new StringBuilder();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java
index f119c44..a78a90f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java
@@ -43,6 +43,9 @@
package org.eclipse.jgit.lib;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+
/**
* Pairing of a name and the {@link ObjectId} it currently has.
* <p>
@@ -126,6 +129,7 @@ public interface Ref {
*
* @return name of this ref.
*/
+ @NonNull
public String getName();
/**
@@ -156,6 +160,7 @@ public interface Ref {
*
* @return the reference that actually stores the ObjectId value.
*/
+ @NonNull
public abstract Ref getLeaf();
/**
@@ -170,22 +175,27 @@ public interface Ref {
*
* @return the target reference, or {@code this}.
*/
+ @NonNull
public abstract Ref getTarget();
/**
* Cached value of this ref.
*
- * @return the value of this ref at the last time we read it.
+ * @return the value of this ref at the last time we read it. May be
+ * {@code null} to indicate a ref that does not exist yet or a
+ * symbolic ref pointing to an unborn branch.
*/
+ @Nullable
public abstract ObjectId getObjectId();
/**
* Cached value of <code>ref^{}</code> (the ref peeled to commit).
*
* @return if this ref is an annotated tag the id of the commit (or tree or
- * blob) that the annotated tag refers to; null if this ref does not
- * refer to an annotated tag.
+ * blob) that the annotated tag refers to; {@code null} if this ref
+ * does not refer to an annotated tag.
*/
+ @Nullable
public abstract ObjectId getPeeledObjectId();
/**
@@ -201,5 +211,6 @@ public interface Ref {
*
* @return type of ref.
*/
+ @NonNull
public abstract Storage getStorage();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
index 7fea880..c0c3862 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
@@ -47,9 +47,13 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+
/**
* Abstraction of name to {@link ObjectId} mapping.
* <p>
@@ -78,8 +82,10 @@ public abstract class RefDatabase {
* <p>
* If the reference is nested deeper than this depth, the implementation
* should either fail, or at least claim the reference does not exist.
+ *
+ * @since 4.2
*/
- protected static final int MAX_SYMBOLIC_REF_DEPTH = 5;
+ public static final int MAX_SYMBOLIC_REF_DEPTH = 5;
/** Magic value for {@link #getRefs(String)} to return all references. */
public static final String ALL = "";//$NON-NLS-1$
@@ -131,6 +137,7 @@ public abstract class RefDatabase {
* @since 2.3
* @see #isNameConflicting(String)
*/
+ @NonNull
public Collection<String> getConflictingNames(String name)
throws IOException {
Map<String, Ref> allRefs = getRefs(ALL);
@@ -168,6 +175,7 @@ public abstract class RefDatabase {
* @throws IOException
* the reference space cannot be accessed.
*/
+ @NonNull
public abstract RefUpdate newUpdate(String name, boolean detach)
throws IOException;
@@ -182,6 +190,7 @@ public abstract class RefDatabase {
* @throws IOException
* the reference space cannot be accessed.
*/
+ @NonNull
public abstract RefRename newRename(String fromName, String toName)
throws IOException;
@@ -192,6 +201,7 @@ public abstract class RefDatabase {
*
* @return a new batch update object.
*/
+ @NonNull
public BatchRefUpdate newBatchUpdate() {
return new BatchRefUpdate(this);
}
@@ -211,6 +221,9 @@ public abstract class RefDatabase {
* Aside from taking advantage of {@link #SEARCH_PATH}, this method may be
* able to more quickly resolve a single reference name than obtaining the
* complete namespace by {@code getRefs(ALL).get(name)}.
+ * <p>
+ * To read a specific reference without using @{link #SEARCH_PATH}, see
+ * {@link #exactRef(String)}.
*
* @param name
* the name of the reference. May be a short name which must be
@@ -219,9 +232,83 @@ public abstract class RefDatabase {
* @throws IOException
* the reference space cannot be accessed.
*/
+ @Nullable
public abstract Ref getRef(String name) throws IOException;
/**
+ * Read a single reference.
+ * <p>
+ * Unlike {@link #getRef}, this method expects an unshortened reference
+ * name and does not search using the standard {@link #SEARCH_PATH}.
+ *
+ * @param name
+ * the unabbreviated name of the reference.
+ * @return the reference (if it exists); else {@code null}.
+ * @throws IOException
+ * the reference space cannot be accessed.
+ * @since 4.1
+ */
+ @Nullable
+ public Ref exactRef(String name) throws IOException {
+ Ref ref = getRef(name);
+ if (ref == null || !name.equals(ref.getName())) {
+ return null;
+ }
+ return ref;
+ }
+
+ /**
+ * Read the specified references.
+ * <p>
+ * This method expects a list of unshortened reference names and returns
+ * a map from reference names to refs. Any named references that do not
+ * exist will not be included in the returned map.
+ *
+ * @param refs
+ * the unabbreviated names of references to look up.
+ * @return modifiable map describing any refs that exist among the ref
+ * ref names supplied. The map can be an unsorted map.
+ * @throws IOException
+ * the reference space cannot be accessed.
+ * @since 4.1
+ */
+ @NonNull
+ public Map<String, Ref> exactRef(String... refs) throws IOException {
+ Map<String, Ref> result = new HashMap<>(refs.length);
+ for (String name : refs) {
+ Ref ref = exactRef(name);
+ if (ref != null) {
+ result.put(name, ref);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Find the first named reference.
+ * <p>
+ * This method expects a list of unshortened reference names and returns
+ * the first that exists.
+ *
+ * @param refs
+ * the unabbreviated names of references to look up.
+ * @return the first named reference that exists (if any); else {@code null}.
+ * @throws IOException
+ * the reference space cannot be accessed.
+ * @since 4.1
+ */
+ @Nullable
+ public Ref firstExactRef(String... refs) throws IOException {
+ for (String name : refs) {
+ Ref ref = exactRef(name);
+ if (ref != null) {
+ return ref;
+ }
+ }
+ return null;
+ }
+
+ /**
* Get a section of the reference namespace.
*
* @param prefix
@@ -234,6 +321,7 @@ public abstract class RefDatabase {
* @throws IOException
* the reference space cannot be accessed.
*/
+ @NonNull
public abstract Map<String, Ref> getRefs(String prefix) throws IOException;
/**
@@ -242,11 +330,13 @@ public abstract class RefDatabase {
* The result list includes non-ref items such as MERGE_HEAD and
* FETCH_RESULT cast to be refs. The names of these refs are not returned by
* <code>getRefs(ALL)</code> but are accepted by {@link #getRef(String)}
+ * and {@link #exactRef(String)}.
*
* @return a list of additional refs
* @throws IOException
* the reference space cannot be accessed.
*/
+ @NonNull
public abstract List<Ref> getAdditionalRefs() throws IOException;
/**
@@ -263,10 +353,11 @@ public abstract class RefDatabase {
* @return {@code ref} if {@code ref.isPeeled()} is true; otherwise a new
* Ref object representing the same data as Ref, but isPeeled() will
* be true and getPeeledObjectId() will contain the peeled object
- * (or null).
+ * (or {@code null}).
* @throws IOException
* the reference space or object space cannot be accessed.
*/
+ @NonNull
public abstract Ref peel(Ref ref) throws IOException;
/**
@@ -290,9 +381,10 @@ public abstract class RefDatabase {
* @param name
* short name of ref to find, e.g. "master" to find
* "refs/heads/master" in map.
- * @return The first ref matching the name, or null if not found.
+ * @return The first ref matching the name, or {@code null} if not found.
* @since 3.4
*/
+ @Nullable
public static Ref findRef(Map<String, Ref> map, String name) {
for (String prefix : SEARCH_PATH) {
String fullname = prefix + name;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefRename.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefRename.java
index 05eb311..59f852b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefRename.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefRename.java
@@ -170,7 +170,7 @@ public abstract class RefRename {
*/
protected boolean needToUpdateHEAD() throws IOException {
Ref head = source.getRefDatabase().getRef(Constants.HEAD);
- if (head.isSymbolic()) {
+ if (head != null && head.isSymbolic()) {
head = head.getTarget();
return head.getName().equals(source.getName());
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
index f47dff7..4316cd0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefUpdate.java
@@ -52,6 +52,7 @@ import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.PushCertificate;
/**
* Creates, updates or deletes any reference.
@@ -165,6 +166,9 @@ public abstract class RefUpdate {
/** Result of the update operation. */
private Result result = Result.NOT_ATTEMPTED;
+ /** Push certificate associated with this update. */
+ private PushCertificate pushCert;
+
private final Ref ref;
/**
@@ -414,6 +418,31 @@ public abstract class RefUpdate {
}
/**
+ * Set a push certificate associated with this update.
+ * <p>
+ * This usually includes a command to update this ref, but is not required to.
+ *
+ * @param cert
+ * push certificate, may be null.
+ * @since 4.1
+ */
+ public void setPushCertificate(PushCertificate cert) {
+ pushCert = cert;
+ }
+
+ /**
+ * Set the push certificate associated with this update.
+ * <p>
+ * This usually includes a command to update this ref, but is not required to.
+ *
+ * @return push certificate, may be null.
+ * @since 4.1
+ */
+ protected PushCertificate getPushCertificate() {
+ return pushCert;
+ }
+
+ /**
* Get the status of this update.
* <p>
* The same value that was previously returned from an update method.
@@ -460,11 +489,8 @@ public abstract class RefUpdate {
* an unexpected IO error occurred while writing changes.
*/
public Result update() throws IOException {
- RevWalk rw = new RevWalk(getRepository());
- try {
+ try (RevWalk rw = new RevWalk(getRepository())) {
return update(rw);
- } finally {
- rw.release();
}
}
@@ -510,11 +536,8 @@ public abstract class RefUpdate {
* @throws IOException
*/
public Result delete() throws IOException {
- RevWalk rw = new RevWalk(getRepository());
- try {
+ try (RevWalk rw = new RevWalk(getRepository())) {
return delete(rw);
- } finally {
- rw.release();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefWriter.java
index 747fa62..3a02b22 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefWriter.java
@@ -119,13 +119,20 @@ public abstract class RefWriter {
continue;
}
- r.getObjectId().copyTo(tmp, w);
+ ObjectId objectId = r.getObjectId();
+ if (objectId == null) {
+ // Symrefs to unborn branches aren't advertised in the info/refs
+ // file.
+ continue;
+ }
+ objectId.copyTo(tmp, w);
w.write('\t');
w.write(r.getName());
w.write('\n');
- if (r.getPeeledObjectId() != null) {
- r.getPeeledObjectId().copyTo(tmp, w);
+ ObjectId peeledObjectId = r.getPeeledObjectId();
+ if (peeledObjectId != null) {
+ peeledObjectId.copyTo(tmp, w);
w.write('\t');
w.write(r.getName());
w.write("^{}\n"); //$NON-NLS-1$
@@ -167,14 +174,21 @@ public abstract class RefWriter {
if (r.getStorage() != Ref.Storage.PACKED)
continue;
- r.getObjectId().copyTo(tmp, w);
+ ObjectId objectId = r.getObjectId();
+ if (objectId == null) {
+ // A packed ref cannot be a symref, let alone a symref
+ // to an unborn branch.
+ throw new NullPointerException();
+ }
+ objectId.copyTo(tmp, w);
w.write(' ');
w.write(r.getName());
w.write('\n');
- if (r.getPeeledObjectId() != null) {
+ ObjectId peeledObjectId = r.getPeeledObjectId();
+ if (peeledObjectId != null) {
w.write('^');
- r.getPeeledObjectId().copyTo(tmp, w);
+ peeledObjectId.copyTo(tmp, w);
w.write('\n');
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
index 6353a5b..f826613 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -64,6 +64,9 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.attributes.AttributesNodeProvider;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.errors.AmbiguousObjectException;
import org.eclipse.jgit.errors.CorruptObjectException;
@@ -99,7 +102,7 @@ import org.eclipse.jgit.util.io.SafeBufferedOutputStream;
* <p>
* This class is thread-safe.
*/
-public abstract class Repository {
+public abstract class Repository implements AutoCloseable {
private static final ListenerList globalListeners = new ListenerList();
/** @return the global listener list observing all events in this JVM. */
@@ -137,6 +140,7 @@ public abstract class Repository {
}
/** @return listeners observing only events on this repository. */
+ @NonNull
public ListenerList getListenerList() {
return myListeners;
}
@@ -181,7 +185,16 @@ public abstract class Repository {
*/
public abstract void create(boolean bare) throws IOException;
- /** @return local metadata directory; null if repository isn't local. */
+ /**
+ * @return local metadata directory; {@code null} if repository isn't local.
+ */
+ /*
+ * TODO This method should be annotated as Nullable, because in some
+ * specific configurations metadata is not located in the local file system
+ * (for example in memory databases). In "usual" repositories this
+ * annotation would only cause compiler errors at places where the actual
+ * directory can never be null.
+ */
public File getDirectory() {
return gitDir;
}
@@ -189,28 +202,52 @@ public abstract class Repository {
/**
* @return the object database which stores this repository's data.
*/
+ @NonNull
public abstract ObjectDatabase getObjectDatabase();
/** @return a new inserter to create objects in {@link #getObjectDatabase()} */
+ @NonNull
public ObjectInserter newObjectInserter() {
return getObjectDatabase().newInserter();
}
/** @return a new reader to read objects from {@link #getObjectDatabase()} */
+ @NonNull
public ObjectReader newObjectReader() {
return getObjectDatabase().newReader();
}
/** @return the reference database which stores the reference namespace. */
+ @NonNull
public abstract RefDatabase getRefDatabase();
/**
* @return the configuration of this repository
*/
+ @NonNull
public abstract StoredConfig getConfig();
/**
- * @return the used file system abstraction
+ * @return a new {@link AttributesNodeProvider}. This
+ * {@link AttributesNodeProvider} is lazy loaded only once. It means
+ * that it will not be updated after loading. Prefer creating new
+ * instance for each use.
+ * @since 4.2
+ */
+ @NonNull
+ public abstract AttributesNodeProvider createAttributesNodeProvider();
+
+
+ /**
+ * @return the used file system abstraction, or or {@code null} if
+ * repository isn't local.
+ */
+ /*
+ * TODO This method should be annotated as Nullable, because in some
+ * specific configurations metadata is not located in the local file system
+ * (for example in memory databases). In "usual" repositories this
+ * annotation would only cause compiler errors at places where the actual
+ * directory can never be null.
*/
public FS getFS() {
return fs;
@@ -244,6 +281,7 @@ public abstract class Repository {
* @throws IOException
* the object store cannot be accessed.
*/
+ @NonNull
public ObjectLoader open(final AnyObjectId objectId)
throws MissingObjectException, IOException {
return getObjectDatabase().open(objectId);
@@ -271,6 +309,7 @@ public abstract class Repository {
* @throws IOException
* the object store cannot be accessed.
*/
+ @NonNull
public ObjectLoader open(AnyObjectId objectId, int typeHint)
throws MissingObjectException, IncorrectObjectTypeException,
IOException {
@@ -289,6 +328,7 @@ public abstract class Repository {
* a symbolic ref was passed in and could not be resolved back
* to the base ref, as the symbolic ref could not be read.
*/
+ @NonNull
public RefUpdate updateRef(final String ref) throws IOException {
return updateRef(ref, false);
}
@@ -307,6 +347,7 @@ public abstract class Repository {
* a symbolic ref was passed in and could not be resolved back
* to the base ref, as the symbolic ref could not be read.
*/
+ @NonNull
public RefUpdate updateRef(final String ref, final boolean detach) throws IOException {
return getRefDatabase().newUpdate(ref, detach);
}
@@ -323,6 +364,7 @@ public abstract class Repository {
* the rename could not be performed.
*
*/
+ @NonNull
public RefRename renameRef(final String fromRef, final String toRef) throws IOException {
return getRefDatabase().newRename(fromRef, toRef);
}
@@ -362,7 +404,8 @@ public abstract class Repository {
*
* @param revstr
* A git object references expression
- * @return an ObjectId or null if revstr can't be resolved to any ObjectId
+ * @return an ObjectId or {@code null} if revstr can't be resolved to any
+ * ObjectId
* @throws AmbiguousObjectException
* {@code revstr} contains an abbreviated ObjectId and this
* repository contains more than one object which match to the
@@ -376,11 +419,11 @@ public abstract class Repository {
* @throws IOException
* on serious errors
*/
+ @Nullable
public ObjectId resolve(final String revstr)
throws AmbiguousObjectException, IncorrectObjectTypeException,
RevisionSyntaxException, IOException {
- RevWalk rw = new RevWalk(this);
- try {
+ try (RevWalk rw = new RevWalk(this)) {
Object resolved = resolve(rw, revstr);
if (resolved instanceof String) {
final Ref ref = getRef((String)resolved);
@@ -388,8 +431,6 @@ public abstract class Repository {
} else {
return (ObjectId) resolved;
}
- } finally {
- rw.release();
}
}
@@ -400,14 +441,15 @@ public abstract class Repository {
* expects a branch or revision id.
*
* @param revstr
- * @return object id or ref name from resolved expression
+ * @return object id or ref name from resolved expression or {@code null} if
+ * given expression cannot be resolved
* @throws AmbiguousObjectException
* @throws IOException
*/
+ @Nullable
public String simplify(final String revstr)
throws AmbiguousObjectException, IOException {
- RevWalk rw = new RevWalk(this);
- try {
+ try (RevWalk rw = new RevWalk(this)) {
Object resolved = resolve(rw, revstr);
if (resolved != null)
if (resolved instanceof String)
@@ -415,11 +457,10 @@ public abstract class Repository {
else
return ((AnyObjectId) resolved).getName();
return null;
- } finally {
- rw.release();
}
}
+ @Nullable
private Object resolve(final RevWalk rw, final String revstr)
throws IOException {
char[] revChars = revstr.toCharArray();
@@ -723,11 +764,13 @@ public abstract class Repository {
return true;
}
+ @Nullable
private RevObject parseSimple(RevWalk rw, String revstr) throws IOException {
ObjectId id = resolveSimple(revstr);
return id != null ? rw.parseAny(id) : null;
}
+ @Nullable
private ObjectId resolveSimple(final String revstr) throws IOException {
if (ObjectId.isId(revstr))
return ObjectId.fromString(revstr);
@@ -755,10 +798,14 @@ public abstract class Repository {
return null;
}
+ @Nullable
private String resolveReflogCheckout(int checkoutNo)
throws IOException {
- List<ReflogEntry> reflogEntries = getReflogReader(Constants.HEAD)
- .getReverseEntries();
+ ReflogReader reader = getReflogReader(Constants.HEAD);
+ if (reader == null) {
+ return null;
+ }
+ List<ReflogEntry> reflogEntries = reader.getReverseEntries();
for (ReflogEntry entry : reflogEntries) {
CheckoutEntry checkout = entry.parseCheckout();
if (checkout != null)
@@ -779,6 +826,11 @@ public abstract class Repository {
}
assert number >= 0;
ReflogReader reader = getReflogReader(ref.getName());
+ if (reader == null) {
+ throw new RevisionSyntaxException(
+ MessageFormat.format(JGitText.get().reflogEntryNotFound,
+ Integer.valueOf(number), ref.getName()));
+ }
ReflogEntry entry = reader.getReverseEntry(number);
if (entry == null)
throw new RevisionSyntaxException(MessageFormat.format(
@@ -788,11 +840,11 @@ public abstract class Repository {
return rw.parseCommit(entry.getNewId());
}
+ @Nullable
private ObjectId resolveAbbreviation(final String revstr) throws IOException,
AmbiguousObjectException {
AbbreviatedObjectId id = AbbreviatedObjectId.fromString(revstr);
- ObjectReader reader = newObjectReader();
- try {
+ try (ObjectReader reader = newObjectReader()) {
Collection<ObjectId> matches = reader.resolve(id);
if (matches.size() == 0)
return null;
@@ -800,8 +852,6 @@ public abstract class Repository {
return matches.iterator().next();
else
throw new AmbiguousObjectException(id, matches);
- } finally {
- reader.release();
}
}
@@ -827,11 +877,13 @@ public abstract class Repository {
getRefDatabase().close();
}
+ @NonNull
@SuppressWarnings("nls")
public String toString() {
String desc;
- if (getDirectory() != null)
- desc = getDirectory().getPath();
+ File directory = getDirectory();
+ if (directory != null)
+ desc = directory.getPath();
else
desc = getClass().getSimpleName() + "-" //$NON-NLS-1$
+ System.identityHashCode(this);
@@ -850,18 +902,25 @@ public abstract class Repository {
* Except when HEAD is detached, in which case this method returns the
* current ObjectId in hexadecimal string format.
*
- * @return name of current branch (for example {@code refs/heads/master}) or
- * an ObjectId in hex format if the current branch is detached.
+ * @return name of current branch (for example {@code refs/heads/master}),
+ * an ObjectId in hex format if the current branch is detached, or
+ * {@code null} if the repository is corrupt and has no HEAD
+ * reference.
* @throws IOException
*/
+ @Nullable
public String getFullBranch() throws IOException {
Ref head = getRef(Constants.HEAD);
- if (head == null)
+ if (head == null) {
return null;
- if (head.isSymbolic())
+ }
+ if (head.isSymbolic()) {
return head.getTarget().getName();
- if (head.getObjectId() != null)
- return head.getObjectId().name();
+ }
+ ObjectId objectId = head.getObjectId();
+ if (objectId != null) {
+ return objectId.name();
+ }
return null;
}
@@ -872,15 +931,17 @@ public abstract class Repository {
* leading prefix {@code refs/heads/} is removed from the reference before
* it is returned to the caller.
*
- * @return name of current branch (for example {@code master}), or an
- * ObjectId in hex format if the current branch is detached.
+ * @return name of current branch (for example {@code master}), an ObjectId
+ * in hex format if the current branch is detached, or {@code null}
+ * if the repository is corrupt and has no HEAD reference.
* @throws IOException
*/
+ @Nullable
public String getBranch() throws IOException {
String name = getFullBranch();
if (name != null)
return shortenRefName(name);
- return name;
+ return null;
}
/**
@@ -893,6 +954,7 @@ public abstract class Repository {
*
* @return unmodifiable collection of other known objects.
*/
+ @NonNull
public Set<ObjectId> getAdditionalHaves() {
return Collections.emptySet();
}
@@ -904,16 +966,53 @@ public abstract class Repository {
* the name of the ref to lookup. May be a short-hand form, e.g.
* "master" which is is automatically expanded to
* "refs/heads/master" if "refs/heads/master" already exists.
- * @return the Ref with the given name, or null if it does not exist
+ * @return the Ref with the given name, or {@code null} if it does not exist
* @throws IOException
+ * @deprecated Use {@link #exactRef(String)} or {@link #findRef(String)}
+ * instead.
*/
+ @Deprecated
+ @Nullable
public Ref getRef(final String name) throws IOException {
+ return findRef(name);
+ }
+
+ /**
+ * Get a ref by name.
+ *
+ * @param name
+ * the name of the ref to lookup. Must not be a short-hand
+ * form; e.g., "master" is not automatically expanded to
+ * "refs/heads/master".
+ * @return the Ref with the given name, or {@code null} if it does not exist
+ * @throws IOException
+ * @since 4.2
+ */
+ @Nullable
+ public Ref exactRef(String name) throws IOException {
+ return getRefDatabase().exactRef(name);
+ }
+
+ /**
+ * Search for a ref by (possibly abbreviated) name.
+ *
+ * @param name
+ * the name of the ref to lookup. May be a short-hand form, e.g.
+ * "master" which is is automatically expanded to
+ * "refs/heads/master" if "refs/heads/master" already exists.
+ * @return the Ref with the given name, or {@code null} if it does not exist
+ * @throws IOException
+ * @since 4.2
+ */
+ @Nullable
+ public Ref findRef(String name) throws IOException {
return getRefDatabase().getRef(name);
}
/**
* @return mutable map of all known refs (heads, tags, remotes).
*/
+ @NonNull
public Map<String, Ref> getAllRefs() {
try {
return getRefDatabase().getRefs(RefDatabase.ALL);
@@ -927,6 +1026,7 @@ public abstract class Repository {
* of the entry contains the ref with the full tag name
* ("refs/tags/v1.0").
*/
+ @NonNull
public Map<String, Ref> getTags() {
try {
return getRefDatabase().getRefs(Constants.R_TAGS);
@@ -948,6 +1048,7 @@ public abstract class Repository {
* will be true and getPeeledObjectId will contain the peeled object
* (or null).
*/
+ @NonNull
public Ref peel(final Ref ref) {
try {
return getRefDatabase().peel(ref);
@@ -962,6 +1063,7 @@ public abstract class Repository {
/**
* @return a map with all objects referenced by a peeled ref.
*/
+ @NonNull
public Map<AnyObjectId, Set<Ref>> getAllRefsByPeeledObjectId() {
Map<String, Ref> allRefs = getAllRefs();
Map<AnyObjectId, Set<Ref>> ret = new HashMap<AnyObjectId, Set<Ref>>(allRefs.size());
@@ -986,11 +1088,13 @@ public abstract class Repository {
}
/**
- * @return the index file location
+ * @return the index file location or {@code null} if repository isn't
+ * local.
* @throws NoWorkTreeException
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @NonNull
public File getIndexFile() throws NoWorkTreeException {
if (isBare())
throw new NoWorkTreeException();
@@ -1015,6 +1119,7 @@ public abstract class Repository {
* the index file is using a format or extension that this
* library does not support.
*/
+ @NonNull
public DirCache readDirCache() throws NoWorkTreeException,
CorruptObjectException, IOException {
return DirCache.read(this);
@@ -1039,6 +1144,7 @@ public abstract class Repository {
* the index file is using a format or extension that this
* library does not support.
*/
+ @NonNull
public DirCache lockDirCache() throws NoWorkTreeException,
CorruptObjectException, IOException {
// we want DirCache to inform us so that we can inform registered
@@ -1064,6 +1170,7 @@ public abstract class Repository {
/**
* @return an important state
*/
+ @NonNull
public RepositoryState getRepositoryState() {
if (isBare() || getDirectory() == null)
return RepositoryState.BARE;
@@ -1206,6 +1313,7 @@ public abstract class Repository {
* @return normalized repository relative path or the empty
* string if the file is not relative to the work directory.
*/
+ @NonNull
public static String stripWorkDir(File workDir, File file) {
final String filePath = file.getPath();
final String workDirPath = workDir.getPath();
@@ -1240,6 +1348,7 @@ public abstract class Repository {
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @NonNull
public File getWorkTree() throws NoWorkTreeException {
if (isBare())
throw new NoWorkTreeException();
@@ -1263,6 +1372,7 @@ public abstract class Repository {
*
* @return a more user friendly ref name
*/
+ @NonNull
public static String shortenRefName(String refName) {
if (refName.startsWith(Constants.R_HEADS))
return refName.substring(Constants.R_HEADS.length());
@@ -1278,9 +1388,10 @@ public abstract class Repository {
* @return the remote branch name part of <code>refName</code>, i.e. without
* the <code>refs/remotes/<remote></code> prefix, if
* <code>refName</code> represents a remote tracking branch;
- * otherwise null.
+ * otherwise {@code null}.
* @since 3.4
*/
+ @Nullable
public String shortenRemoteBranchName(String refName) {
for (String remote : getRemoteNames()) {
String remotePrefix = Constants.R_REMOTES + remote + "/"; //$NON-NLS-1$
@@ -1295,9 +1406,10 @@ public abstract class Repository {
* @return the remote name part of <code>refName</code>, i.e. without the
* <code>refs/remotes/<remote></code> prefix, if
* <code>refName</code> represents a remote tracking branch;
- * otherwise null.
+ * otherwise {@code null}.
* @since 3.4
*/
+ @Nullable
public String getRemoteName(String refName) {
for (String remote : getRemoteNames()) {
String remotePrefix = Constants.R_REMOTES + remote + "/"; //$NON-NLS-1$
@@ -1309,12 +1421,13 @@ public abstract class Repository {
/**
* @param refName
- * @return a {@link ReflogReader} for the supplied refname, or null if the
- * named ref does not exist.
+ * @return a {@link ReflogReader} for the supplied refname, or {@code null}
+ * if the named ref does not exist.
* @throws IOException
* the ref could not be accessed.
* @since 3.0
*/
+ @Nullable
public abstract ReflogReader getReflogReader(String refName)
throws IOException;
@@ -1330,6 +1443,7 @@ public abstract class Repository {
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @Nullable
public String readMergeCommitMsg() throws IOException, NoWorkTreeException {
return readCommitMsgFile(Constants.MERGE_MSG);
}
@@ -1352,6 +1466,41 @@ public abstract class Repository {
}
/**
+ * Return the information stored in the file $GIT_DIR/COMMIT_EDITMSG. In
+ * this file hooks triggered by an operation may read or modify the current
+ * commit message.
+ *
+ * @return a String containing the content of the COMMIT_EDITMSG file or
+ * {@code null} if this file doesn't exist
+ * @throws IOException
+ * @throws NoWorkTreeException
+ * if this is bare, which implies it has no working directory.
+ * See {@link #isBare()}.
+ * @since 4.0
+ */
+ @Nullable
+ public String readCommitEditMsg() throws IOException, NoWorkTreeException {
+ return readCommitMsgFile(Constants.COMMIT_EDITMSG);
+ }
+
+ /**
+ * Write new content to the file $GIT_DIR/COMMIT_EDITMSG. In this file hooks
+ * triggered by an operation may read or modify the current commit message.
+ * If {@code null} is specified as message the file will be deleted.
+ *
+ * @param msg
+ * the message which should be written or {@code null} to delete
+ * the file
+ *
+ * @throws IOException
+ * @since 4.0
+ */
+ public void writeCommitEditMsg(String msg) throws IOException {
+ File commiEditMsgFile = new File(gitDir, Constants.COMMIT_EDITMSG);
+ writeCommitMsg(commiEditMsgFile, msg);
+ }
+
+ /**
* Return the information stored in the file $GIT_DIR/MERGE_HEAD. In this
* file operations triggering a merge will store the IDs of all heads which
* should be merged together with HEAD.
@@ -1364,6 +1513,7 @@ public abstract class Repository {
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @Nullable
public List<ObjectId> readMergeHeads() throws IOException, NoWorkTreeException {
if (isBare() || getDirectory() == null)
throw new NoWorkTreeException();
@@ -1407,6 +1557,7 @@ public abstract class Repository {
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @Nullable
public ObjectId readCherryPickHead() throws IOException,
NoWorkTreeException {
if (isBare() || getDirectory() == null)
@@ -1430,6 +1581,7 @@ public abstract class Repository {
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @Nullable
public ObjectId readRevertHead() throws IOException, NoWorkTreeException {
if (isBare() || getDirectory() == null)
throw new NoWorkTreeException();
@@ -1495,6 +1647,7 @@ public abstract class Repository {
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @Nullable
public ObjectId readOrigHead() throws IOException, NoWorkTreeException {
if (isBare() || getDirectory() == null)
throw new NoWorkTreeException();
@@ -1515,6 +1668,7 @@ public abstract class Repository {
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @Nullable
public String readSquashCommitMsg() throws IOException {
return readCommitMsgFile(Constants.SQUASH_MSG);
}
@@ -1536,6 +1690,7 @@ public abstract class Repository {
writeCommitMsg(squashMsgFile, msg);
}
+ @Nullable
private String readCommitMsgFile(String msgFilename) throws IOException {
if (isBare() || getDirectory() == null)
throw new NoWorkTreeException();
@@ -1544,6 +1699,9 @@ public abstract class Repository {
try {
return RawParseUtils.decode(IO.readFully(mergeMsgFile));
} catch (FileNotFoundException e) {
+ if (mergeMsgFile.exists()) {
+ throw e;
+ }
// the file has disappeared in the meantime ignore it
return null;
}
@@ -1566,15 +1724,20 @@ public abstract class Repository {
* Read a file from the git directory.
*
* @param filename
- * @return the raw contents or null if the file doesn't exist or is empty
+ * @return the raw contents or {@code null} if the file doesn't exist or is
+ * empty
* @throws IOException
*/
+ @Nullable
private byte[] readGitDirectoryFile(String filename) throws IOException {
File file = new File(getDirectory(), filename);
try {
byte[] raw = IO.readFully(file);
return raw.length > 0 ? raw : null;
} catch (FileNotFoundException notFound) {
+ if (file.exists()) {
+ throw notFound;
+ }
return null;
}
}
@@ -1622,6 +1785,7 @@ public abstract class Repository {
* @throws IOException
* @since 3.2
*/
+ @NonNull
public List<RebaseTodoLine> readRebaseTodo(String path,
boolean includeComments)
throws IOException {
@@ -1651,6 +1815,7 @@ public abstract class Repository {
* @return the names of all known remotes
* @since 3.4
*/
+ @NonNull
public Set<String> getRemoteNames() {
return getConfig()
.getSubsections(ConfigConstants.CONFIG_REMOTE_SECTION);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
index c7d957c..23cc264 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
@@ -47,6 +47,8 @@ import java.io.File;
import java.io.IOException;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -143,6 +145,28 @@ public class RepositoryCache {
}
}
+ /**
+ * Remove a repository from the cache.
+ * <p>
+ * Removes a repository from the cache, if it is still registered here,
+ * permitting it to close.
+ *
+ * @param location
+ * location of the repository to remove.
+ * @since 4.1
+ */
+ public static void unregister(Key location) {
+ cache.unregisterRepository(location);
+ }
+
+ /**
+ * @return the locations of all repositories registered in the cache.
+ * @since 4.1
+ */
+ public static Collection<Key> getRegisteredKeys() {
+ return cache.getKeys();
+ }
+
/** Unregister all repositories from the cache. */
public static void clear() {
cache.clearAll();
@@ -159,6 +183,7 @@ public class RepositoryCache {
openLocks[i] = new Lock();
}
+ @SuppressWarnings("resource")
private Repository openRepository(final Key location,
final boolean mustExist) throws IOException {
Reference<Repository> ref = cacheMap.get(location);
@@ -194,6 +219,10 @@ public class RepositoryCache {
oldDb.close();
}
+ private Collection<Key> getKeys() {
+ return new ArrayList<Key>(cacheMap.keySet());
+ }
+
private void clearAll() {
for (int stage = 0; stage < 2; stage++) {
for (Iterator<Map.Entry<Key, Reference<Repository>>> i = cacheMap
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryState.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryState.java
index 4fac2eb..9b7234b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryState.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryState.java
@@ -73,7 +73,9 @@ public enum RepositoryState {
public boolean isRebasing() { return false; }
@Override
- public String getDescription() { return "Bare"; }
+ public String getDescription() {
+ return JGitText.get().repositoryState_bare;
+ }
},
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java
index 43b1510..eeab921 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java
@@ -43,6 +43,9 @@
package org.eclipse.jgit.lib;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+
/**
* A reference that indirectly points at another {@link Ref}.
* <p>
@@ -62,11 +65,12 @@ public class SymbolicRef implements Ref {
* @param target
* the ref we reference and derive our value from.
*/
- public SymbolicRef(String refName, Ref target) {
+ public SymbolicRef(@NonNull String refName, @NonNull Ref target) {
this.name = refName;
this.target = target;
}
+ @NonNull
public String getName() {
return name;
}
@@ -75,6 +79,7 @@ public class SymbolicRef implements Ref {
return true;
}
+ @NonNull
public Ref getLeaf() {
Ref dst = getTarget();
while (dst.isSymbolic())
@@ -82,18 +87,22 @@ public class SymbolicRef implements Ref {
return dst;
}
+ @NonNull
public Ref getTarget() {
return target;
}
+ @Nullable
public ObjectId getObjectId() {
return getLeaf().getObjectId();
}
+ @NonNull
public Storage getStorage() {
return Storage.LOOSE;
}
+ @Nullable
public ObjectId getPeeledObjectId() {
return getLeaf().getPeeledObjectId();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymlinkTreeEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymlinkTreeEntry.java
deleted file mode 100644
index c7e41bc..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymlinkTreeEntry.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg at dewire.com>
- * Copyright (C) 2006-2007, Shawn O. Pearce <spearce at spearce.org>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-/**
- * A tree entry representing a symbolic link.
- *
- * Note. Java cannot really handle these as file system objects.
- *
- * @deprecated To look up information about a single path, use
- * {@link org.eclipse.jgit.treewalk.TreeWalk#forPath(Repository, String, org.eclipse.jgit.revwalk.RevTree)}.
- * To lookup information about multiple paths at once, use a
- * {@link org.eclipse.jgit.treewalk.TreeWalk} and obtain the current entry's
- * information from its getter methods.
- */
- at Deprecated
-public class SymlinkTreeEntry extends TreeEntry {
-
- /**
- * Construct a {@link SymlinkTreeEntry} with the specified name and SHA-1 in
- * the specified parent
- *
- * @param parent
- * @param id
- * @param nameUTF8
- */
- public SymlinkTreeEntry(final Tree parent, final ObjectId id,
- final byte[] nameUTF8) {
- super(parent, id, nameUTF8);
- }
-
- public FileMode getMode() {
- return FileMode.SYMLINK;
- }
-
- public String toString() {
- final StringBuilder r = new StringBuilder();
- r.append(ObjectId.toString(getId()));
- r.append(" S "); //$NON-NLS-1$
- r.append(getFullName());
- return r.toString();
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tree.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tree.java
deleted file mode 100644
index 43bd489..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tree.java
+++ /dev/null
@@ -1,601 +0,0 @@
-/*
- * Copyright (C) 2007, Robin Rosenberg <me at lathund.dewire.com>
- * Copyright (C) 2007-2008, Robin Rosenberg <robin.rosenberg at dewire.com>
- * Copyright (C) 2006-2008, Shawn O. Pearce <spearce at spearce.org>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.IOException;
-import java.text.MessageFormat;
-
-import org.eclipse.jgit.errors.CorruptObjectException;
-import org.eclipse.jgit.errors.EntryExistsException;
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.errors.ObjectWritingException;
-import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.util.RawParseUtils;
-
-/**
- * A representation of a Git tree entry. A Tree is a directory in Git.
- *
- * @deprecated To look up information about a single path, use
- * {@link org.eclipse.jgit.treewalk.TreeWalk#forPath(Repository, String, org.eclipse.jgit.revwalk.RevTree)}.
- * To lookup information about multiple paths at once, use a
- * {@link org.eclipse.jgit.treewalk.TreeWalk} and obtain the current entry's
- * information from its getter methods.
- */
- at Deprecated
-public class Tree extends TreeEntry {
- private static final TreeEntry[] EMPTY_TREE = {};
-
- /**
- * Compare two names represented as bytes. Since git treats names of trees and
- * blobs differently we have one parameter that represents a '/' for trees. For
- * other objects the value should be NUL. The names are compare by their positive
- * byte value (0..255).
- *
- * A blob and a tree with the same name will not compare equal.
- *
- * @param a name
- * @param b name
- * @param lasta '/' if a is a tree, else NUL
- * @param lastb '/' if b is a tree, else NUL
- *
- * @return < 0 if a is sorted before b, 0 if they are the same, else b
- */
- public static final int compareNames(final byte[] a, final byte[] b, final int lasta,final int lastb) {
- return compareNames(a, b, 0, b.length, lasta, lastb);
- }
-
- private static final int compareNames(final byte[] a, final byte[] nameUTF8,
- final int nameStart, final int nameEnd, final int lasta, int lastb) {
- int j,k;
- for (j = 0, k = nameStart; j < a.length && k < nameEnd; j++, k++) {
- final int aj = a[j] & 0xff;
- final int bk = nameUTF8[k] & 0xff;
- if (aj < bk)
- return -1;
- else if (aj > bk)
- return 1;
- }
- if (j < a.length) {
- int aj = a[j]&0xff;
- if (aj < lastb)
- return -1;
- else if (aj > lastb)
- return 1;
- else
- if (j == a.length - 1)
- return 0;
- else
- return -1;
- }
- if (k < nameEnd) {
- int bk = nameUTF8[k] & 0xff;
- if (lasta < bk)
- return -1;
- else if (lasta > bk)
- return 1;
- else
- if (k == nameEnd - 1)
- return 0;
- else
- return 1;
- }
- if (lasta < lastb)
- return -1;
- else if (lasta > lastb)
- return 1;
-
- final int namelength = nameEnd - nameStart;
- if (a.length == namelength)
- return 0;
- else if (a.length < namelength)
- return -1;
- else
- return 1;
- }
-
- private static final byte[] substring(final byte[] s, final int nameStart,
- final int nameEnd) {
- if (nameStart == 0 && nameStart == s.length)
- return s;
- final byte[] n = new byte[nameEnd - nameStart];
- System.arraycopy(s, nameStart, n, 0, n.length);
- return n;
- }
-
- private static final int binarySearch(final TreeEntry[] entries,
- final byte[] nameUTF8, final int nameUTF8last, final int nameStart, final int nameEnd) {
- if (entries.length == 0)
- return -1;
- int high = entries.length;
- int low = 0;
- do {
- final int mid = (low + high) >>> 1;
- final int cmp = compareNames(entries[mid].getNameUTF8(), nameUTF8,
- nameStart, nameEnd, TreeEntry.lastChar(entries[mid]), nameUTF8last);
- if (cmp < 0)
- low = mid + 1;
- else if (cmp == 0)
- return mid;
- else
- high = mid;
- } while (low < high);
- return -(low + 1);
- }
-
- private final Repository db;
-
- private TreeEntry[] contents;
-
- /**
- * Constructor for a new Tree
- *
- * @param repo The repository that owns the Tree.
- */
- public Tree(final Repository repo) {
- super(null, null, null);
- db = repo;
- contents = EMPTY_TREE;
- }
-
- /**
- * Construct a Tree object with known content and hash value
- *
- * @param repo
- * @param myId
- * @param raw
- * @throws IOException
- */
- public Tree(final Repository repo, final ObjectId myId, final byte[] raw)
- throws IOException {
- super(null, myId, null);
- db = repo;
- readTree(raw);
- }
-
- /**
- * Construct a new Tree under another Tree
- *
- * @param parent
- * @param nameUTF8
- */
- public Tree(final Tree parent, final byte[] nameUTF8) {
- super(parent, null, nameUTF8);
- db = parent.getRepository();
- contents = EMPTY_TREE;
- }
-
- /**
- * Construct a Tree with a known SHA-1 under another tree. Data is not yet
- * specified and will have to be loaded on demand.
- *
- * @param parent
- * @param id
- * @param nameUTF8
- */
- public Tree(final Tree parent, final ObjectId id, final byte[] nameUTF8) {
- super(parent, id, nameUTF8);
- db = parent.getRepository();
- }
-
- public FileMode getMode() {
- return FileMode.TREE;
- }
-
- /**
- * @return true if this Tree is the top level Tree.
- */
- public boolean isRoot() {
- return getParent() == null;
- }
-
- public Repository getRepository() {
- return db;
- }
-
- /**
- * @return true of the data of this Tree is loaded
- */
- public boolean isLoaded() {
- return contents != null;
- }
-
- /**
- * Forget the in-memory data for this tree.
- */
- public void unload() {
- if (isModified())
- throw new IllegalStateException(JGitText.get().cannotUnloadAModifiedTree);
- contents = null;
- }
-
- /**
- * Adds a new or existing file with the specified name to this tree.
- * Trees are added if necessary as the name may contain '/':s.
- *
- * @param name Name
- * @return a {@link FileTreeEntry} for the added file.
- * @throws IOException
- */
- public FileTreeEntry addFile(final String name) throws IOException {
- return addFile(Repository.gitInternalSlash(Constants.encode(name)), 0);
- }
-
- /**
- * Adds a new or existing file with the specified name to this tree.
- * Trees are added if necessary as the name may contain '/':s.
- *
- * @param s an array containing the name
- * @param offset when the name starts in the tree.
- *
- * @return a {@link FileTreeEntry} for the added file.
- * @throws IOException
- */
- public FileTreeEntry addFile(final byte[] s, final int offset)
- throws IOException {
- int slash;
- int p;
-
- for (slash = offset; slash < s.length && s[slash] != '/'; slash++) {
- // search for path component terminator
- }
-
- ensureLoaded();
- byte xlast = slash<s.length ? (byte)'/' : 0;
- p = binarySearch(contents, s, xlast, offset, slash);
- if (p >= 0 && slash < s.length && contents[p] instanceof Tree)
- return ((Tree) contents[p]).addFile(s, slash + 1);
-
- final byte[] newName = substring(s, offset, slash);
- if (p >= 0)
- throw new EntryExistsException(RawParseUtils.decode(newName));
- else if (slash < s.length) {
- final Tree t = new Tree(this, newName);
- insertEntry(p, t);
- return t.addFile(s, slash + 1);
- } else {
- final FileTreeEntry f = new FileTreeEntry(this, null, newName,
- false);
- insertEntry(p, f);
- return f;
- }
- }
-
- /**
- * Adds a new or existing Tree with the specified name to this tree.
- * Trees are added if necessary as the name may contain '/':s.
- *
- * @param name Name
- * @return a {@link FileTreeEntry} for the added tree.
- * @throws IOException
- */
- public Tree addTree(final String name) throws IOException {
- return addTree(Repository.gitInternalSlash(Constants.encode(name)), 0);
- }
-
- /**
- * Adds a new or existing Tree with the specified name to this tree.
- * Trees are added if necessary as the name may contain '/':s.
- *
- * @param s an array containing the name
- * @param offset when the name starts in the tree.
- *
- * @return a {@link FileTreeEntry} for the added tree.
- * @throws IOException
- */
- public Tree addTree(final byte[] s, final int offset) throws IOException {
- int slash;
- int p;
-
- for (slash = offset; slash < s.length && s[slash] != '/'; slash++) {
- // search for path component terminator
- }
-
- ensureLoaded();
- p = binarySearch(contents, s, (byte)'/', offset, slash);
- if (p >= 0 && slash < s.length && contents[p] instanceof Tree)
- return ((Tree) contents[p]).addTree(s, slash + 1);
-
- final byte[] newName = substring(s, offset, slash);
- if (p >= 0)
- throw new EntryExistsException(RawParseUtils.decode(newName));
-
- final Tree t = new Tree(this, newName);
- insertEntry(p, t);
- return slash == s.length ? t : t.addTree(s, slash + 1);
- }
-
- /**
- * Add the specified tree entry to this tree.
- *
- * @param e
- * @throws IOException
- */
- public void addEntry(final TreeEntry e) throws IOException {
- final int p;
-
- ensureLoaded();
- p = binarySearch(contents, e.getNameUTF8(), TreeEntry.lastChar(e), 0, e.getNameUTF8().length);
- if (p < 0) {
- e.attachParent(this);
- insertEntry(p, e);
- } else {
- throw new EntryExistsException(e.getName());
- }
- }
-
- private void insertEntry(int p, final TreeEntry e) {
- final TreeEntry[] c = contents;
- final TreeEntry[] n = new TreeEntry[c.length + 1];
- p = -(p + 1);
- for (int k = c.length - 1; k >= p; k--)
- n[k + 1] = c[k];
- n[p] = e;
- for (int k = p - 1; k >= 0; k--)
- n[k] = c[k];
- contents = n;
- setModified();
- }
-
- void removeEntry(final TreeEntry e) {
- final TreeEntry[] c = contents;
- final int p = binarySearch(c, e.getNameUTF8(), TreeEntry.lastChar(e), 0,
- e.getNameUTF8().length);
- if (p >= 0) {
- final TreeEntry[] n = new TreeEntry[c.length - 1];
- for (int k = c.length - 1; k > p; k--)
- n[k - 1] = c[k];
- for (int k = p - 1; k >= 0; k--)
- n[k] = c[k];
- contents = n;
- setModified();
- }
- }
-
- /**
- * @return number of members in this tree
- * @throws IOException
- */
- public int memberCount() throws IOException {
- ensureLoaded();
- return contents.length;
- }
-
- /**
- * Return all members of the tree sorted in Git order.
- *
- * Entries are sorted by the numerical unsigned byte
- * values with (sub)trees having an implicit '/'. An
- * example of a tree with three entries. a:b is an
- * actual file name here.
- *
- * <p>
- * 100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 a.b
- * 040000 tree 4277b6e69d25e5efa77c455340557b384a4c018a a
- * 100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 a:b
- *
- * @return all entries in this Tree, sorted.
- * @throws IOException
- */
- public TreeEntry[] members() throws IOException {
- ensureLoaded();
- final TreeEntry[] c = contents;
- if (c.length != 0) {
- final TreeEntry[] r = new TreeEntry[c.length];
- for (int k = c.length - 1; k >= 0; k--)
- r[k] = c[k];
- return r;
- } else
- return c;
- }
-
- private boolean exists(final String s, byte slast) throws IOException {
- return findMember(s, slast) != null;
- }
-
- /**
- * @param path to the tree.
- * @return true if a tree with the specified path can be found under this
- * tree.
- * @throws IOException
- */
- public boolean existsTree(String path) throws IOException {
- return exists(path,(byte)'/');
- }
-
- /**
- * @param path of the non-tree entry.
- * @return true if a blob, symlink, or gitlink with the specified name
- * can be found under this tree.
- * @throws IOException
- */
- public boolean existsBlob(String path) throws IOException {
- return exists(path,(byte)0);
- }
-
- private TreeEntry findMember(final String s, byte slast) throws IOException {
- return findMember(Repository.gitInternalSlash(Constants.encode(s)), slast, 0);
- }
-
- private TreeEntry findMember(final byte[] s, final byte slast, final int offset)
- throws IOException {
- int slash;
- int p;
-
- for (slash = offset; slash < s.length && s[slash] != '/'; slash++) {
- // search for path component terminator
- }
-
- ensureLoaded();
- byte xlast = slash<s.length ? (byte)'/' : slast;
- p = binarySearch(contents, s, xlast, offset, slash);
- if (p >= 0) {
- final TreeEntry r = contents[p];
- if (slash < s.length-1)
- return r instanceof Tree ? ((Tree) r).findMember(s, slast, slash + 1)
- : null;
- return r;
- }
- return null;
- }
-
- /**
- * @param s
- * blob name
- * @return a {@link TreeEntry} representing an object with the specified
- * relative path.
- * @throws IOException
- */
- public TreeEntry findBlobMember(String s) throws IOException {
- return findMember(s,(byte)0);
- }
-
- /**
- * @param s Tree Name
- * @return a Tree with the name s or null
- * @throws IOException
- */
- public TreeEntry findTreeMember(String s) throws IOException {
- return findMember(s,(byte)'/');
- }
-
- private void ensureLoaded() throws IOException, MissingObjectException {
- if (!isLoaded()) {
- ObjectLoader ldr = db.open(getId(), Constants.OBJ_TREE);
- readTree(ldr.getCachedBytes());
- }
- }
-
- private void readTree(final byte[] raw) throws IOException {
- final int rawSize = raw.length;
- int rawPtr = 0;
- TreeEntry[] temp;
- int nextIndex = 0;
-
- while (rawPtr < rawSize) {
- while (rawPtr < rawSize && raw[rawPtr] != 0)
- rawPtr++;
- rawPtr++;
- rawPtr += Constants.OBJECT_ID_LENGTH;
- nextIndex++;
- }
-
- temp = new TreeEntry[nextIndex];
- rawPtr = 0;
- nextIndex = 0;
- while (rawPtr < rawSize) {
- int c = raw[rawPtr++];
- if (c < '0' || c > '7')
- throw new CorruptObjectException(getId(), JGitText.get().corruptObjectInvalidEntryMode);
- int mode = c - '0';
- for (;;) {
- c = raw[rawPtr++];
- if (' ' == c)
- break;
- else if (c < '0' || c > '7')
- throw new CorruptObjectException(getId(), JGitText.get().corruptObjectInvalidMode);
- mode <<= 3;
- mode += c - '0';
- }
-
- int nameLen = 0;
- while (raw[rawPtr + nameLen] != 0)
- nameLen++;
- final byte[] name = new byte[nameLen];
- System.arraycopy(raw, rawPtr, name, 0, nameLen);
- rawPtr += nameLen + 1;
-
- final ObjectId id = ObjectId.fromRaw(raw, rawPtr);
- rawPtr += Constants.OBJECT_ID_LENGTH;
-
- final TreeEntry ent;
- if (FileMode.REGULAR_FILE.equals(mode))
- ent = new FileTreeEntry(this, id, name, false);
- else if (FileMode.EXECUTABLE_FILE.equals(mode))
- ent = new FileTreeEntry(this, id, name, true);
- else if (FileMode.TREE.equals(mode))
- ent = new Tree(this, id, name);
- else if (FileMode.SYMLINK.equals(mode))
- ent = new SymlinkTreeEntry(this, id, name);
- else if (FileMode.GITLINK.equals(mode))
- ent = new GitlinkTreeEntry(this, id, name);
- else
- throw new CorruptObjectException(getId(), MessageFormat.format(
- JGitText.get().corruptObjectInvalidMode2, Integer.toOctalString(mode)));
- temp[nextIndex++] = ent;
- }
-
- contents = temp;
- }
-
- /**
- * Format this Tree in canonical format.
- *
- * @return canonical encoding of the tree object.
- * @throws IOException
- * the tree cannot be loaded, or its not in a writable state.
- */
- public byte[] format() throws IOException {
- TreeFormatter fmt = new TreeFormatter();
- for (TreeEntry e : members()) {
- ObjectId id = e.getId();
- if (id == null)
- throw new ObjectWritingException(MessageFormat.format(JGitText
- .get().objectAtPathDoesNotHaveId, e.getFullName()));
-
- fmt.append(e.getNameUTF8(), e.getMode(), id);
- }
- return fmt.toByteArray();
- }
-
- public String toString() {
- final StringBuilder r = new StringBuilder();
- r.append(ObjectId.toString(getId()));
- r.append(" T "); //$NON-NLS-1$
- r.append(getFullName());
- return r.toString();
- }
-
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TreeEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TreeEntry.java
deleted file mode 100644
index a1ffa68..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TreeEntry.java
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- * Copyright (C) 2007-2008, Robin Rosenberg <robin.rosenberg at dewire.com>
- * Copyright (C) 2006-2007, Shawn O. Pearce <spearce at spearce.org>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.lib;
-
-import java.io.IOException;
-
-import org.eclipse.jgit.util.RawParseUtils;
-
-/**
- * This class represents an entry in a tree, like a blob or another tree.
- *
- * @deprecated To look up information about a single path, use
- * {@link org.eclipse.jgit.treewalk.TreeWalk#forPath(Repository, String, org.eclipse.jgit.revwalk.RevTree)}.
- * To lookup information about multiple paths at once, use a
- * {@link org.eclipse.jgit.treewalk.TreeWalk} and obtain the current entry's
- * information from its getter methods.
- */
- at Deprecated
-public abstract class TreeEntry implements Comparable {
- private byte[] nameUTF8;
-
- private Tree parent;
-
- private ObjectId id;
-
- /**
- * Construct a named tree entry.
- *
- * @param myParent
- * @param myId
- * @param myNameUTF8
- */
- protected TreeEntry(final Tree myParent, final ObjectId myId,
- final byte[] myNameUTF8) {
- nameUTF8 = myNameUTF8;
- parent = myParent;
- id = myId;
- }
-
- /**
- * @return parent of this tree.
- */
- public Tree getParent() {
- return parent;
- }
-
- /**
- * Delete this entry.
- */
- public void delete() {
- getParent().removeEntry(this);
- detachParent();
- }
-
- /**
- * Detach this entry from it's parent.
- */
- public void detachParent() {
- parent = null;
- }
-
- void attachParent(final Tree p) {
- parent = p;
- }
-
- /**
- * @return the repository owning this entry.
- */
- public Repository getRepository() {
- return getParent().getRepository();
- }
-
- /**
- * @return the raw byte name of this entry.
- */
- public byte[] getNameUTF8() {
- return nameUTF8;
- }
-
- /**
- * @return the name of this entry.
- */
- public String getName() {
- if (nameUTF8 != null)
- return RawParseUtils.decode(nameUTF8);
- return null;
- }
-
- /**
- * Rename this entry.
- *
- * @param n The new name
- * @throws IOException
- */
- public void rename(final String n) throws IOException {
- rename(Constants.encode(n));
- }
-
- /**
- * Rename this entry.
- *
- * @param n The new name
- * @throws IOException
- */
- public void rename(final byte[] n) throws IOException {
- final Tree t = getParent();
- if (t != null) {
- delete();
- }
- nameUTF8 = n;
- if (t != null) {
- t.addEntry(this);
- }
- }
-
- /**
- * @return true if this entry is new or modified since being loaded.
- */
- public boolean isModified() {
- return getId() == null;
- }
-
- /**
- * Mark this entry as modified.
- */
- public void setModified() {
- setId(null);
- }
-
- /**
- * @return SHA-1 of this tree entry (null for new unhashed entries)
- */
- public ObjectId getId() {
- return id;
- }
-
- /**
- * Set (update) the SHA-1 of this entry. Invalidates the id's of all
- * entries above this entry as they will have to be recomputed.
- *
- * @param n SHA-1 for this entry.
- */
- public void setId(final ObjectId n) {
- // If we have a parent and our id is being cleared or changed then force
- // the parent's id to become unset as it depends on our id.
- //
- final Tree p = getParent();
- if (p != null && id != n) {
- if ((id == null && n != null) || (id != null && n == null)
- || !id.equals(n)) {
- p.setId(null);
- }
- }
-
- id = n;
- }
-
- /**
- * @return repository relative name of this entry
- */
- public String getFullName() {
- final StringBuilder r = new StringBuilder();
- appendFullName(r);
- return r.toString();
- }
-
- /**
- * @return repository relative name of the entry
- * FIXME better encoding
- */
- public byte[] getFullNameUTF8() {
- return getFullName().getBytes();
- }
-
- public int compareTo(final Object o) {
- if (this == o)
- return 0;
- if (o instanceof TreeEntry)
- return Tree.compareNames(nameUTF8, ((TreeEntry) o).nameUTF8, lastChar(this), lastChar((TreeEntry)o));
- return -1;
- }
-
- /**
- * Helper for accessing tree/blob methods.
- *
- * @param treeEntry
- * @return '/' for Tree entries and NUL for non-treeish objects.
- */
- final public static int lastChar(TreeEntry treeEntry) {
- if (!(treeEntry instanceof Tree))
- return '\0';
- else
- return '/';
- }
-
- /**
- * @return mode (type of object)
- */
- public abstract FileMode getMode();
-
- private void appendFullName(final StringBuilder r) {
- final TreeEntry p = getParent();
- final String n = getName();
- if (p != null) {
- p.appendFullName(r);
- if (r.length() > 0) {
- r.append('/');
- }
- }
- if (n != null) {
- r.append(n);
- }
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/LockFailedException.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/EolAwareOutputStream.java
similarity index 64%
copy from org.eclipse.jgit/src/org/eclipse/jgit/errors/LockFailedException.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/merge/EolAwareOutputStream.java
index 18aa9d9..1ddac18 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/LockFailedException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/EolAwareOutputStream.java
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2011, GitHub Inc.
- * and other copyright owners as documented in the project's IP log.
+ * Copyright (C) 2014, André de Oliveira <andre.oliveira at liferay.com>
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
@@ -40,51 +39,57 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.errors;
+package org.eclipse.jgit.merge;
-import java.io.File;
import java.io.IOException;
-import java.text.MessageFormat;
-
-import org.eclipse.jgit.internal.JGitText;
+import java.io.OutputStream;
/**
- * An exception occurring when a file cannot be locked
+ * An output stream which is aware of newlines and can be asked to begin a new
+ * line if not already in one.
*/
-public class LockFailedException extends IOException {
- private static final long serialVersionUID = 1L;
+class EolAwareOutputStream extends OutputStream {
+ private final OutputStream out;
- private File file;
+ private boolean bol = true;
/**
- * Construct a CannotLockException for the given file and message
+ * Initialize a new EOL aware stream.
*
- * @param file
- * file that could not be locked
- * @param message
- * exception message
+ * @param out
+ * stream to output all writes to.
*/
- public LockFailedException(File file, String message) {
- super(message);
- this.file = file;
+ EolAwareOutputStream(OutputStream out) {
+ this.out = out;
}
/**
- * Construct a CannotLockException for the given file
+ * Begin a new line if not already in one.
*
- * @param file
- * file that could not be locked
+ * @exception IOException
+ * if an I/O error occurs.
*/
- public LockFailedException(File file) {
- this(file, MessageFormat.format(JGitText.get().cannotLock, file));
+ void beginln() throws IOException {
+ if (!bol)
+ write('\n');
}
- /**
- * Get the file that could not be locked
- *
- * @return file
- */
- public File getFile() {
- return file;
+ /** @return true if a new line has just begun. */
+ boolean isBeginln() {
+ return bol;
+ }
+
+ @Override
+ public void write(int val) throws IOException {
+ out.write(val);
+ bol = (val == '\n');
+ }
+
+ @Override
+ public void write(byte[] buf, int pos, int cnt) throws IOException {
+ if (cnt > 0) {
+ out.write(buf, pos, cnt);
+ bol = (buf[pos + (cnt - 1)] == '\n');
+ }
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java
index eed1dcf..977f953 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java
@@ -49,7 +49,6 @@ import java.util.ArrayList;
import java.util.List;
import org.eclipse.jgit.diff.RawText;
-import org.eclipse.jgit.merge.MergeChunk.ConflictState;
/**
* A class to convert merge results into a Git conformant textual presentation
@@ -78,47 +77,7 @@ public class MergeFormatter {
*/
public void formatMerge(OutputStream out, MergeResult<RawText> res,
List<String> seqName, String charsetName) throws IOException {
- String lastConflictingName = null; // is set to non-null whenever we are
- // in a conflict
- boolean threeWayMerge = (res.getSequences().size() == 3);
- for (MergeChunk chunk : res) {
- RawText seq = res.getSequences().get(chunk.getSequenceIndex());
- if (lastConflictingName != null
- && chunk.getConflictState() != ConflictState.NEXT_CONFLICTING_RANGE) {
- // found the end of an conflict
- out.write((">>>>>>> " + lastConflictingName + "\n").getBytes(charsetName)); //$NON-NLS-1$ //$NON-NLS-2$
- lastConflictingName = null;
- }
- if (chunk.getConflictState() == ConflictState.FIRST_CONFLICTING_RANGE) {
- // found the start of an conflict
- out.write(("<<<<<<< " + seqName.get(chunk.getSequenceIndex()) + //$NON-NLS-1$
- "\n").getBytes(charsetName)); //$NON-NLS-1$
- lastConflictingName = seqName.get(chunk.getSequenceIndex());
- } else if (chunk.getConflictState() == ConflictState.NEXT_CONFLICTING_RANGE) {
- // found another conflicting chunk
-
- /*
- * In case of a non-three-way merge I'll add the name of the
- * conflicting chunk behind the equal signs. I also append the
- * name of the last conflicting chunk after the ending
- * greater-than signs. If somebody knows a better notation to
- * present non-three-way merges - feel free to correct here.
- */
- lastConflictingName = seqName.get(chunk.getSequenceIndex());
- out.write((threeWayMerge ? "=======\n" : "======= " //$NON-NLS-1$ //$NON-NLS-2$
- + lastConflictingName + "\n").getBytes(charsetName)); //$NON-NLS-1$
- }
- // the lines with conflict-metadata are written. Now write the chunk
- for (int i = chunk.getBegin(); i < chunk.getEnd(); i++) {
- seq.writeLine(out, i);
- out.write('\n');
- }
- }
- // one possible leftover: if the merge result ended with a conflict we
- // have to close the last conflict here
- if (lastConflictingName != null) {
- out.write((">>>>>>> " + lastConflictingName + "\n").getBytes(charsetName)); //$NON-NLS-1$ //$NON-NLS-2$
- }
+ new MergeFormatterPass(out, res, seqName, charsetName).formatMerge();
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatterPass.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatterPass.java
new file mode 100644
index 0000000..0345921
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatterPass.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2009, Christian Halstrick <christian.halstrick at sap.com>
+ * Copyright (C) 2014, André de Oliveira <andre.oliveira at liferay.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.merge;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+
+import org.eclipse.jgit.diff.RawText;
+import org.eclipse.jgit.merge.MergeChunk.ConflictState;
+
+class MergeFormatterPass {
+
+ private final EolAwareOutputStream out;
+
+ private final MergeResult<RawText> res;
+
+ private final List<String> seqName;
+
+ private final String charsetName;
+
+ private final boolean threeWayMerge;
+
+ private String lastConflictingName; // is set to non-null whenever we are in
+ // a conflict
+
+ MergeFormatterPass(OutputStream out, MergeResult<RawText> res, List<String> seqName,
+ String charsetName) {
+ this.out = new EolAwareOutputStream(out);
+ this.res = res;
+ this.seqName = seqName;
+ this.charsetName = charsetName;
+ this.threeWayMerge = (res.getSequences().size() == 3);
+ }
+
+ void formatMerge() throws IOException {
+ boolean missingNewlineAtEnd = false;
+ for (MergeChunk chunk : res) {
+ RawText seq = res.getSequences().get(chunk.getSequenceIndex());
+ writeConflictMetadata(chunk);
+ // the lines with conflict-metadata are written. Now write the chunk
+ for (int i = chunk.getBegin(); i < chunk.getEnd(); i++)
+ writeLine(seq, i);
+ missingNewlineAtEnd = seq.isMissingNewlineAtEnd();
+ }
+ // one possible leftover: if the merge result ended with a conflict we
+ // have to close the last conflict here
+ if (lastConflictingName != null)
+ writeConflictEnd();
+ if (!missingNewlineAtEnd)
+ out.beginln();
+ }
+
+ private void writeConflictMetadata(MergeChunk chunk) throws IOException {
+ if (lastConflictingName != null
+ && chunk.getConflictState() != ConflictState.NEXT_CONFLICTING_RANGE) {
+ // found the end of an conflict
+ writeConflictEnd();
+ }
+ if (chunk.getConflictState() == ConflictState.FIRST_CONFLICTING_RANGE) {
+ // found the start of an conflict
+ writeConflictStart(chunk);
+ } else if (chunk.getConflictState() == ConflictState.NEXT_CONFLICTING_RANGE) {
+ // found another conflicting chunk
+ writeConflictChange(chunk);
+ }
+ }
+
+ private void writeConflictEnd() throws IOException {
+ writeln(">>>>>>> " + lastConflictingName); //$NON-NLS-1$
+ lastConflictingName = null;
+ }
+
+ private void writeConflictStart(MergeChunk chunk) throws IOException {
+ lastConflictingName = seqName.get(chunk.getSequenceIndex());
+ writeln("<<<<<<< " + lastConflictingName); //$NON-NLS-1$
+ }
+
+ private void writeConflictChange(MergeChunk chunk) throws IOException {
+ /*
+ * In case of a non-three-way merge I'll add the name of the conflicting
+ * chunk behind the equal signs. I also append the name of the last
+ * conflicting chunk after the ending greater-than signs. If somebody
+ * knows a better notation to present non-three-way merges - feel free
+ * to correct here.
+ */
+ lastConflictingName = seqName.get(chunk.getSequenceIndex());
+ writeln(threeWayMerge ? "=======" : "======= " //$NON-NLS-1$ //$NON-NLS-2$
+ + lastConflictingName);
+ }
+
+ private void writeln(String s) throws IOException {
+ out.beginln();
+ out.write((s + "\n").getBytes(charsetName)); //$NON-NLS-1$
+ }
+
+ private void writeLine(RawText seq, int i) throws IOException {
+ out.beginln();
+ seq.writeLine(out, i);
+ // still BOL? It was a blank line. But writeLine won't lf, so we do.
+ if (out.isBeginln())
+ out.write('\n');
+ }
+}
\ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java
index d94e728..82cbf36 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeMessageFormatter.java
@@ -46,6 +46,7 @@ import java.util.ArrayList;
import java.util.List;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.util.ChangeIdUtil;
@@ -76,48 +77,48 @@ public class MergeMessageFormatter {
List<String> commits = new ArrayList<String>();
List<String> others = new ArrayList<String>();
for (Ref ref : refsToMerge) {
- if (ref.getName().startsWith(Constants.R_HEADS))
+ if (ref.getName().startsWith(Constants.R_HEADS)) {
branches.add("'" + Repository.shortenRefName(ref.getName()) //$NON-NLS-1$
+ "'"); //$NON-NLS-1$
-
- else if (ref.getName().startsWith(Constants.R_REMOTES))
+ } else if (ref.getName().startsWith(Constants.R_REMOTES)) {
remoteBranches.add("'" //$NON-NLS-1$
+ Repository.shortenRefName(ref.getName()) + "'"); //$NON-NLS-1$
-
- else if (ref.getName().startsWith(Constants.R_TAGS))
+ } else if (ref.getName().startsWith(Constants.R_TAGS)) {
tags.add("'" + Repository.shortenRefName(ref.getName()) + "'"); //$NON-NLS-1$ //$NON-NLS-2$
-
- else if (ref.getName().equals(ref.getObjectId().getName()))
- commits.add("'" + ref.getName() + "'"); //$NON-NLS-1$ //$NON-NLS-2$
-
- else
- others.add(ref.getName());
+ } else {
+ ObjectId objectId = ref.getObjectId();
+ if (objectId != null && ref.getName().equals(objectId.getName())) {
+ commits.add("'" + ref.getName() + "'"); //$NON-NLS-1$ //$NON-NLS-2$
+ } else {
+ others.add(ref.getName());
+ }
+ }
}
List<String> listings = new ArrayList<String>();
if (!branches.isEmpty())
- listings.add(joinNames(branches, "branch", "branches"));
+ listings.add(joinNames(branches, "branch", "branches")); //$NON-NLS-1$//$NON-NLS-2$
if (!remoteBranches.isEmpty())
- listings.add(joinNames(remoteBranches, "remote-tracking branch",
- "remote-tracking branches"));
+ listings.add(joinNames(remoteBranches, "remote-tracking branch", //$NON-NLS-1$
+ "remote-tracking branches")); //$NON-NLS-1$
if (!tags.isEmpty())
- listings.add(joinNames(tags, "tag", "tags"));
+ listings.add(joinNames(tags, "tag", "tags")); //$NON-NLS-1$ //$NON-NLS-2$
if (!commits.isEmpty())
- listings.add(joinNames(commits, "commit", "commits"));
+ listings.add(joinNames(commits, "commit", "commits")); //$NON-NLS-1$ //$NON-NLS-2$
if (!others.isEmpty())
- listings.add(StringUtils.join(others, ", ", " and ")); //$NON-NLS-1$
+ listings.add(StringUtils.join(others, ", ", " and ")); //$NON-NLS-1$ //$NON-NLS-2$
sb.append(StringUtils.join(listings, ", ")); //$NON-NLS-1$
String targetName = target.getLeaf().getName();
if (!targetName.equals(Constants.R_HEADS + Constants.MASTER)) {
String targetShortName = Repository.shortenRefName(targetName);
- sb.append(" into " + targetShortName);
+ sb.append(" into " + targetShortName); //$NON-NLS-1$
}
return sb.toString();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeResult.java
index dc3c772..106f9c7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeResult.java
@@ -70,7 +70,7 @@ import org.eclipse.jgit.util.IntList;
public class MergeResult<S extends Sequence> implements Iterable<MergeChunk> {
private final List<S> sequences;
- private final IntList chunks = new IntList();
+ final IntList chunks = new IntList();
private boolean containsConflicts = false;
@@ -127,7 +127,7 @@ public class MergeResult<S extends Sequence> implements Iterable<MergeChunk> {
return sequences;
}
- private static final ConflictState[] states = ConflictState.values();
+ static final ConflictState[] states = ConflictState.values();
/**
* @return an iterator over the MergeChunks. The iterator does not support
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java
index d786a18..bee2d03 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2008-2013, Google Inc.
+ * Copyright (C) 2016, Laurent Delaigue <laurent.delaigue at obeo.fr>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -51,10 +52,11 @@ import org.eclipse.jgit.errors.NoMergeBaseException;
import org.eclipse.jgit.errors.NoMergeBaseException.MergeBaseFailureReason;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
@@ -89,6 +91,13 @@ public abstract class Merger {
protected RevTree[] sourceTrees;
/**
+ * A progress monitor.
+ *
+ * @since 4.2
+ */
+ protected ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
+
+ /**
* Create a new merge instance for a repository.
*
* @param local
@@ -125,9 +134,9 @@ public abstract class Merger {
* repository instance returned by {@link #getRepository()}.
*/
public void setObjectInserter(ObjectInserter oi) {
- walk.release();
- reader.release();
- inserter.release();
+ walk.close();
+ reader.close();
+ inserter.close();
inserter = oi;
reader = oi.newReader();
walk = new RevWalk(reader);
@@ -206,8 +215,8 @@ public abstract class Merger {
return ok;
} finally {
if (flush)
- inserter.release();
- reader.release();
+ inserter.close();
+ reader.close();
}
}
@@ -220,38 +229,6 @@ public abstract class Merger {
/**
* Return the merge base of two commits.
- * <p>
- * May only be called after {@link #merge(RevCommit...)}.
- *
- * @param aIdx
- * index of the first commit in tips passed to
- * {@link #merge(RevCommit...)}.
- * @param bIdx
- * index of the second commit in tips passed to
- * {@link #merge(RevCommit...)}.
- * @return the merge base of two commits
- * @throws IncorrectObjectTypeException
- * one of the input objects is not a commit.
- * @throws IOException
- * objects are missing or multiple merge bases were found.
- * @deprecated use {@link #getBaseCommitId()} instead, as that does not
- * require walking the commits again
- */
- @Deprecated
- public RevCommit getBaseCommit(final int aIdx, final int bIdx)
- throws IncorrectObjectTypeException,
- IOException {
- if (sourceCommits[aIdx] == null)
- throw new IncorrectObjectTypeException(sourceObjects[aIdx],
- Constants.TYPE_COMMIT);
- if (sourceCommits[bIdx] == null)
- throw new IncorrectObjectTypeException(sourceObjects[bIdx],
- Constants.TYPE_COMMIT);
- return getBaseCommit(sourceCommits[aIdx], sourceCommits[bIdx]);
- }
-
- /**
- * Return the merge base of two commits.
*
* @param a
* the first commit in {@link #sourceObjects}.
@@ -323,4 +300,20 @@ public abstract class Merger {
* @return resulting tree, if {@link #merge(AnyObjectId[])} returned true.
*/
public abstract ObjectId getResultTreeId();
+
+ /**
+ * Set a progress monitor.
+ *
+ * @param monitor
+ * Monitor to use, can be null to indicate no progress reporting
+ * is desired.
+ * @since 4.2
+ */
+ public void setProgressMonitor(ProgressMonitor monitor) {
+ if (monitor == null) {
+ this.monitor = NullProgressMonitor.INSTANCE;
+ } else {
+ this.monitor = monitor;
+ }
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
index 85cdb76..e055644 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
@@ -57,8 +57,6 @@ import java.util.List;
import java.util.TimeZone;
import org.eclipse.jgit.dircache.DirCache;
-import org.eclipse.jgit.dircache.DirCacheBuilder;
-import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.NoMergeBaseException;
import org.eclipse.jgit.internal.JGitText;
@@ -68,7 +66,8 @@ import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.filter.RevFilter;
-import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.AbstractTreeIterator;
+import org.eclipse.jgit.treewalk.EmptyTreeIterator;
import org.eclipse.jgit.treewalk.WorkingTreeIterator;
/**
@@ -179,7 +178,7 @@ public class RecursiveMerger extends ResolveMerger {
WorkingTreeIterator oldWTreeIt = workingTreeIterator;
workingTreeIterator = null;
try {
- dircache = dircacheFromTree(currentBase.getTree());
+ dircache = DirCache.read(reader, currentBase.getTree());
inCore = true;
List<RevCommit> parents = new ArrayList<RevCommit>();
@@ -194,10 +193,12 @@ public class RecursiveMerger extends ResolveMerger {
Integer.valueOf(MAX_BASES), a.name(), b.name(),
Integer.valueOf(baseCommits.size())));
parents.add(nextBase);
- if (mergeTrees(
- openTree(getBaseCommit(currentBase, nextBase,
- callDepth + 1).getTree()),
- currentBase.getTree(), nextBase.getTree(), true))
+ RevCommit bc = getBaseCommit(currentBase, nextBase,
+ callDepth + 1);
+ AbstractTreeIterator bcTree = (bc == null) ? new EmptyTreeIterator()
+ : openTree(bc.getTree());
+ if (mergeTrees(bcTree, currentBase.getTree(),
+ nextBase.getTree(), true))
currentBase = createCommitForTree(resultTree, parents);
else
throw new NoMergeBaseException(
@@ -252,29 +253,4 @@ public class RecursiveMerger extends ResolveMerger {
new Date((time + 1) * 1000L),
TimeZone.getTimeZone("GMT+0000")); //$NON-NLS-1$
}
-
- /**
- * Create a new in memory dircache which has the same content as a given
- * tree.
- *
- * @param treeId
- * the tree which should be used to fill the dircache
- * @return a new in memory dircache
- * @throws IOException
- */
- private DirCache dircacheFromTree(ObjectId treeId) throws IOException {
- DirCache ret = DirCache.newInCore();
- DirCacheBuilder aBuilder = ret.builder();
- TreeWalk atw = new TreeWalk(reader);
- atw.addTree(treeId);
- atw.setRecursive(true);
- while (atw.next()) {
- DirCacheEntry e = new DirCacheEntry(atw.getRawPath());
- e.setFileMode(atw.getFileMode(0));
- e.setObjectId(atw.getObjectId(0));
- aBuilder.add(e);
- }
- aBuilder.finish();
- return ret;
- }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
index 8e70f57..de08e4b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -47,12 +47,14 @@ package org.eclipse.jgit.merge;
import static org.eclipse.jgit.lib.Constants.CHARACTER_ENCODING;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -88,6 +90,7 @@ import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.WorkingTreeIterator;
+import org.eclipse.jgit.treewalk.filter.TreeFilter;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.TemporaryBuffer;
@@ -522,10 +525,11 @@ public class ResolveMerger extends ThreeWayMerger {
}
}
- if (nonTree(modeO) && modeB == modeT && tw.idEqual(T_BASE, T_THEIRS)) {
+ if (modeB == modeT && tw.idEqual(T_BASE, T_THEIRS)) {
// THEIRS was not changed compared to BASE. All changes must be in
// OURS. OURS is chosen. We can keep the existing entry.
- keep(ourDce);
+ if (ourDce != null)
+ keep(ourDce);
// no checkout needed!
return true;
}
@@ -546,11 +550,12 @@ public class ResolveMerger extends ThreeWayMerger {
if (e != null)
toBeCheckedOut.put(tw.getPathString(), e);
return true;
- } else if (modeT == 0 && modeB != 0) {
- // we want THEIRS ... but THEIRS contains the deletion of the
- // file. Also, do not complain if the file is already deleted
- // locally. This complements the test in isWorktreeDirty() for
- // the same case.
+ } else {
+ // we want THEIRS ... but THEIRS contains a folder or the
+ // deletion of the path. Delete what's in the workingtree (the
+ // workingtree is clean) but do not complain if the file is
+ // already deleted locally. This complements the test in
+ // isWorktreeDirty() for the same case.
if (tw.getTreeCount() > T_FILE && tw.getRawMode(T_FILE) == 0)
return true;
toBeDeleted.add(tw.getPathString());
@@ -781,35 +786,30 @@ public class ResolveMerger extends ThreeWayMerger {
private File writeMergedFile(MergeResult<RawText> result)
throws FileNotFoundException, IOException {
File workTree = db.getWorkTree();
- if (workTree == null)
- // TODO: This should be handled by WorkingTreeIterators which
- // support write operations
- throw new UnsupportedOperationException();
-
FS fs = db.getFS();
File of = new File(workTree, tw.getPathString());
File parentFolder = of.getParentFile();
if (!fs.exists(parentFolder))
parentFolder.mkdirs();
- FileOutputStream fos = new FileOutputStream(of);
- try {
- new MergeFormatter().formatMerge(fos, result,
+ try (OutputStream os = new BufferedOutputStream(
+ new FileOutputStream(of))) {
+ new MergeFormatter().formatMerge(os, result,
Arrays.asList(commitNames), CHARACTER_ENCODING);
- } finally {
- fos.close();
}
return of;
}
private ObjectId insertMergeResult(MergeResult<RawText> result)
throws IOException {
- TemporaryBuffer.LocalFile buf = new TemporaryBuffer.LocalFile(10 << 20);
+ TemporaryBuffer.LocalFile buf = new TemporaryBuffer.LocalFile(
+ db.getDirectory(), 10 << 20);
try {
new MergeFormatter().formatMerge(buf, result,
Arrays.asList(commitNames), CHARACTER_ENCODING);
buf.close();
- return getObjectInserter().insert(OBJ_BLOB, buf.length(),
- buf.openInputStream());
+ try (InputStream in = buf.openInputStream()) {
+ return getObjectInserter().insert(OBJ_BLOB, buf.length(), in);
+ }
} finally {
buf.destroy();
}
@@ -1010,8 +1010,11 @@ public class ResolveMerger extends ThreeWayMerger {
tw.addTree(headTree);
tw.addTree(mergeTree);
tw.addTree(buildIt);
- if (workingTreeIterator != null)
+ if (workingTreeIterator != null) {
tw.addTree(workingTreeIterator);
+ } else {
+ tw.setFilter(TreeFilter.ANY_DIFF);
+ }
if (!mergeTreeWalk(tw, ignoreConflicts)) {
return false;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/SquashMessageFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/SquashMessageFormatter.java
index 8931fd6..39138b9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/SquashMessageFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/SquashMessageFormatter.java
@@ -76,9 +76,9 @@ public class SquashMessageFormatter {
*/
public String format(List<RevCommit> squashedCommits, Ref target) {
StringBuilder sb = new StringBuilder();
- sb.append("Squashed commit of the following:\n");
+ sb.append("Squashed commit of the following:\n"); //$NON-NLS-1$
for (RevCommit c : squashedCommits) {
- sb.append("\ncommit ");
+ sb.append("\ncommit "); //$NON-NLS-1$
sb.append(c.getName());
sb.append("\n"); //$NON-NLS-1$
sb.append(toString(c.getAuthorIdent()));
@@ -92,12 +92,12 @@ public class SquashMessageFormatter {
private String toString(PersonIdent author) {
final StringBuilder a = new StringBuilder();
- a.append("Author: ");
+ a.append("Author: "); //$NON-NLS-1$
a.append(author.getName());
a.append(" <"); //$NON-NLS-1$
a.append(author.getEmailAddress());
a.append(">\n"); //$NON-NLS-1$
- a.append("Date: ");
+ a.append("Date: "); //$NON-NLS-1$
a.append(dateFormatter.formatDate(author));
a.append("\n"); //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyRecursive.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyRecursive.java
index 58c1ed2..22e608e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyRecursive.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyRecursive.java
@@ -64,6 +64,6 @@ public class StrategyRecursive extends StrategyResolve {
@Override
public String getName() {
- return "recursive";
+ return "recursive"; //$NON-NLS-1$
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/FanoutBucket.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/FanoutBucket.java
index ea904cd..79fbb09 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/notes/FanoutBucket.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/FanoutBucket.java
@@ -260,8 +260,8 @@ class FanoutBucket extends InMemoryNoteBucket {
}
ObjectId getTreeId() {
- try {
- return new ObjectInserter.Formatter().idFor(build(false, null));
+ try (ObjectInserter.Formatter f = new ObjectInserter.Formatter()) {
+ return f.idFor(build(false, null));
} catch (IOException e) {
// should never happen as we are not inserting
throw new RuntimeException(e);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/LeafBucket.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/LeafBucket.java
index ea4d7bc..41f7501 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/notes/LeafBucket.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/LeafBucket.java
@@ -53,6 +53,7 @@ import java.util.NoSuchElementException;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectInserter.Formatter;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.TreeFormatter;
@@ -183,7 +184,9 @@ class LeafBucket extends InMemoryNoteBucket {
@Override
ObjectId getTreeId() {
- return new ObjectInserter.Formatter().idFor(build());
+ try (Formatter f = new ObjectInserter.Formatter()) {
+ return f.idFor(build());
+ }
}
private TreeFormatter build() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/NonNoteEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NonNoteEntry.java
index 6a2d44b..362328a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/notes/NonNoteEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NonNoteEntry.java
@@ -47,6 +47,7 @@ import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.TreeFormatter;
+import org.eclipse.jgit.util.Paths;
/** A tree entry found in a note branch that isn't a valid note. */
class NonNoteEntry extends ObjectId {
@@ -74,27 +75,8 @@ class NonNoteEntry extends ObjectId {
}
int pathCompare(byte[] bBuf, int bPos, int bLen, FileMode bMode) {
- return pathCompare(name, 0, name.length, mode, //
- bBuf, bPos, bLen, bMode);
- }
-
- private static int pathCompare(final byte[] aBuf, int aPos, final int aEnd,
- final FileMode aMode, final byte[] bBuf, int bPos, final int bEnd,
- final FileMode bMode) {
- while (aPos < aEnd && bPos < bEnd) {
- int cmp = (aBuf[aPos++] & 0xff) - (bBuf[bPos++] & 0xff);
- if (cmp != 0)
- return cmp;
- }
-
- if (aPos < aEnd)
- return (aBuf[aPos] & 0xff) - lastPathChar(bMode);
- if (bPos < bEnd)
- return lastPathChar(aMode) - (bBuf[bPos] & 0xff);
- return 0;
- }
-
- private static int lastPathChar(final FileMode mode) {
- return FileMode.TREE.equals(mode.getBits()) ? '/' : '\0';
+ return Paths.compare(
+ name, 0, name.length, mode.getBits(),
+ bBuf, bPos, bLen, bMode.getBits());
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMapMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMapMerger.java
index 0614476..19ec1a1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMapMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMapMerger.java
@@ -135,8 +135,8 @@ public class NoteMapMerger {
inserter.flush();
return NoteMap.newMap(mergedBucket, reader);
} finally {
- reader.release();
- inserter.release();
+ reader.close();
+ inserter.close();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java
index 7822947..f1d7dc8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java
@@ -137,7 +137,6 @@ class MergeBaseGenerator extends Generator {
for (;;) {
final RevCommit c = pending.next();
if (c == null) {
- walker.reader.walkAdviceEnd();
return null;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
index a0af067..9c3af5a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/ObjectWalk.java
@@ -60,6 +60,7 @@ import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.filter.ObjectFilter;
import org.eclipse.jgit.util.RawParseUtils;
/**
@@ -99,9 +100,7 @@ public class ObjectWalk extends RevWalk {
private BlockObjQueue pendingObjects;
- private RevCommit firstCommit;
-
- private RevCommit lastCommit;
+ private ObjectFilter objectFilter;
private TreeVisit freeVisit;
@@ -133,8 +132,10 @@ public class ObjectWalk extends RevWalk {
*/
public ObjectWalk(ObjectReader or) {
super(or);
+ setRetainBody(false);
rootObjects = new ArrayList<RevObject>();
pendingObjects = new BlockObjQueue();
+ objectFilter = ObjectFilter.ALL;
pathBuf = new byte[256];
}
@@ -253,26 +254,59 @@ public class ObjectWalk extends RevWalk {
boundary = hasRevSort(RevSort.BOUNDARY);
}
+ /**
+ * Get the currently configured object filter.
+ *
+ * @return the current filter. Never null as a filter is always needed.
+ *
+ * @since 4.0
+ */
+ public ObjectFilter getObjectFilter() {
+ return objectFilter;
+ }
+
+ /**
+ * Set the object filter for this walker. This filter affects the objects
+ * visited by {@link #nextObject()}. It does not affect the commits
+ * listed by {@link #next()}.
+ * <p>
+ * If the filter returns false for an object, then that object is skipped
+ * and objects reachable from it are not enqueued to be walked recursively.
+ * This can be used to speed up the object walk by skipping subtrees that
+ * are known to be uninteresting.
+ *
+ * @param newFilter
+ * the new filter. If null the special {@link ObjectFilter#ALL}
+ * filter will be used instead, as it matches every object.
+ *
+ * @since 4.0
+ */
+ public void setObjectFilter(ObjectFilter newFilter) {
+ assertNotStarted();
+ objectFilter = newFilter != null ? newFilter : ObjectFilter.ALL;
+ }
+
@Override
public RevCommit next() throws MissingObjectException,
IncorrectObjectTypeException, IOException {
for (;;) {
final RevCommit r = super.next();
if (r == null) {
- if (firstCommit != null)
- reader.walkAdviceBeginTrees(this, firstCommit, lastCommit);
return null;
}
+ final RevTree t = r.getTree();
if ((r.flags & UNINTERESTING) != 0) {
- markTreeUninteresting(r.getTree());
- if (boundary)
+ if (objectFilter.include(this, t)) {
+ markTreeUninteresting(t);
+ }
+ if (boundary) {
return r;
+ }
continue;
}
- if (firstCommit == null)
- firstCommit = r;
- lastCommit = r;
- pendingObjects.add(r.getTree());
+ if (objectFilter.include(this, t)) {
+ pendingObjects.add(t);
+ }
return r;
}
}
@@ -304,6 +338,10 @@ public class ObjectWalk extends RevWalk {
idBuffer.fromRaw(buf, ptr);
ptr += ID_SZ;
+ if (!objectFilter.include(this, idBuffer)) {
+ continue;
+ }
+
RevObject obj = objects.get(idBuffer);
if (obj != null && (obj.flags & SEEN) != 0)
continue;
@@ -365,7 +403,6 @@ public class ObjectWalk extends RevWalk {
for (;;) {
RevObject o = pendingObjects.next();
if (o == null) {
- reader.walkAdviceEnd();
return null;
}
int flags = o.flags;
@@ -633,8 +670,6 @@ public class ObjectWalk extends RevWalk {
public void dispose() {
super.dispose();
pendingObjects = new BlockObjQueue();
- firstCommit = null;
- lastCommit = null;
currVisit = null;
freeVisit = null;
}
@@ -648,8 +683,6 @@ public class ObjectWalk extends RevWalk {
rootObjects = new ArrayList<RevObject>();
pendingObjects = new BlockObjQueue();
- firstCommit = null;
- lastCommit = null;
currVisit = null;
freeVisit = null;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PendingGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PendingGenerator.java
index f24c278..94ae2c9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PendingGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PendingGenerator.java
@@ -128,7 +128,6 @@ class PendingGenerator extends Generator {
for (;;) {
final RevCommit c = pending.next();
if (c == null) {
- walker.reader.walkAdviceEnd();
return null;
}
@@ -177,7 +176,6 @@ class PendingGenerator extends Generator {
c.disposeBody();
}
} catch (StopWalkException swe) {
- walker.reader.walkAdviceEnd();
pending.clear();
return null;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java
index fac4f80..e67ada6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java
@@ -44,12 +44,17 @@
package org.eclipse.jgit.revwalk;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import java.io.IOException;
import java.nio.charset.Charset;
+import java.nio.charset.IllegalCharsetNameException;
+import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AnyObjectId;
@@ -92,29 +97,34 @@ public class RevCommit extends RevObject {
/**
* Parse a commit from its canonical format.
- *
+ * <p>
* This method inserts the commit directly into the caller supplied revision
* pool, making it appear as though the commit exists in the repository,
* even if it doesn't. The repository under the pool is not affected.
+ * <p>
+ * The body of the commit (message, author, committer) is always retained in
+ * the returned {@code RevCommit}, even if the supplied {@code RevWalk} has
+ * been configured with {@code setRetainBody(false)}.
*
* @param rw
* the revision pool to allocate the commit within. The commit's
* tree and parent pointers will be obtained from this pool.
* @param raw
- * the canonical formatted commit to be parsed.
+ * the canonical formatted commit to be parsed. This buffer will
+ * be retained by the returned {@code RevCommit} and must not be
+ * modified by the caller.
* @return the parsed commit, in an isolated revision pool that is not
* available to the caller.
* @throws IOException
* in case of RevWalk initialization fails
*/
public static RevCommit parse(RevWalk rw, byte[] raw) throws IOException {
- ObjectInserter.Formatter fmt = new ObjectInserter.Formatter();
- boolean retain = rw.isRetainBody();
- rw.setRetainBody(true);
- RevCommit r = rw.lookupCommit(fmt.idFor(Constants.OBJ_COMMIT, raw));
- r.parseCanonical(rw, raw);
- rw.setRetainBody(retain);
- return r;
+ try (ObjectInserter.Formatter fmt = new ObjectInserter.Formatter()) {
+ RevCommit r = rw.lookupCommit(fmt.idFor(Constants.OBJ_COMMIT, raw));
+ r.parseCanonical(rw, raw);
+ r.buffer = raw;
+ return r;
+ }
}
static final RevCommit[] NO_PARENTS = {};
@@ -436,12 +446,12 @@ public class RevCommit extends RevObject {
* @return decoded commit message as a string. Never null.
*/
public final String getFullMessage() {
- final byte[] raw = buffer;
- final int msgB = RawParseUtils.commitMessage(raw, 0);
- if (msgB < 0)
+ byte[] raw = buffer;
+ int msgB = RawParseUtils.commitMessage(raw, 0);
+ if (msgB < 0) {
return ""; //$NON-NLS-1$
- final Charset enc = RawParseUtils.parseEncoding(raw);
- return RawParseUtils.decode(enc, raw, msgB, raw.length);
+ }
+ return RawParseUtils.decode(guessEncoding(), raw, msgB, raw.length);
}
/**
@@ -460,16 +470,17 @@ public class RevCommit extends RevObject {
* spanned multiple lines. Embedded LFs are converted to spaces.
*/
public final String getShortMessage() {
- final byte[] raw = buffer;
- final int msgB = RawParseUtils.commitMessage(raw, 0);
- if (msgB < 0)
+ byte[] raw = buffer;
+ int msgB = RawParseUtils.commitMessage(raw, 0);
+ if (msgB < 0) {
return ""; //$NON-NLS-1$
+ }
- final Charset enc = RawParseUtils.parseEncoding(raw);
- final int msgE = RawParseUtils.endOfParagraph(raw, msgB);
- String str = RawParseUtils.decode(enc, raw, msgB, msgE);
- if (hasLF(raw, msgB, msgE))
+ int msgE = RawParseUtils.endOfParagraph(raw, msgB);
+ String str = RawParseUtils.decode(guessEncoding(), raw, msgB, msgE);
+ if (hasLF(raw, msgB, msgE)) {
str = StringUtils.replaceLineBreaksWithSpace(str);
+ }
return str;
}
@@ -483,18 +494,49 @@ public class RevCommit extends RevObject {
/**
* Determine the encoding of the commit message buffer.
* <p>
+ * Locates the "encoding" header (if present) and returns its value. Due to
+ * corruption in the wild this may be an invalid encoding name that is not
+ * recognized by any character encoding library.
+ * <p>
+ * If no encoding header is present, null.
+ *
+ * @return the preferred encoding of {@link #getRawBuffer()}; or null.
+ * @since 4.2
+ */
+ @Nullable
+ public final String getEncodingName() {
+ return RawParseUtils.parseEncodingName(buffer);
+ }
+
+ /**
+ * Determine the encoding of the commit message buffer.
+ * <p>
* Locates the "encoding" header (if present) and then returns the proper
* character set to apply to this buffer to evaluate its contents as
* character data.
* <p>
- * If no encoding header is present, {@link Constants#CHARSET} is assumed.
+ * If no encoding header is present {@code UTF-8} is assumed.
*
* @return the preferred encoding of {@link #getRawBuffer()}.
+ * @throws IllegalCharsetNameException
+ * if the character set requested by the encoding header is
+ * malformed and unsupportable.
+ * @throws UnsupportedCharsetException
+ * if the JRE does not support the character set requested by
+ * the encoding header.
*/
public final Charset getEncoding() {
return RawParseUtils.parseEncoding(buffer);
}
+ private Charset guessEncoding() {
+ try {
+ return getEncoding();
+ } catch (IllegalCharsetNameException | UnsupportedCharsetException e) {
+ return UTF_8;
+ }
+ }
+
/**
* Parse the footer lines (e.g. "Signed-off-by") for machine processing.
* <p>
@@ -524,7 +566,7 @@ public class RevCommit extends RevObject {
final int msgB = RawParseUtils.commitMessage(raw, 0);
final ArrayList<FooterLine> r = new ArrayList<FooterLine>(4);
- final Charset enc = getEncoding();
+ final Charset enc = guessEncoding();
for (;;) {
ptr = RawParseUtils.prevLF(raw, ptr);
if (ptr <= msgB)
@@ -604,7 +646,19 @@ public class RevCommit extends RevObject {
inDegree = 0;
}
- final void disposeBody() {
+ /**
+ * Discard the message buffer to reduce memory usage.
+ * <p>
+ * After discarding the memory usage of the {@code RevCommit} is reduced to
+ * only the {@link #getTree()} and {@link #getParents()} pointers and the
+ * time in {@link #getCommitTime()}. Accessing other properties such as
+ * {@link #getAuthorIdent()}, {@link #getCommitterIdent()} or either message
+ * function requires reloading the buffer by invoking
+ * {@link RevWalk#parseBody(RevObject)}.
+ *
+ * @since 4.0
+ */
+ public final void disposeBody() {
buffer = null;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
index 1a59b44..81a54bf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
@@ -45,8 +45,12 @@
package org.eclipse.jgit.revwalk;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import java.io.IOException;
import java.nio.charset.Charset;
+import java.nio.charset.IllegalCharsetNameException;
+import java.nio.charset.UnsupportedCharsetException;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -87,16 +91,22 @@ public class RevTag extends RevObject {
/**
* Parse an annotated tag from its canonical format.
- *
+ * <p>
* This method inserts the tag directly into the caller supplied revision
* pool, making it appear as though the tag exists in the repository, even
* if it doesn't. The repository under the pool is not affected.
+ * <p>
+ * The body of the tag (message, tagger, signature) is always retained in
+ * the returned {@code RevTag}, even if the supplied {@code RevWalk} has
+ * been configured with {@code setRetainBody(false)}.
*
* @param rw
* the revision pool to allocate the tag within. The tag's object
* pointer will be obtained from this pool.
* @param raw
- * the canonical formatted tag to be parsed.
+ * the canonical formatted tag to be parsed. This buffer will be
+ * retained by the returned {@code RevTag} and must not be
+ * modified by the caller.
* @return the parsed tag, in an isolated revision pool that is not
* available to the caller.
* @throws CorruptObjectException
@@ -104,13 +114,12 @@ public class RevTag extends RevObject {
*/
public static RevTag parse(RevWalk rw, byte[] raw)
throws CorruptObjectException {
- ObjectInserter.Formatter fmt = new ObjectInserter.Formatter();
- boolean retain = rw.isRetainBody();
- rw.setRetainBody(true);
- RevTag r = rw.lookupTag(fmt.idFor(Constants.OBJ_TAG, raw));
- r.parseCanonical(rw, raw);
- rw.setRetainBody(retain);
- return r;
+ try (ObjectInserter.Formatter fmt = new ObjectInserter.Formatter()) {
+ RevTag r = rw.lookupTag(fmt.idFor(Constants.OBJ_TAG, raw));
+ r.parseCanonical(rw, raw);
+ r.buffer = raw;
+ return r;
+ }
}
private RevObject object;
@@ -157,7 +166,7 @@ public class RevTag extends RevObject {
int p = pos.value += 4; // "tag "
final int nameEnd = RawParseUtils.nextLF(rawTag, p) - 1;
- tagName = RawParseUtils.decode(Constants.CHARSET, rawTag, p, nameEnd);
+ tagName = RawParseUtils.decode(UTF_8, rawTag, p, nameEnd);
if (walk.isRetainBody())
buffer = rawTag;
@@ -202,12 +211,12 @@ public class RevTag extends RevObject {
* @return decoded tag message as a string. Never null.
*/
public final String getFullMessage() {
- final byte[] raw = buffer;
- final int msgB = RawParseUtils.tagMessage(raw, 0);
- if (msgB < 0)
+ byte[] raw = buffer;
+ int msgB = RawParseUtils.tagMessage(raw, 0);
+ if (msgB < 0) {
return ""; //$NON-NLS-1$
- final Charset enc = RawParseUtils.parseEncoding(raw);
- return RawParseUtils.decode(enc, raw, msgB, raw.length);
+ }
+ return RawParseUtils.decode(guessEncoding(), raw, msgB, raw.length);
}
/**
@@ -226,19 +235,28 @@ public class RevTag extends RevObject {
* multiple lines. Embedded LFs are converted to spaces.
*/
public final String getShortMessage() {
- final byte[] raw = buffer;
- final int msgB = RawParseUtils.tagMessage(raw, 0);
- if (msgB < 0)
+ byte[] raw = buffer;
+ int msgB = RawParseUtils.tagMessage(raw, 0);
+ if (msgB < 0) {
return ""; //$NON-NLS-1$
+ }
- final Charset enc = RawParseUtils.parseEncoding(raw);
- final int msgE = RawParseUtils.endOfParagraph(raw, msgB);
- String str = RawParseUtils.decode(enc, raw, msgB, msgE);
- if (RevCommit.hasLF(raw, msgB, msgE))
+ int msgE = RawParseUtils.endOfParagraph(raw, msgB);
+ String str = RawParseUtils.decode(guessEncoding(), raw, msgB, msgE);
+ if (RevCommit.hasLF(raw, msgB, msgE)) {
str = StringUtils.replaceLineBreaksWithSpace(str);
+ }
return str;
}
+ private Charset guessEncoding() {
+ try {
+ return RawParseUtils.parseEncoding(buffer);
+ } catch (IllegalCharsetNameException | UnsupportedCharsetException e) {
+ return UTF_8;
+ }
+ }
+
/**
* Get a reference to the object this tag was placed on.
* <p>
@@ -265,7 +283,18 @@ public class RevTag extends RevObject {
return tagName;
}
- final void disposeBody() {
+ /**
+ * Discard the message buffer to reduce memory usage.
+ * <p>
+ * After discarding the memory usage of the {@code RevTag} is reduced to
+ * only the {@link #getObject()} pointer and {@link #getTagName()}.
+ * Accessing other properties such as {@link #getTaggerIdent()} or either
+ * message function requires reloading the buffer by invoking
+ * {@link RevWalk#parseBody(RevObject)}.
+ *
+ * @since 4.0
+ */
+ public final void disposeBody() {
buffer = null;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
index d19e467..c850493 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
@@ -95,7 +95,7 @@ import org.eclipse.jgit.treewalk.filter.TreeFilter;
* the same RevWalk at the same time. The Iterator may buffer RevCommits, while
* {@link #next()} does not.
*/
-public class RevWalk implements Iterable<RevCommit> {
+public class RevWalk implements Iterable<RevCommit>, AutoCloseable {
private static final int MB = 1 << 20;
/**
@@ -166,6 +166,8 @@ public class RevWalk implements Iterable<RevCommit> {
final ObjectReader reader;
+ private final boolean closeReader;
+
final MutableObjectId idBuffer;
ObjectIdOwnerMap<RevObject> objects;
@@ -175,6 +177,7 @@ public class RevWalk implements Iterable<RevCommit> {
private int delayFreeFlags;
private int retainOnReset;
+
int carryFlags = UNINTERESTING;
final ArrayList<RevCommit> roots;
@@ -189,7 +192,7 @@ public class RevWalk implements Iterable<RevCommit> {
private TreeFilter treeFilter;
- private boolean retainBody;
+ private boolean retainBody = true;
private boolean rewriteParents = true;
@@ -200,22 +203,27 @@ public class RevWalk implements Iterable<RevCommit> {
*
* @param repo
* the repository the walker will obtain data from. An
- * ObjectReader will be created by the walker, and must be
- * released by the caller.
+ * ObjectReader will be created by the walker, and will be closed
+ * when the walker is closed.
*/
public RevWalk(final Repository repo) {
- this(repo.newObjectReader());
+ this(repo.newObjectReader(), true);
}
/**
* Create a new revision walker for a given repository.
+ * <p>
*
* @param or
- * the reader the walker will obtain data from. The reader should
- * be released by the caller when the walker is no longer
- * required.
+ * the reader the walker will obtain data from. The reader is not
+ * closed when the walker is closed (but is closed by {@link
+ * #dispose()}.
*/
public RevWalk(ObjectReader or) {
+ this(or, false);
+ }
+
+ private RevWalk(ObjectReader or, boolean closeReader) {
reader = or;
idBuffer = new MutableObjectId();
objects = new ObjectIdOwnerMap<RevObject>();
@@ -225,7 +233,7 @@ public class RevWalk implements Iterable<RevCommit> {
sorting = EnumSet.of(RevSort.NONE);
filter = RevFilter.ALL;
treeFilter = TreeFilter.ALL;
- retainBody = true;
+ this.closeReader = closeReader;
}
/** @return the reader this walker is using to load objects. */
@@ -238,9 +246,14 @@ public class RevWalk implements Iterable<RevCommit> {
* <p>
* A walker that has been released can be used again, but may need to be
* released after the subsequent usage.
+ *
+ * @since 4.0
*/
- public void release() {
- reader.release();
+ @Override
+ public void close() {
+ if (closeReader) {
+ reader.close();
+ }
}
/**
@@ -582,6 +595,9 @@ public class RevWalk implements Iterable<RevCommit> {
* Usually the body is always retained, but some application code might not
* care and would prefer to discard the body of a commit as early as
* possible, to reduce memory usage.
+ * <p>
+ * True by default on {@link RevWalk} and false by default for
+ * {@link ObjectWalk}.
*
* @return true if the body should be retained; false it is discarded.
*/
@@ -595,6 +611,9 @@ public class RevWalk implements Iterable<RevCommit> {
* If a body of a commit or tag is not retained, the application must
* call {@link #parseBody(RevObject)} before the body can be safely
* accessed through the type specific access methods.
+ * <p>
+ * True by default on {@link RevWalk} and false by default for
+ * {@link ObjectWalk}.
*
* @param retain true to retain bodies; false to discard them early.
*/
@@ -1270,13 +1289,12 @@ public class RevWalk implements Iterable<RevCommit> {
* All RevFlag instances are also invalidated, and must not be reused.
*/
public void dispose() {
- reader.release();
+ reader.close();
freeFlags = APP_FLAGS;
delayFreeFlags = 0;
retainOnReset = 0;
carryFlags = UNINTERESTING;
objects.clear();
- reader.release();
roots.clear();
queue = new DateRevQueue();
pending = new StartGenerator(this);
@@ -1387,6 +1405,8 @@ public class RevWalk implements Iterable<RevCommit> {
/**
* Assume additional commits are shallow (have no parents).
+ * <p>
+ * This method is a No-op if the collection is empty.
*
* @param ids
* commits that should be treated as shallow commits, in addition
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java
index 593e09e..1ec6290 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/StartGenerator.java
@@ -85,8 +85,6 @@ class StartGenerator extends Generator {
final TreeFilter tf = w.getTreeFilter();
AbstractRevQueue q = walker.queue;
- w.reader.walkAdviceBeginCommits(w, w.roots);
-
if (rf == RevFilter.MERGE_BASE) {
// Computing for merge bases is a special case and does not
// use the bulk of the generator pipeline.
@@ -144,7 +142,7 @@ class StartGenerator extends Generator {
} else {
g = new PendingGenerator(w, pending, rf, pendingOutputType);
- if (boundary) {
+ if (walker.hasRevSort(RevSort.BOUNDARY)) {
// Because the boundary generator may produce uninteresting
// commits we cannot allow the pending generator to dispose
// of them early.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/CorruptObjectException.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/ObjectFilter.java
similarity index 54%
copy from org.eclipse.jgit/src/org/eclipse/jgit/errors/CorruptObjectException.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/ObjectFilter.java
index c6ea093..2ad273d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/errors/CorruptObjectException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/filter/ObjectFilter.java
@@ -1,8 +1,5 @@
-/*
- * Copyright (C) 2008, Google Inc.
- * Copyright (C) 2008, Jonas Fonseca <fonseca at diku.dk>
- * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg at dewire.com>
- * Copyright (C) 2006-2007, Shawn O. Pearce <spearce at spearce.org>
+/**
+ * Copyright (C) 2015, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -44,65 +41,53 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.errors;
+package org.eclipse.jgit.revwalk.filter;
import java.io.IOException;
-import java.text.MessageFormat;
-import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.ObjectWalk;
/**
- * Exception thrown when an object cannot be read from Git.
+ * Selects interesting objects when walking.
+ * <p>
+ * Applications should install the filter on an ObjectWalk by
+ * {@link ObjectWalk#setObjectFilter(ObjectFilter)} prior to starting traversal.
+ *
+ * @since 4.0
*/
-public class CorruptObjectException extends IOException {
- private static final long serialVersionUID = 1L;
-
- /**
- * Construct a CorruptObjectException for reporting a problem specified
- * object id
- *
- * @param id
- * @param why
- */
- public CorruptObjectException(final AnyObjectId id, final String why) {
- this(id.toObjectId(), why);
- }
-
- /**
- * Construct a CorruptObjectException for reporting a problem specified
- * object id
- *
- * @param id
- * @param why
- */
- public CorruptObjectException(final ObjectId id, final String why) {
- super(MessageFormat.format(JGitText.get().objectIsCorrupt, id.name(), why));
- }
+public abstract class ObjectFilter {
+ /** Default filter that always returns true. */
+ public static final ObjectFilter ALL = new AllFilter();
- /**
- * Construct a CorruptObjectException for reporting a problem not associated
- * with a specific object id.
- *
- * @param why
- */
- public CorruptObjectException(final String why) {
- super(why);
+ private static final class AllFilter extends ObjectFilter {
+ @Override
+ public boolean include(ObjectWalk walker, AnyObjectId o) {
+ return true;
+ }
}
/**
- * Construct a CorruptObjectException for reporting a problem not associated
- * with a specific object id.
+ * Determine if the named object should be included in the walk.
*
- * @param why
- * message describing the corruption.
- * @param cause
- * optional root cause exception
- * @since 3.4
+ * @param walker
+ * the active walker this filter is being invoked from within.
+ * @param objid
+ * the object currently being tested.
+ * @return {@code true} if the named object should be included in the walk.
+ * @throws MissingObjectException
+ * an object the filter needed to consult to determine its
+ * answer was missing
+ * @throws IncorrectObjectTypeException
+ * an object the filter needed to consult to determine its
+ * answer was of the wrong type
+ * @throws IOException
+ * an object the filter needed to consult to determine its
+ * answer could not be read.
*/
- public CorruptObjectException(String why, Throwable cause) {
- super(why);
- initCause(cause);
- }
+ public abstract boolean include(ObjectWalk walker, AnyObjectId objid)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
index 5509fc6..29c7f98 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
@@ -163,6 +163,9 @@ public class FileBasedConfig extends StoredConfig {
hash = newHash;
}
} catch (FileNotFoundException noFile) {
+ if (configFile.exists()) {
+ throw noFile;
+ }
clear();
snapshot = newSnapshot;
} catch (IOException e) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java
index 69abd0d..4205fc4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/WindowCacheConfig.java
@@ -221,6 +221,7 @@ public class WindowCacheConfig {
*
* @since 3.0
*/
+ @SuppressWarnings("deprecation")
public void install() {
WindowCache.reconfigure(this);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
index a8835b7..d594e97 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackConfig.java
@@ -138,6 +138,65 @@ public class PackConfig {
*/
public static final boolean DEFAULT_BUILD_BITMAPS = true;
+ /**
+ * Default count of most recent commits to select for bitmaps. Only applies
+ * when bitmaps are enabled: {@value}
+ *
+ * @see #setBitmapContiguousCommitCount(int)
+ * @since 4.2
+ */
+ public static final int DEFAULT_BITMAP_CONTIGUOUS_COMMIT_COUNT = 100;
+
+ /**
+ * Count at which the span between selected commits changes from
+ * "bitmapRecentCommitSpan" to "bitmapDistantCommitSpan". Only applies when
+ * bitmaps are enabled: {@value}
+ *
+ * @see #setBitmapRecentCommitCount(int)
+ * @since 4.2
+ */
+ public static final int DEFAULT_BITMAP_RECENT_COMMIT_COUNT = 20000;
+
+ /**
+ * Default spacing between commits in recent history when selecting commits
+ * for bitmaps. Only applies when bitmaps are enabled: {@value}
+ *
+ * @see #setBitmapRecentCommitSpan(int)
+ * @since 4.2
+ */
+ public static final int DEFAULT_BITMAP_RECENT_COMMIT_SPAN = 100;
+
+ /**
+ * Default spacing between commits in distant history when selecting commits
+ * for bitmaps. Only applies when bitmaps are enabled: {@value}
+ *
+ * @see #setBitmapDistantCommitSpan(int)
+ * @since 4.2
+ */
+ public static final int DEFAULT_BITMAP_DISTANT_COMMIT_SPAN = 5000;
+
+ /**
+ * Default count of branches required to activate inactive branch commit
+ * selection. If the number of branches is less than this then bitmaps for
+ * the entire commit history of all branches will be created, otherwise
+ * branches marked as "inactive" will have coverage for only partial
+ * history: {@value}
+ *
+ * @see #setBitmapExcessiveBranchCount(int)
+ * @since 4.2
+ */
+ public static final int DEFAULT_BITMAP_EXCESSIVE_BRANCH_COUNT = 100;
+
+ /**
+ * Default age at which a branch is considered inactive. Age is taken as the
+ * number of days ago that the most recent commit was made to a branch. Only
+ * affects bitmap processing if bitmaps are enabled and the
+ * "excessive branch count" has been exceeded: {@value}
+ *
+ * @see #setBitmapInactiveBranchAgeInDays(int)
+ * @since 4.2
+ */
+ public static final int DEFAULT_BITMAP_INACTIVE_BRANCH_AGE_IN_DAYS = 90;
private int compressionLevel = Deflater.DEFAULT_COMPRESSION;
@@ -169,6 +228,18 @@ public class PackConfig {
private boolean buildBitmaps = DEFAULT_BUILD_BITMAPS;
+ private int bitmapContiguousCommitCount = DEFAULT_BITMAP_CONTIGUOUS_COMMIT_COUNT;
+
+ private int bitmapRecentCommitCount = DEFAULT_BITMAP_RECENT_COMMIT_COUNT;
+
+ private int bitmapRecentCommitSpan = DEFAULT_BITMAP_RECENT_COMMIT_SPAN;
+
+ private int bitmapDistantCommitSpan = DEFAULT_BITMAP_DISTANT_COMMIT_SPAN;
+
+ private int bitmapExcessiveBranchCount = DEFAULT_BITMAP_EXCESSIVE_BRANCH_COUNT;
+
+ private int bitmapInactiveBranchAgeInDays = DEFAULT_BITMAP_INACTIVE_BRANCH_AGE_IN_DAYS;
+
private boolean cutDeltaChains;
/** Create a default configuration. */
@@ -222,6 +293,12 @@ public class PackConfig {
this.executor = cfg.executor;
this.indexVersion = cfg.indexVersion;
this.buildBitmaps = cfg.buildBitmaps;
+ this.bitmapContiguousCommitCount = cfg.bitmapContiguousCommitCount;
+ this.bitmapRecentCommitCount = cfg.bitmapRecentCommitCount;
+ this.bitmapRecentCommitSpan = cfg.bitmapRecentCommitSpan;
+ this.bitmapDistantCommitSpan = cfg.bitmapDistantCommitSpan;
+ this.bitmapExcessiveBranchCount = cfg.bitmapExcessiveBranchCount;
+ this.bitmapInactiveBranchAgeInDays = cfg.bitmapInactiveBranchAgeInDays;
this.cutDeltaChains = cfg.cutDeltaChains;
}
@@ -650,7 +727,7 @@ public class PackConfig {
* oldest (most compatible) format available for the objects.
* @see PackIndexWriter
*/
- public void setIndexVersion(final int version) {
+ public void setIndexVersion(int version) {
indexVersion = version;
}
@@ -684,6 +761,162 @@ public class PackConfig {
}
/**
+ * Get the count of most recent commits for which to build bitmaps.
+ *
+ * Default setting: {@value #DEFAULT_BITMAP_CONTIGUOUS_COMMIT_COUNT}
+ *
+ * @return the count of most recent commits for which to build bitmaps
+ * @since 4.2
+ */
+ public int getBitmapContiguousCommitCount() {
+ return bitmapContiguousCommitCount;
+ }
+
+ /**
+ * Set the count of most recent commits for which to build bitmaps.
+ *
+ * Default setting: {@value #DEFAULT_BITMAP_CONTIGUOUS_COMMIT_COUNT}
+ *
+ * @param count
+ * the count of most recent commits for which to build bitmaps
+ * @since 4.2
+ */
+ public void setBitmapContiguousCommitCount(int count) {
+ bitmapContiguousCommitCount = count;
+ }
+
+ /**
+ * Get the count at which to switch from "bitmapRecentCommitSpan" to
+ * "bitmapDistantCommitSpan".
+ *
+ * Default setting: {@value #DEFAULT_BITMAP_RECENT_COMMIT_COUNT}
+ *
+ * @return the count for switching between recent and distant spans
+ * @since 4.2
+ */
+ public int getBitmapRecentCommitCount() {
+ return bitmapRecentCommitCount;
+ }
+
+ /**
+ * Set the count at which to switch from "bitmapRecentCommitSpan" to
+ * "bitmapDistantCommitSpan".
+ *
+ * Default setting: {@value #DEFAULT_BITMAP_RECENT_COMMIT_COUNT}
+ *
+ * @param count
+ * the count for switching between recent and distant spans
+ * @since 4.2
+ */
+ public void setBitmapRecentCommitCount(int count) {
+ bitmapRecentCommitCount = count;
+ }
+
+ /**
+ * Get the span of commits when building bitmaps for recent history.
+ *
+ * Default setting: {@value #DEFAULT_BITMAP_RECENT_COMMIT_SPAN}
+ *
+ * @return the span of commits when building bitmaps for recent history
+ * @since 4.2
+ */
+ public int getBitmapRecentCommitSpan() {
+ return bitmapRecentCommitSpan;
+ }
+
+ /**
+ * Set the span of commits when building bitmaps for recent history.
+ *
+ * Default setting: {@value #DEFAULT_BITMAP_RECENT_COMMIT_SPAN}
+ *
+ * @param span
+ * the span of commits when building bitmaps for recent history
+ * @since 4.2
+ */
+ public void setBitmapRecentCommitSpan(int span) {
+ bitmapRecentCommitSpan = span;
+ }
+
+ /**
+ * Get the span of commits when building bitmaps for distant history.
+ *
+ * Default setting: {@value #DEFAULT_BITMAP_DISTANT_COMMIT_SPAN}
+ *
+ * @return the span of commits when building bitmaps for distant history
+ * @since 4.2
+ */
+ public int getBitmapDistantCommitSpan() {
+ return bitmapDistantCommitSpan;
+ }
+
+ /**
+ * Set the span of commits when building bitmaps for distant history.
+ *
+ * Default setting: {@value #DEFAULT_BITMAP_DISTANT_COMMIT_SPAN}
+ *
+ * @param span
+ * the span of commits when building bitmaps for distant history
+ * @since 4.2
+ */
+ public void setBitmapDistantCommitSpan(int span) {
+ bitmapDistantCommitSpan = span;
+ }
+
+ /**
+ * Get the count of branches deemed "excessive". If the count of branches in
+ * a repository exceeds this number and bitmaps are enabled, "inactive"
+ * branches will have fewer bitmaps than "active" branches.
+ *
+ * Default setting: {@value #DEFAULT_BITMAP_EXCESSIVE_BRANCH_COUNT}
+ *
+ * @return the count of branches deemed "excessive"
+ * @since 4.2
+ */
+ public int getBitmapExcessiveBranchCount() {
+ return bitmapExcessiveBranchCount;
+ }
+
+ /**
+ * Set the count of branches deemed "excessive". If the count of branches in
+ * a repository exceeds this number and bitmaps are enabled, "inactive"
+ * branches will have fewer bitmaps than "active" branches.
+ *
+ * Default setting: {@value #DEFAULT_BITMAP_EXCESSIVE_BRANCH_COUNT}
+ *
+ * @param count
+ * the count of branches deemed "excessive"
+ * @since 4.2
+ */
+ public void setBitmapExcessiveBranchCount(int count) {
+ bitmapExcessiveBranchCount = count;
+ }
+
+ /**
+ * Get the the age in days that marks a branch as "inactive".
+ *
+ * Default setting: {@value #DEFAULT_BITMAP_INACTIVE_BRANCH_AGE_IN_DAYS}
+ *
+ * @return the age in days that marks a branch as "inactive"
+ * @since 4.2
+ */
+ public int getBitmapInactiveBranchAgeInDays() {
+ return bitmapInactiveBranchAgeInDays;
+ }
+
+ /**
+ * Set the the age in days that marks a branch as "inactive".
+ *
+ * Default setting: {@value #DEFAULT_BITMAP_INACTIVE_BRANCH_AGE_IN_DAYS}
+ *
+ * @param ageInDays
+ * the age in days that marks a branch as "inactive"
+ * @since 4.2
+ */
+ public void setBitmapInactiveBranchAgeInDays(int ageInDays) {
+ bitmapInactiveBranchAgeInDays = ageInDays;
+ }
+
+ /**
* Update properties by setting fields from the configuration.
*
* If a property's corresponding variable is not defined in the supplied
@@ -712,19 +945,36 @@ public class PackConfig {
// These variables aren't standardized
//
setReuseDeltas(rc.getBoolean("pack", "reusedeltas", isReuseDeltas())); //$NON-NLS-1$ //$NON-NLS-2$
- setReuseObjects(rc.getBoolean("pack", "reuseobjects", isReuseObjects())); //$NON-NLS-1$ //$NON-NLS-2$
- setDeltaCompress(rc.getBoolean(
- "pack", "deltacompression", isDeltaCompress())); //$NON-NLS-1$ //$NON-NLS-2$
- setCutDeltaChains(rc.getBoolean(
- "pack", "cutdeltachains", getCutDeltaChains())); //$NON-NLS-1$ //$NON-NLS-2$
- setBuildBitmaps(rc.getBoolean("pack", "buildbitmaps", isBuildBitmaps())); //$NON-NLS-1$ //$NON-NLS-2$
+ setReuseObjects(
+ rc.getBoolean("pack", "reuseobjects", isReuseObjects())); //$NON-NLS-1$ //$NON-NLS-2$
+ setDeltaCompress(
+ rc.getBoolean("pack", "deltacompression", isDeltaCompress())); //$NON-NLS-1$ //$NON-NLS-2$
+ setCutDeltaChains(
+ rc.getBoolean("pack", "cutdeltachains", getCutDeltaChains())); //$NON-NLS-1$ //$NON-NLS-2$
+ setBuildBitmaps(
+ rc.getBoolean("pack", "buildbitmaps", isBuildBitmaps())); //$NON-NLS-1$ //$NON-NLS-2$
+ setBitmapContiguousCommitCount(
+ rc.getInt("pack", "bitmapcontiguouscommitcount", //$NON-NLS-1$ //$NON-NLS-2$
+ getBitmapContiguousCommitCount()));
+ setBitmapRecentCommitCount(rc.getInt("pack", "bitmaprecentcommitcount", //$NON-NLS-1$ //$NON-NLS-2$
+ getBitmapRecentCommitCount()));
+ setBitmapRecentCommitSpan(rc.getInt("pack", "bitmaprecentcommitspan", //$NON-NLS-1$ //$NON-NLS-2$
+ getBitmapRecentCommitSpan()));
+ setBitmapDistantCommitSpan(rc.getInt("pack", "bitmapdistantcommitspan", //$NON-NLS-1$ //$NON-NLS-2$
+ getBitmapDistantCommitSpan()));
+ setBitmapExcessiveBranchCount(rc.getInt("pack", //$NON-NLS-1$
+ "bitmapexcessivebranchcount", getBitmapExcessiveBranchCount())); //$NON-NLS-1$
+ setBitmapInactiveBranchAgeInDays(
+ rc.getInt("pack", "bitmapinactivebranchageindays", //$NON-NLS-1$ //$NON-NLS-2$
+ getBitmapInactiveBranchAgeInDays()));
}
public String toString() {
final StringBuilder b = new StringBuilder();
b.append("maxDeltaDepth=").append(getMaxDeltaDepth()); //$NON-NLS-1$
b.append(", deltaSearchWindowSize=").append(getDeltaSearchWindowSize()); //$NON-NLS-1$
- b.append(", deltaSearchMemoryLimit=").append(getDeltaSearchMemoryLimit()); //$NON-NLS-1$
+ b.append(", deltaSearchMemoryLimit=") //$NON-NLS-1$
+ .append(getDeltaSearchMemoryLimit());
b.append(", deltaCacheSize=").append(getDeltaCacheSize()); //$NON-NLS-1$
b.append(", deltaCacheLimit=").append(getDeltaCacheLimit()); //$NON-NLS-1$
b.append(", compressionLevel=").append(getCompressionLevel()); //$NON-NLS-1$
@@ -735,6 +985,18 @@ public class PackConfig {
b.append(", reuseObjects=").append(isReuseObjects()); //$NON-NLS-1$
b.append(", deltaCompress=").append(isDeltaCompress()); //$NON-NLS-1$
b.append(", buildBitmaps=").append(isBuildBitmaps()); //$NON-NLS-1$
+ b.append(", bitmapContiguousCommitCount=") //$NON-NLS-1$
+ .append(getBitmapContiguousCommitCount());
+ b.append(", bitmapRecentCommitCount=") //$NON-NLS-1$
+ .append(getBitmapRecentCommitCount());
+ b.append(", bitmapRecentCommitSpan=") //$NON-NLS-1$
+ .append(getBitmapRecentCommitSpan());
+ b.append(", bitmapDistantCommitSpan=") //$NON-NLS-1$
+ .append(getBitmapDistantCommitSpan());
+ b.append(", bitmapExcessiveBranchCount=") //$NON-NLS-1$
+ .append(getBitmapExcessiveBranchCount());
+ b.append(", bitmapInactiveBranchAge=") //$NON-NLS-1$
+ .append(getBitmapInactiveBranchAgeInDays());
return b.toString();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java
new file mode 100644
index 0000000..a811fe3
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java
@@ -0,0 +1,491 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.storage.pack;
+
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
+import static org.eclipse.jgit.lib.Constants.OBJ_TAG;
+import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
+
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.pack.CachedPack;
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * Statistics about {@link org.eclipse.jgit.internal.storage.pack.PackWriter}
+ * pack creation.
+ *
+ * @since 4.1
+ */
+public class PackStatistics {
+ /**
+ * Statistics about a single type of object (commits, tags, trees and
+ * blobs).
+ */
+ public static class ObjectType {
+ /**
+ * POJO for accumulating the ObjectType statistics.
+ */
+ public static class Accumulator {
+ /** Count of objects of this type. */
+ public long cntObjects;
+
+ /** Count of deltas of this type. */
+ public long cntDeltas;
+
+ /** Count of reused objects of this type. */
+ public long reusedObjects;
+
+ /** Count of reused deltas of this type. */
+ public long reusedDeltas;
+
+ /** Count of bytes for all objects of this type. */
+ public long bytes;
+
+ /** Count of delta bytes for objects of this type. */
+ public long deltaBytes;
+ }
+
+ private ObjectType.Accumulator objectType;
+
+ /**
+ * Creates a new {@link ObjectType} object from the accumulator.
+ *
+ * @param accumulator
+ * the accumulator of the statistics
+ */
+ public ObjectType(ObjectType.Accumulator accumulator) {
+ /*
+ * For efficiency this wraps and serves up the Accumulator object
+ * rather than making a deep clone. Normal usage of PackWriter is to
+ * create a single pack/index/bitmap and only call getStatistics()
+ * after all work is complete.
+ */
+ objectType = accumulator;
+ }
+
+ /**
+ * @return total number of objects output. This total includes the value
+ * of {@link #getDeltas()}.
+ */
+ public long getObjects() {
+ return objectType.cntObjects;
+ }
+
+ /**
+ * @return total number of deltas output. This may be lower than the
+ * actual number of deltas if a cached pack was reused.
+ */
+ public long getDeltas() {
+ return objectType.cntDeltas;
+ }
+
+ /**
+ * @return number of objects whose existing representation was reused in
+ * the output. This count includes {@link #getReusedDeltas()}.
+ */
+ public long getReusedObjects() {
+ return objectType.reusedObjects;
+ }
+
+ /**
+ * @return number of deltas whose existing representation was reused in
+ * the output, as their base object was also output or was
+ * assumed present for a thin pack. This may be lower than the
+ * actual number of reused deltas if a cached pack was reused.
+ */
+ public long getReusedDeltas() {
+ return objectType.reusedDeltas;
+ }
+
+ /**
+ * @return total number of bytes written. This size includes the object
+ * headers as well as the compressed data. This size also
+ * includes all of {@link #getDeltaBytes()}.
+ */
+ public long getBytes() {
+ return objectType.bytes;
+ }
+
+ /**
+ * @return number of delta bytes written. This size includes the object
+ * headers for the delta objects.
+ */
+ public long getDeltaBytes() {
+ return objectType.deltaBytes;
+ }
+ }
+
+ /**
+ * POJO for accumulating the statistics.
+ */
+ public static class Accumulator {
+ /** The set of objects to be included in the pack. */
+ public Set<ObjectId> interestingObjects;
+
+ /** The set of objects to be excluded from the pack. */
+ public Set<ObjectId> uninterestingObjects;
+
+ /** The set of shallow commits on the client. */
+ public Set<ObjectId> clientShallowCommits;
+
+ /** The collection of reused packs in the upload. */
+ public List<CachedPack> reusedPacks;
+
+ /** Commits with no parents. */
+ public Set<ObjectId> rootCommits;
+
+ /** If a shallow pack, the depth in commits. */
+ public int depth;
+
+ /**
+ * The count of objects in the pack that went through the delta search
+ * process in order to find a potential delta base.
+ */
+ public int deltaSearchNonEdgeObjects;
+
+ /**
+ * The count of objects in the pack that went through delta base search
+ * and found a suitable base. This is a subset of
+ * deltaSearchNonEdgeObjects.
+ */
+ public int deltasFound;
+
+ /** The total count of objects in the pack. */
+ public long totalObjects;
+
+ /**
+ * The count of objects that needed to be discovered through an object
+ * walk because they were not found in bitmap indices.
+ */
+ public long bitmapIndexMisses;
+
+ /** The total count of deltas output. */
+ public long totalDeltas;
+
+ /** The count of reused objects in the pack. */
+ public long reusedObjects;
+
+ /** The count of reused deltas in the pack. */
+ public long reusedDeltas;
+
+ /** The count of total bytes in the pack. */
+ public long totalBytes;
+
+ /** The size of the thin pack in bytes, if a thin pack was generated. */
+ public long thinPackBytes;
+
+ /** Time in ms spent counting the objects that will go into the pack. */
+ public long timeCounting;
+
+ /** Time in ms spent searching for objects to reuse. */
+ public long timeSearchingForReuse;
+
+ /** Time in ms spent searching for sizes of objects. */
+ public long timeSearchingForSizes;
+
+ /** Time in ms spent compressing the pack. */
+ public long timeCompressing;
+
+ /** Time in ms spent writing the pack. */
+ public long timeWriting;
+
+ /**
+ * Statistics about each object type in the pack (commits, tags, trees
+ * and blobs.)
+ */
+ public ObjectType.Accumulator[] objectTypes;
+
+ {
+ objectTypes = new ObjectType.Accumulator[5];
+ objectTypes[OBJ_COMMIT] = new ObjectType.Accumulator();
+ objectTypes[OBJ_TREE] = new ObjectType.Accumulator();
+ objectTypes[OBJ_BLOB] = new ObjectType.Accumulator();
+ objectTypes[OBJ_TAG] = new ObjectType.Accumulator();
+ }
+ }
+
+ private Accumulator statistics;
+
+ /**
+ * Creates a new {@link PackStatistics} object from the accumulator.
+ *
+ * @param accumulator
+ * the accumulator of the statistics
+ */
+ public PackStatistics(Accumulator accumulator) {
+ /*
+ * For efficiency this wraps and serves up the Accumulator object rather
+ * than making a deep clone. Normal usage of PackWriter is to create a
+ * single pack/index/bitmap and only call getStatistics() after all work
+ * is complete.
+ */
+ statistics = accumulator;
+ }
+
+ /**
+ * @return unmodifiable collection of objects to be included in the pack.
+ * May be {@code null} if the pack was hand-crafted in a unit test.
+ */
+ public Set<ObjectId> getInterestingObjects() {
+ return statistics.interestingObjects;
+ }
+
+ /**
+ * @return unmodifiable collection of objects that should be excluded from
+ * the pack, as the peer that will receive the pack already has
+ * these objects.
+ */
+ public Set<ObjectId> getUninterestingObjects() {
+ return statistics.uninterestingObjects;
+ }
+
+ /**
+ * @return unmodifiable collection of objects that were shallow commits on
+ * the client.
+ */
+ public Set<ObjectId> getClientShallowCommits() {
+ return statistics.clientShallowCommits;
+ }
+
+ /**
+ * @return unmodifiable list of the cached packs that were reused in the
+ * output, if any were selected for reuse.
+ */
+ public List<CachedPack> getReusedPacks() {
+ return statistics.reusedPacks;
+ }
+
+ /** @return unmodifiable collection of the root commits of the history. */
+ public Set<ObjectId> getRootCommits() {
+ return statistics.rootCommits;
+ }
+
+ /**
+ * @return number of objects in the output pack that went through the delta
+ * search process in order to find a potential delta base.
+ */
+ public int getDeltaSearchNonEdgeObjects() {
+ return statistics.deltaSearchNonEdgeObjects;
+ }
+
+ /**
+ * @return number of objects in the output pack that went through delta base
+ * search and found a suitable base. This is a subset of
+ * {@link #getDeltaSearchNonEdgeObjects()}.
+ */
+ public int getDeltasFound() {
+ return statistics.deltasFound;
+ }
+
+ /**
+ * @return total number of objects output. This total includes the value of
+ * {@link #getTotalDeltas()}.
+ */
+ public long getTotalObjects() {
+ return statistics.totalObjects;
+ }
+
+ /**
+ * @return the count of objects that needed to be discovered through an
+ * object walk because they were not found in bitmap indices.
+ * Returns -1 if no bitmap indices were found.
+ */
+ public long getBitmapIndexMisses() {
+ return statistics.bitmapIndexMisses;
+ }
+
+ /**
+ * @return total number of deltas output. This may be lower than the actual
+ * number of deltas if a cached pack was reused.
+ */
+ public long getTotalDeltas() {
+ return statistics.totalDeltas;
+ }
+
+ /**
+ * @return number of objects whose existing representation was reused in the
+ * output. This count includes {@link #getReusedDeltas()}.
+ */
+ public long getReusedObjects() {
+ return statistics.reusedObjects;
+ }
+
+ /**
+ * @return number of deltas whose existing representation was reused in the
+ * output, as their base object was also output or was assumed
+ * present for a thin pack. This may be lower than the actual number
+ * of reused deltas if a cached pack was reused.
+ */
+ public long getReusedDeltas() {
+ return statistics.reusedDeltas;
+ }
+
+ /**
+ * @return total number of bytes written. This size includes the pack
+ * header, trailer, thin pack, and reused cached pack(s).
+ */
+ public long getTotalBytes() {
+ return statistics.totalBytes;
+ }
+
+ /**
+ * @return size of the thin pack in bytes, if a thin pack was generated. A
+ * thin pack is created when the client already has objects and some
+ * deltas are created against those objects, or if a cached pack is
+ * being used and some deltas will reference objects in the cached
+ * pack. This size does not include the pack header or trailer.
+ */
+ public long getThinPackBytes() {
+ return statistics.thinPackBytes;
+ }
+
+ /**
+ * @param typeCode
+ * object type code, e.g. OBJ_COMMIT or OBJ_TREE.
+ * @return information about this type of object in the pack.
+ */
+ public ObjectType byObjectType(int typeCode) {
+ return new ObjectType(statistics.objectTypes[typeCode]);
+ }
+
+ /** @return true if the resulting pack file was a shallow pack. */
+ public boolean isShallow() {
+ return statistics.depth > 0;
+ }
+
+ /** @return depth (in commits) the pack includes if shallow. */
+ public int getDepth() {
+ return statistics.depth;
+ }
+
+ /**
+ * @return time in milliseconds spent enumerating the objects that need to
+ * be included in the output. This time includes any restarts that
+ * occur when a cached pack is selected for reuse.
+ */
+ public long getTimeCounting() {
+ return statistics.timeCounting;
+ }
+
+ /**
+ * @return time in milliseconds spent matching existing representations
+ * against objects that will be transmitted, or that the client can
+ * be assumed to already have.
+ */
+ public long getTimeSearchingForReuse() {
+ return statistics.timeSearchingForReuse;
+ }
+
+ /**
+ * @return time in milliseconds spent finding the sizes of all objects that
+ * will enter the delta compression search window. The sizes need to
+ * be known to better match similar objects together and improve
+ * delta compression ratios.
+ */
+ public long getTimeSearchingForSizes() {
+ return statistics.timeSearchingForSizes;
+ }
+
+ /**
+ * @return time in milliseconds spent on delta compression. This is observed
+ * wall-clock time and does not accurately track CPU time used when
+ * multiple threads were used to perform the delta compression.
+ */
+ public long getTimeCompressing() {
+ return statistics.timeCompressing;
+ }
+
+ /**
+ * @return time in milliseconds spent writing the pack output, from start of
+ * header until end of trailer. The transfer speed can be
+ * approximated by dividing {@link #getTotalBytes()} by this value.
+ */
+ public long getTimeWriting() {
+ return statistics.timeWriting;
+ }
+
+ /** @return total time spent processing this pack. */
+ public long getTimeTotal() {
+ return statistics.timeCounting + statistics.timeSearchingForReuse
+ + statistics.timeSearchingForSizes + statistics.timeCompressing
+ + statistics.timeWriting;
+ }
+
+ /**
+ * @return get the average output speed in terms of bytes-per-second.
+ * {@code getTotalBytes() / (getTimeWriting() / 1000.0)}.
+ */
+ public double getTransferRate() {
+ return getTotalBytes() / (getTimeWriting() / 1000.0);
+ }
+
+ /** @return formatted message string for display to clients. */
+ public String getMessage() {
+ return MessageFormat.format(JGitText.get().packWriterStatistics,
+ Long.valueOf(statistics.totalObjects),
+ Long.valueOf(statistics.totalDeltas),
+ Long.valueOf(statistics.reusedObjects),
+ Long.valueOf(statistics.reusedDeltas));
+ }
+
+ /** @return a map containing ObjectType statistics. */
+ public Map<Integer, ObjectType> getObjectTypes() {
+ HashMap<Integer, ObjectType> map = new HashMap<>();
+ map.put(Integer.valueOf(OBJ_BLOB), new ObjectType(
+ statistics.objectTypes[OBJ_BLOB]));
+ map.put(Integer.valueOf(OBJ_COMMIT), new ObjectType(
+ statistics.objectTypes[OBJ_COMMIT]));
+ map.put(Integer.valueOf(OBJ_TAG), new ObjectType(
+ statistics.objectTypes[OBJ_TAG]));
+ map.put(Integer.valueOf(OBJ_TREE), new ObjectType(
+ statistics.objectTypes[OBJ_TREE]));
+ return map;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java
index 5db3378..6263d4b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java
@@ -76,7 +76,7 @@ import org.eclipse.jgit.util.FS;
/**
* Walker that visits all submodule entries found in a tree
*/
-public class SubmoduleWalk {
+public class SubmoduleWalk implements AutoCloseable {
/**
* The values for the config param submodule.<name>.ignore
@@ -122,7 +122,7 @@ public class SubmoduleWalk {
DirCache index = repository.readDirCache();
generator.setTree(new DirCacheIterator(index));
} catch (IOException e) {
- generator.release();
+ generator.close();
throw e;
}
return generator;
@@ -152,10 +152,10 @@ public class SubmoduleWalk {
if (filter.isDone(generator.walk))
return generator;
} catch (IOException e) {
- generator.release();
+ generator.close();
throw e;
}
- generator.release();
+ generator.close();
return null;
}
@@ -183,10 +183,10 @@ public class SubmoduleWalk {
if (filter.isDone(generator.walk))
return generator;
} catch (IOException e) {
- generator.release();
+ generator.close();
throw e;
}
- generator.release();
+ generator.close();
return null;
}
@@ -419,8 +419,7 @@ public class SubmoduleWalk {
config.load();
modulesConfig = config;
} else {
- TreeWalk configWalk = new TreeWalk(repository);
- try {
+ try (TreeWalk configWalk = new TreeWalk(repository)) {
configWalk.addTree(rootTree);
// The root tree may be part of the submodule walk, so we need to revert
@@ -446,21 +445,20 @@ public class SubmoduleWalk {
if (idx > 0)
rootTree.next(idx);
}
- } finally {
- configWalk.release();
}
}
return this;
}
/**
- * Checks whether the working tree (or the index in case of a bare repo)
- * contains a .gitmodules file. That's a hint that the repo contains
- * submodules.
+ * Checks whether the working tree contains a .gitmodules file. That's a
+ * hint that the repo contains submodules.
*
* @param repository
* the repository to check
- * @return <code>true</code> if the repo contains a .gitmodules file
+ * @return <code>true</code> if the working tree contains a .gitmodules file,
+ * <code>false</code> otherwise. Always returns <code>false</code>
+ * for bare repositories.
* @throws IOException
* @throws CorruptObjectException
* @since 3.6
@@ -468,8 +466,7 @@ public class SubmoduleWalk {
public static boolean containsGitModulesFile(Repository repository)
throws IOException {
if (repository.isBare()) {
- DirCache dc = repository.readDirCache();
- return (dc.findEntry(Constants.DOT_GIT_MODULES) >= 0);
+ return false;
}
File modulesFile = new File(repository.getWorkTree(),
Constants.DOT_GIT_MODULES);
@@ -729,8 +726,13 @@ public class SubmoduleWalk {
return url != null ? getSubmoduleRemoteUrl(repository, url) : null;
}
- /** Release any resources used by this walker's reader. */
- public void release() {
- walk.release();
+ /**
+ * Release any resources used by this walker's reader.
+ *
+ * @since 4.0
+ */
+ @Override
+ public void close() {
+ walk.close();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java
index 722bfc4..4069a64 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java
@@ -56,10 +56,10 @@ import java.net.ProxySelector;
import java.net.URL;
import java.net.URLConnection;
import java.security.DigestOutputStream;
+import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
-import java.security.spec.InvalidKeySpecException;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -115,8 +115,6 @@ public class AmazonS3 {
private static final String HMAC = "HmacSHA1"; //$NON-NLS-1$
- private static final String DOMAIN = "s3.amazonaws.com"; //$NON-NLS-1$
-
private static final String X_AMZ_ACL = "x-amz-acl"; //$NON-NLS-1$
private static final String X_AMZ_META = "x-amz-meta-"; //$NON-NLS-1$
@@ -177,7 +175,7 @@ public class AmazonS3 {
private final String acl;
/** Maximum number of times to try an operation. */
- private final int maxAttempts;
+ final int maxAttempts;
/** Encryption algorithm, may be a null instance that provides pass-through. */
private final WalkEncryption encryption;
@@ -185,6 +183,22 @@ public class AmazonS3 {
/** Directory for locally buffered content. */
private final File tmpDir;
+ /** S3 Bucket Domain. */
+ private final String domain;
+
+ /** Property names used in amazon connection configuration file. */
+ interface Keys {
+ String ACCESS_KEY = "accesskey"; //$NON-NLS-1$
+ String SECRET_KEY = "secretkey"; //$NON-NLS-1$
+ String PASSWORD = "password"; //$NON-NLS-1$
+ String CRYPTO_ALG = "crypto.algorithm"; //$NON-NLS-1$
+ String CRYPTO_VER = "crypto.version"; //$NON-NLS-1$
+ String ACL = "acl"; //$NON-NLS-1$
+ String DOMAIN = "domain"; //$NON-NLS-1$
+ String HTTP_RETRY = "httpclient.retry-max"; //$NON-NLS-1$
+ String TMP_DIR = "tmpdir"; //$NON-NLS-1$
+ }
+
/**
* Create a new S3 client for the supplied user information.
* <p>
@@ -201,6 +215,10 @@ public class AmazonS3 {
* # PRIVATE, PUBLIC_READ (defaults to PRIVATE).
* acl: PRIVATE
*
+ * # S3 Domain
+ * # AWS S3 Region Domain (defaults to s3.amazonaws.com)
+ * domain: s3.amazonaws.com
+ *
* # Number of times to retry after internal error from S3.
* httpclient.retry-max: 3
*
@@ -214,16 +232,18 @@ public class AmazonS3 {
*
*/
public AmazonS3(final Properties props) {
- publicKey = props.getProperty("accesskey"); //$NON-NLS-1$
+ domain = props.getProperty(Keys.DOMAIN, "s3.amazonaws.com"); //$NON-NLS-1$
+
+ publicKey = props.getProperty(Keys.ACCESS_KEY);
if (publicKey == null)
throw new IllegalArgumentException(JGitText.get().missingAccesskey);
- final String secret = props.getProperty("secretkey"); //$NON-NLS-1$
+ final String secret = props.getProperty(Keys.SECRET_KEY);
if (secret == null)
throw new IllegalArgumentException(JGitText.get().missingSecretkey);
privateKey = new SecretKeySpec(Constants.encodeASCII(secret), HMAC);
- final String pacl = props.getProperty("acl", "PRIVATE"); //$NON-NLS-1$ //$NON-NLS-2$
+ final String pacl = props.getProperty(Keys.ACL, "PRIVATE"); //$NON-NLS-1$
if (StringUtils.equalsIgnoreCase("PRIVATE", pacl)) //$NON-NLS-1$
acl = "private"; //$NON-NLS-1$
else if (StringUtils.equalsIgnoreCase("PUBLIC", pacl)) //$NON-NLS-1$
@@ -236,26 +256,16 @@ public class AmazonS3 {
throw new IllegalArgumentException("Invalid acl: " + pacl); //$NON-NLS-1$
try {
- final String cPas = props.getProperty("password"); //$NON-NLS-1$
- if (cPas != null) {
- String cAlg = props.getProperty("crypto.algorithm"); //$NON-NLS-1$
- if (cAlg == null)
- cAlg = "PBEWithMD5AndDES"; //$NON-NLS-1$
- encryption = new WalkEncryption.ObjectEncryptionV2(cAlg, cPas);
- } else {
- encryption = WalkEncryption.NONE;
- }
- } catch (InvalidKeySpecException e) {
- throw new IllegalArgumentException(JGitText.get().invalidEncryption, e);
- } catch (NoSuchAlgorithmException e) {
+ encryption = WalkEncryption.instance(props);
+ } catch (GeneralSecurityException e) {
throw new IllegalArgumentException(JGitText.get().invalidEncryption, e);
}
- maxAttempts = Integer.parseInt(props.getProperty(
- "httpclient.retry-max", "3")); //$NON-NLS-1$ //$NON-NLS-2$
+ maxAttempts = Integer
+ .parseInt(props.getProperty(Keys.HTTP_RETRY, "3")); //$NON-NLS-1$
proxySelector = ProxySelector.getDefault();
- String tmp = props.getProperty("tmpdir"); //$NON-NLS-1$
+ String tmp = props.getProperty(Keys.TMP_DIR);
tmpDir = tmp != null && tmp.length() > 0 ? new File(tmp) : null;
}
@@ -286,10 +296,10 @@ public class AmazonS3 {
case HttpURLConnection.HTTP_INTERNAL_ERROR:
continue;
default:
- throw error("Reading", key, c);
+ throw error(JGitText.get().s3ActionReading, key, c);
}
}
- throw maxAttempts("Reading", key);
+ throw maxAttempts(JGitText.get().s3ActionReading, key);
}
/**
@@ -359,10 +369,10 @@ public class AmazonS3 {
case HttpURLConnection.HTTP_INTERNAL_ERROR:
continue;
default:
- throw error("Deletion", key, c);
+ throw error(JGitText.get().s3ActionDeletion, key, c);
}
}
- throw maxAttempts("Deletion", key);
+ throw maxAttempts(JGitText.get().s3ActionDeletion, key);
}
/**
@@ -420,10 +430,10 @@ public class AmazonS3 {
case HttpURLConnection.HTTP_INTERNAL_ERROR:
continue;
default:
- throw error("Writing", key, c);
+ throw error(JGitText.get().s3ActionWriting, key, c);
}
}
- throw maxAttempts("Writing", key);
+ throw maxAttempts(JGitText.get().s3ActionWriting, key);
}
/**
@@ -473,7 +483,7 @@ public class AmazonS3 {
return encryption.encrypt(new DigestOutputStream(buffer, md5));
}
- private void putImpl(final String bucket, final String key,
+ void putImpl(final String bucket, final String key,
final byte[] csum, final TemporaryBuffer buf,
ProgressMonitor monitor, String monitorTask) throws IOException {
if (monitor == null)
@@ -483,16 +493,14 @@ public class AmazonS3 {
final String md5str = Base64.encodeBytes(csum);
final long len = buf.length();
- final String lenstr = String.valueOf(len);
for (int curAttempt = 0; curAttempt < maxAttempts; curAttempt++) {
final HttpURLConnection c = open("PUT", bucket, key); //$NON-NLS-1$
- c.setRequestProperty("Content-Length", lenstr); //$NON-NLS-1$
+ c.setFixedLengthStreamingMode(len);
c.setRequestProperty("Content-MD5", md5str); //$NON-NLS-1$
c.setRequestProperty(X_AMZ_ACL, acl);
encryption.request(c, X_AMZ_META);
authorize(c);
c.setDoOutput(true);
- c.setFixedLengthStreamingMode((int) len);
monitor.beginTask(monitorTask, (int) (len / 1024));
final OutputStream os = c.getOutputStream();
try {
@@ -508,13 +516,13 @@ public class AmazonS3 {
case HttpURLConnection.HTTP_INTERNAL_ERROR:
continue;
default:
- throw error("Writing", key, c);
+ throw error(JGitText.get().s3ActionWriting, key, c);
}
}
- throw maxAttempts("Writing", key);
+ throw maxAttempts(JGitText.get().s3ActionWriting, key);
}
- private IOException error(final String action, final String key,
+ IOException error(final String action, final String key,
final HttpURLConnection c) throws IOException {
final IOException err = new IOException(MessageFormat.format(
JGitText.get().amazonS3ActionFailed, action, key,
@@ -539,7 +547,7 @@ public class AmazonS3 {
return err;
}
- private IOException maxAttempts(final String action, final String key) {
+ IOException maxAttempts(final String action, final String key) {
return new IOException(MessageFormat.format(
JGitText.get().amazonS3ActionFailedGivingUp, action, key,
Integer.valueOf(maxAttempts)));
@@ -551,14 +559,14 @@ public class AmazonS3 {
return open(method, bucket, key, noArgs);
}
- private HttpURLConnection open(final String method, final String bucket,
+ HttpURLConnection open(final String method, final String bucket,
final String key, final Map<String, String> args)
throws IOException {
final StringBuilder urlstr = new StringBuilder();
urlstr.append("http://"); //$NON-NLS-1$
urlstr.append(bucket);
urlstr.append('.');
- urlstr.append(DOMAIN);
+ urlstr.append(domain);
urlstr.append('/');
if (key.length() > 0)
HttpSupport.encode(urlstr, key);
@@ -588,7 +596,7 @@ public class AmazonS3 {
return c;
}
- private void authorize(final HttpURLConnection c) throws IOException {
+ void authorize(final HttpURLConnection c) throws IOException {
final Map<String, List<String>> reqHdr = c.getRequestProperties();
final SortedMap<String, String> sigHdr = new TreeMap<String, String>();
for (final Map.Entry<String, List<String>> entry : reqHdr.entrySet()) {
@@ -619,7 +627,7 @@ public class AmazonS3 {
final String host = c.getURL().getHost();
s.append('/');
- s.append(host.substring(0, host.length() - DOMAIN.length() - 1));
+ s.append(host.substring(0, host.length() - domain.length() - 1));
s.append(c.getURL().getPath());
final String sec;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseConnection.java
index 1072d58..59ff1bd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseConnection.java
@@ -65,6 +65,8 @@ import org.eclipse.jgit.lib.Ref;
public abstract class BaseConnection implements Connection {
private Map<String, Ref> advertisedRefs = Collections.emptyMap();
+ private String peerUserAgent;
+
private boolean startedOperation;
private Writer messageWriter;
@@ -85,6 +87,28 @@ public abstract class BaseConnection implements Connection {
return messageWriter != null ? messageWriter.toString() : ""; //$NON-NLS-1$
}
+ /**
+ * User agent advertised by the remote server.
+ *
+ * @return agent (version of Git) running on the remote server. Null if the
+ * server does not advertise this version.
+ * @since 4.0
+ */
+ public String getPeerUserAgent() {
+ return peerUserAgent;
+ }
+
+ /**
+ * Remember the remote peer's agent.
+ *
+ * @param agent
+ * remote peer agent string.
+ * @since 4.0
+ */
+ protected void setPeerUserAgent(String agent) {
+ peerUserAgent = agent;
+ }
+
public abstract void close();
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
index 8f825ea..aa36aeb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
@@ -46,6 +46,8 @@
package org.eclipse.jgit.transport;
+import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
+
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
@@ -141,7 +143,9 @@ abstract class BasePackConnection extends BaseConnection {
final int timeout = transport.getTimeout();
if (timeout > 0) {
final Thread caller = Thread.currentThread();
- myTimer = new InterruptTimer(caller.getName() + "-Timer"); //$NON-NLS-1$
+ if (myTimer == null) {
+ myTimer = new InterruptTimer(caller.getName() + "-Timer"); //$NON-NLS-1$
+ }
timeoutIn = new TimeoutInputStream(myIn, myTimer);
timeoutOut = new TimeoutOutputStream(myOut, myTimer);
timeoutIn.setTimeout(timeout * 1000);
@@ -275,6 +279,18 @@ abstract class BasePackConnection extends BaseConnection {
return true;
}
+ protected void addUserAgentCapability(StringBuilder b) {
+ String a = UserAgent.get();
+ if (a != null && UserAgent.hasAgent(remoteCapablities)) {
+ b.append(' ').append(OPTION_AGENT).append('=').append(a);
+ }
+ }
+
+ @Override
+ public String getPeerUserAgent() {
+ return UserAgent.getAgent(remoteCapablities, super.getPeerUserAgent());
+ }
+
private PackProtocolException duplicateAdvertisement(final String name) {
return new PackProtocolException(uri, MessageFormat.format(JGitText.get().duplicateAdvertisementsOf, name));
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
index f907891..754cf36 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackFetchConnection.java
@@ -193,6 +193,13 @@ public abstract class BasePackFetchConnection extends BasePackConnection
*/
public static final String OPTION_ALLOW_TIP_SHA1_IN_WANT = GitProtocolConstants.OPTION_ALLOW_TIP_SHA1_IN_WANT;
+ /**
+ * The client supports fetching objects that are reachable from a tip of a
+ * ref that is allowed to fetch.
+ * @since 4.1
+ */
+ public static final String OPTION_ALLOW_REACHABLE_SHA1_IN_WANT = GitProtocolConstants.OPTION_ALLOW_REACHABLE_SHA1_IN_WANT;
+
private final RevWalk walk;
/** All commits that are immediately reachable by a local ref. */
@@ -377,7 +384,7 @@ public abstract class BasePackFetchConnection extends BasePackConnection
@Override
public void close() {
if (walk != null)
- walk.release();
+ walk.close();
super.close();
}
@@ -457,8 +464,12 @@ public abstract class BasePackFetchConnection extends BasePackConnection
final PacketLineOut p = statelessRPC ? pckState : pckOut;
boolean first = true;
for (final Ref r : want) {
+ ObjectId objectId = r.getObjectId();
+ if (objectId == null) {
+ continue;
+ }
try {
- if (walk.parseAny(r.getObjectId()).has(REACHABLE)) {
+ if (walk.parseAny(objectId).has(REACHABLE)) {
// We already have this object. Asking for it is
// not a very good idea.
//
@@ -471,7 +482,7 @@ public abstract class BasePackFetchConnection extends BasePackConnection
final StringBuilder line = new StringBuilder(46);
line.append("want "); //$NON-NLS-1$
- line.append(r.getObjectId().name());
+ line.append(objectId.name());
if (first) {
line.append(enableCapabilities());
first = false;
@@ -521,6 +532,7 @@ public abstract class BasePackFetchConnection extends BasePackConnection
OPTION_MULTI_ACK_DETAILED));
}
+ addUserAgentCapability(line);
return line.toString();
}
@@ -753,16 +765,13 @@ public abstract class BasePackFetchConnection extends BasePackConnection
input = new SideBandInputStream(input, monitor, getMessageWriter(),
outputStream);
- ObjectInserter ins = local.newObjectInserter();
- try {
+ try (ObjectInserter ins = local.newObjectInserter()) {
PackParser parser = ins.newPackParser(input);
parser.setAllowThin(thinPack);
parser.setObjectChecker(transport.getObjectChecker());
parser.setLockMessage(lockMessage);
packLock = parser.parse(monitor);
ins.flush();
- } finally {
- ins.release();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
index e367ab4..963de35 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
@@ -44,6 +44,8 @@
package org.eclipse.jgit.transport;
+import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_ATOMIC;
+
import java.io.IOException;
import java.io.OutputStream;
import java.text.MessageFormat;
@@ -55,6 +57,7 @@ import java.util.Set;
import org.eclipse.jgit.errors.NoRemoteRepositoryException;
import org.eclipse.jgit.errors.NotSupportedException;
import org.eclipse.jgit.errors.PackProtocolException;
+import org.eclipse.jgit.errors.TooLargePackException;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.pack.PackWriter;
@@ -109,17 +112,15 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
public static final String CAPABILITY_SIDE_BAND_64K = GitProtocolConstants.CAPABILITY_SIDE_BAND_64K;
private final boolean thinPack;
+ private final boolean atomic;
+ private boolean capableAtomic;
private boolean capableDeleteRefs;
-
private boolean capableReport;
-
private boolean capableSideBand;
-
private boolean capableOfsDelta;
private boolean sentCommand;
-
private boolean writePack;
/** Time in milliseconds spent transferring the pack data. */
@@ -134,6 +135,7 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
public BasePackPushConnection(final PackTransport packTransport) {
super(packTransport);
thinPack = transport.isPushThin();
+ atomic = transport.isPushAtomic();
}
public void push(final ProgressMonitor monitor,
@@ -223,6 +225,11 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
private void writeCommands(final Collection<RemoteRefUpdate> refUpdates,
final ProgressMonitor monitor, OutputStream outputStream) throws IOException {
final String capabilities = enableCapabilities(monitor, outputStream);
+ if (atomic && !capableAtomic) {
+ throw new TransportException(uri,
+ JGitText.get().atomicPushNotSupported);
+ }
+
for (final RemoteRefUpdate rru : refUpdates) {
if (!capableDeleteRefs && rru.isDelete()) {
rru.setStatus(Status.REJECTED_NODELETE);
@@ -230,9 +237,14 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
}
final StringBuilder sb = new StringBuilder();
- final Ref advertisedRef = getRef(rru.getRemoteName());
- final ObjectId oldId = (advertisedRef == null ? ObjectId.zeroId()
- : advertisedRef.getObjectId());
+ ObjectId oldId = rru.getExpectedOldObjectId();
+ if (oldId == null) {
+ final Ref advertised = getRef(rru.getRemoteName());
+ oldId = advertised != null ? advertised.getObjectId() : null;
+ if (oldId == null) {
+ oldId = ObjectId.zeroId();
+ }
+ }
sb.append(oldId.name());
sb.append(' ');
sb.append(rru.getNewObjectId().name());
@@ -258,6 +270,8 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
private String enableCapabilities(final ProgressMonitor monitor,
OutputStream outputStream) {
final StringBuilder line = new StringBuilder();
+ if (atomic)
+ capableAtomic = wantCapability(line, CAPABILITY_ATOMIC);
capableReport = wantCapability(line, CAPABILITY_REPORT_STATUS);
capableDeleteRefs = wantCapability(line, CAPABILITY_DELETE_REFS);
capableOfsDelta = wantCapability(line, CAPABILITY_OFS_DELTA);
@@ -268,6 +282,7 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
outputStream);
pckIn = new PacketLineIn(in);
}
+ addUserAgentCapability(line);
if (line.length() > 0)
line.setCharAt(0, '\0');
@@ -279,9 +294,8 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
Set<ObjectId> remoteObjects = new HashSet<ObjectId>();
Set<ObjectId> newObjects = new HashSet<ObjectId>();
- final PackWriter writer = new PackWriter(transport.getPackConfig(),
- local.newObjectReader());
- try {
+ try (final PackWriter writer = new PackWriter(transport.getPackConfig(),
+ local.newObjectReader())) {
for (final Ref r : getRefs()) {
// only add objects that we actually have
@@ -303,10 +317,9 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
writer.setDeltaBaseAsOffset(capableOfsDelta);
writer.preparePack(monitor, newObjects, remoteObjects);
writer.writePack(monitor, monitor, out);
- } finally {
- writer.release();
+
+ packTransferTime = writer.getStatistics().getTimeWriting();
}
- packTransferTime = writer.getStatistics().getTimeWriting();
}
private void readStatusReport(final Map<String, RemoteRefUpdate> refUpdates)
@@ -315,6 +328,9 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
if (!unpackLine.startsWith("unpack ")) //$NON-NLS-1$
throw new PackProtocolException(uri, MessageFormat.format(JGitText.get().unexpectedReportLine, unpackLine));
final String unpackStatus = unpackLine.substring("unpack ".length()); //$NON-NLS-1$
+ if (unpackStatus.startsWith("error Pack exceeds the limit of")) //$NON-NLS-1$
+ throw new TooLargePackException(uri,
+ unpackStatus.substring("error ".length())); //$NON-NLS-1$
if (!unpackStatus.equals("ok")) //$NON-NLS-1$
throw new TransportException(uri, MessageFormat.format(
JGitText.get().errorOccurredDuringUnpackingOnTheRemoteEnd, unpackStatus));
@@ -369,7 +385,8 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
final int oldTimeout = timeoutIn.getTimeout();
final int sendTime = (int) Math.min(packTransferTime, 28800000L);
try {
- timeoutIn.setTimeout(10 * Math.max(sendTime, oldTimeout));
+ int timeout = 10 * Math.max(sendTime, oldTimeout);
+ timeoutIn.setTimeout((timeout < 0) ? Integer.MAX_VALUE : timeout);
return pckIn.readString();
} finally {
timeoutIn.setTimeout(oldTimeout);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
index 0475d27..a20e652 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
@@ -46,8 +46,10 @@ package org.eclipse.jgit.transport;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_ATOMIC;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_DELETE_REFS;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_OFS_DELTA;
+import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_QUIET;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REPORT_STATUS;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SIDE_BAND_64K;
+import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
import static org.eclipse.jgit.transport.SideBandOutputStream.CH_DATA;
import static org.eclipse.jgit.transport.SideBandOutputStream.CH_PROGRESS;
import static org.eclipse.jgit.transport.SideBandOutputStream.MAX_BUF;
@@ -175,6 +177,7 @@ public abstract class BaseReceivePack {
private boolean allowNonFastForwards;
private boolean allowOfsDelta;
+ private boolean allowQuiet = true;
/** Identity to record action as within the reflog. */
private PersonIdent refLogIdent;
@@ -224,6 +227,7 @@ public abstract class BaseReceivePack {
/** Capabilities requested by the client. */
private Set<String> enabledCapabilities;
+ String userAgent;
private Set<ObjectId> clientShallowCommits;
private List<ReceiveCommand> commands;
@@ -232,6 +236,8 @@ public abstract class BaseReceivePack {
/** If {@link BasePackPushConnection#CAPABILITY_SIDE_BAND_64K} is enabled. */
private boolean sideBand;
+ private boolean quiet;
+
/** Lock around the received pack file, while updating refs. */
private PackLock packLock;
@@ -246,6 +252,37 @@ public abstract class BaseReceivePack {
/** The size of the received pack, including index size */
private Long packSize;
+ private PushCertificateParser pushCertificateParser;
+ private SignedPushConfig signedPushConfig;
+ private PushCertificate pushCert;
+
+ /**
+ * Get the push certificate used to verify the pusher's identity.
+ * <p>
+ * Only valid after commands are read from the wire.
+ *
+ * @return the parsed certificate, or null if push certificates are disabled
+ * or no cert was presented by the client.
+ * @since 4.1
+ */
+ public PushCertificate getPushCertificate() {
+ return pushCert;
+ }
+
+ /**
+ * Set the push certificate used to verify the pusher's identity.
+ * <p>
+ * Should only be called if reconstructing an instance without going through
+ * the normal {@link #recvCommands()} flow.
+ *
+ * @param cert
+ * the push certificate to set.
+ * @since 4.1
+ */
+ public void setPushCertificate(PushCertificate cert) {
+ pushCert = cert;
+ }
+
/**
* Create a new pack receive for an open repository.
*
@@ -256,17 +293,20 @@ public abstract class BaseReceivePack {
db = into;
walk = new RevWalk(db);
- final ReceiveConfig cfg = db.getConfig().get(ReceiveConfig.KEY);
- objectChecker = cfg.newObjectChecker();
- allowCreates = cfg.allowCreates;
+ TransferConfig tc = db.getConfig().get(TransferConfig.KEY);
+ objectChecker = tc.newReceiveObjectChecker();
+
+ ReceiveConfig rc = db.getConfig().get(ReceiveConfig.KEY);
+ allowCreates = rc.allowCreates;
allowAnyDeletes = true;
- allowBranchDeletes = cfg.allowDeletes;
- allowNonFastForwards = cfg.allowNonFastForwards;
- allowOfsDelta = cfg.allowOfsDelta;
+ allowBranchDeletes = rc.allowDeletes;
+ allowNonFastForwards = rc.allowNonFastForwards;
+ allowOfsDelta = rc.allowOfsDelta;
advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
refFilter = RefFilter.DEFAULT;
advertisedHaves = new HashSet<ObjectId>();
clientShallowCommits = new HashSet<ObjectId>();
+ signedPushConfig = rc.signedPush;
}
/** Configuration for receive operations. */
@@ -277,42 +317,20 @@ public abstract class BaseReceivePack {
}
};
- final boolean checkReceivedObjects;
- final boolean allowLeadingZeroFileMode;
- final boolean safeForWindows;
- final boolean safeForMacOS;
-
final boolean allowCreates;
final boolean allowDeletes;
final boolean allowNonFastForwards;
final boolean allowOfsDelta;
+ final SignedPushConfig signedPush;
ReceiveConfig(final Config config) {
- checkReceivedObjects = config.getBoolean(
- "receive", "fsckobjects", //$NON-NLS-1$ //$NON-NLS-2$
- config.getBoolean("transfer", "fsckobjects", false)); //$NON-NLS-1$ //$NON-NLS-2$
- allowLeadingZeroFileMode = checkReceivedObjects
- && config.getBoolean("fsck", "allowLeadingZeroFileMode", false); //$NON-NLS-1$ //$NON-NLS-2$
- safeForWindows = checkReceivedObjects
- && config.getBoolean("fsck", "safeForWindows", false); //$NON-NLS-1$ //$NON-NLS-2$
- safeForMacOS = checkReceivedObjects
- && config.getBoolean("fsck", "safeForMacOS", false); //$NON-NLS-1$ //$NON-NLS-2$
-
allowCreates = true;
allowDeletes = !config.getBoolean("receive", "denydeletes", false); //$NON-NLS-1$ //$NON-NLS-2$
allowNonFastForwards = !config.getBoolean("receive", //$NON-NLS-1$
"denynonfastforwards", false); //$NON-NLS-1$
allowOfsDelta = config.getBoolean("repack", "usedeltabaseoffset", //$NON-NLS-1$ //$NON-NLS-2$
true);
- }
-
- ObjectChecker newObjectChecker() {
- if (!checkReceivedObjects)
- return null;
- return new ObjectChecker()
- .setAllowLeadingZeroFileMode(allowLeadingZeroFileMode)
- .setSafeForWindows(safeForWindows)
- .setSafeForMacOS(safeForMacOS);
+ signedPush = SignedPushConfig.KEY.parse(config);
}
}
@@ -719,6 +737,84 @@ public abstract class BaseReceivePack {
return enabledCapabilities.contains(CAPABILITY_SIDE_BAND_64K);
}
+ /**
+ * @return true if clients may request avoiding noisy progress messages.
+ * @since 4.0
+ */
+ public boolean isAllowQuiet() {
+ return allowQuiet;
+ }
+
+ /**
+ * Configure if clients may request the server skip noisy messages.
+ *
+ * @param allow
+ * true to allow clients to request quiet behavior; false to
+ * refuse quiet behavior and send messages anyway. This may be
+ * necessary if processing is slow and the client-server network
+ * connection can timeout.
+ * @since 4.0
+ */
+ public void setAllowQuiet(boolean allow) {
+ allowQuiet = allow;
+ }
+
+ /**
+ * True if the client wants less verbose output.
+ *
+ * @return true if the client has requested the server to be less verbose.
+ * @throws RequestNotYetReadException
+ * if the client's request has not yet been read from the wire,
+ * so we do not know if they expect side-band. Note that the
+ * client may have already written the request, it just has not
+ * been read.
+ * @since 4.0
+ */
+ public boolean isQuiet() throws RequestNotYetReadException {
+ if (enabledCapabilities == null)
+ throw new RequestNotYetReadException();
+ return quiet;
+ }
+
+ /**
+ * Set the configuration for push certificate verification.
+ *
+ * @param cfg
+ * new configuration; if this object is null or its {@link
+ * SignedPushConfig#getCertNonceSeed()} is null, push certificate
+ * verification will be disabled.
+ * @since 4.1
+ */
+ public void setSignedPushConfig(SignedPushConfig cfg) {
+ signedPushConfig = cfg;
+ }
+
+ private PushCertificateParser getPushCertificateParser() {
+ if (pushCertificateParser == null) {
+ pushCertificateParser = new PushCertificateParser(db, signedPushConfig);
+ }
+ return pushCertificateParser;
+ }
+
+ /**
+ * Get the user agent of the client.
+ * <p>
+ * If the client is new enough to use {@code agent=} capability that value
+ * will be returned. Older HTTP clients may also supply their version using
+ * the HTTP {@code User-Agent} header. The capability overrides the HTTP
+ * header if both are available.
+ * <p>
+ * When an HTTP request has been received this method returns the HTTP
+ * {@code User-Agent} header value until capabilities have been parsed.
+ *
+ * @return user agent supplied by the client. Available only if the client
+ * is new enough to advertise its user agent.
+ * @since 4.0
+ */
+ public String getPeerUserAgent() {
+ return UserAgent.getAgent(enabledCapabilities, userAgent);
+ }
+
/** @return all of the command received by the current request. */
public List<ReceiveCommand> getAllCommands() {
return Collections.unmodifiableList(commands);
@@ -929,10 +1025,17 @@ public abstract class BaseReceivePack {
adv.advertiseCapability(CAPABILITY_SIDE_BAND_64K);
adv.advertiseCapability(CAPABILITY_DELETE_REFS);
adv.advertiseCapability(CAPABILITY_REPORT_STATUS);
+ if (allowQuiet)
+ adv.advertiseCapability(CAPABILITY_QUIET);
+ String nonce = getPushCertificateParser().getAdvertiseNonce();
+ if (nonce != null) {
+ adv.advertiseCapability(nonce);
+ }
if (db.getRefDatabase().performsAtomicTransactions())
adv.advertiseCapability(CAPABILITY_ATOMIC);
if (allowOfsDelta)
adv.advertiseCapability(CAPABILITY_OFS_DELTA);
+ adv.advertiseCapability(OPTION_AGENT, UserAgent.get());
adv.send(getAdvertisedOrDefaultRefs());
for (ObjectId obj : advertisedHaves)
adv.advertiseHave(obj);
@@ -947,51 +1050,94 @@ public abstract class BaseReceivePack {
* @throws IOException
*/
protected void recvCommands() throws IOException {
- for (;;) {
- String line;
- try {
- line = pckIn.readStringRaw();
- } catch (EOFException eof) {
- if (commands.isEmpty())
- return;
- throw eof;
- }
- if (line == PacketLineIn.END)
- break;
+ PushCertificateParser certParser = getPushCertificateParser();
+ FirstLine firstLine = null;
+ try {
+ for (;;) {
+ String line;
+ try {
+ line = pckIn.readString();
+ } catch (EOFException eof) {
+ if (commands.isEmpty())
+ return;
+ throw eof;
+ }
+ if (line == PacketLineIn.END) {
+ break;
+ }
- if (line.length() >= 48 && line.startsWith("shallow ")) { //$NON-NLS-1$
- clientShallowCommits.add(ObjectId.fromString(line.substring(8, 48)));
- continue;
- }
+ if (line.length() >= 48 && line.startsWith("shallow ")) { //$NON-NLS-1$
+ clientShallowCommits.add(ObjectId.fromString(line.substring(8, 48)));
+ continue;
+ }
- if (commands.isEmpty()) {
- final FirstLine firstLine = new FirstLine(line);
- enabledCapabilities = firstLine.getCapabilities();
- line = firstLine.getLine();
- }
+ if (firstLine == null) {
+ firstLine = new FirstLine(line);
+ enabledCapabilities = firstLine.getCapabilities();
+ line = firstLine.getLine();
- if (line.length() < 83) {
- final String m = JGitText.get().errorInvalidProtocolWantedOldNewRef;
- sendError(m);
- throw new PackProtocolException(m);
- }
+ if (line.equals(GitProtocolConstants.OPTION_PUSH_CERT)) {
+ certParser.receiveHeader(pckIn, !isBiDirectionalPipe());
+ continue;
+ }
+ }
- final ObjectId oldId = ObjectId.fromString(line.substring(0, 40));
- final ObjectId newId = ObjectId.fromString(line.substring(41, 81));
- final String name = line.substring(82);
- final ReceiveCommand cmd = new ReceiveCommand(oldId, newId, name);
- if (name.equals(Constants.HEAD)) {
- cmd.setResult(Result.REJECTED_CURRENT_BRANCH);
- } else {
- cmd.setRef(refs.get(cmd.getRefName()));
+ if (line.equals(PushCertificateParser.BEGIN_SIGNATURE)) {
+ certParser.receiveSignature(pckIn);
+ continue;
+ }
+
+ ReceiveCommand cmd;
+ try {
+ cmd = parseCommand(line);
+ } catch (PackProtocolException e) {
+ sendError(e.getMessage());
+ throw e;
+ }
+ if (cmd.getRefName().equals(Constants.HEAD)) {
+ cmd.setResult(Result.REJECTED_CURRENT_BRANCH);
+ } else {
+ cmd.setRef(refs.get(cmd.getRefName()));
+ }
+ commands.add(cmd);
+ if (certParser.enabled()) {
+ certParser.addCommand(cmd);
+ }
}
- commands.add(cmd);
+ pushCert = certParser.build();
+ } catch (PackProtocolException e) {
+ sendError(e.getMessage());
+ throw e;
+ }
+ }
+
+ static ReceiveCommand parseCommand(String line) throws PackProtocolException {
+ if (line == null || line.length() < 83) {
+ throw new PackProtocolException(
+ JGitText.get().errorInvalidProtocolWantedOldNewRef);
+ }
+ String oldStr = line.substring(0, 40);
+ String newStr = line.substring(41, 81);
+ ObjectId oldId, newId;
+ try {
+ oldId = ObjectId.fromString(oldStr);
+ newId = ObjectId.fromString(newStr);
+ } catch (IllegalArgumentException e) {
+ throw new PackProtocolException(
+ JGitText.get().errorInvalidProtocolWantedOldNewRef, e);
}
+ String name = line.substring(82);
+ if (!Repository.isValidRefName(name)) {
+ throw new PackProtocolException(
+ JGitText.get().errorInvalidProtocolWantedOldNewRef);
+ }
+ return new ReceiveCommand(oldId, newId, name);
}
/** Enable capabilities based on a previously read capabilities line. */
protected void enableCapabilities() {
sideBand = isCapabilityEnabled(CAPABILITY_SIDE_BAND_64K);
+ quiet = allowQuiet && isCapabilityEnabled(CAPABILITY_QUIET);
if (sideBand) {
OutputStream out = rawOut;
@@ -1039,11 +1185,10 @@ public abstract class BaseReceivePack {
ProgressMonitor receiving = NullProgressMonitor.INSTANCE;
ProgressMonitor resolving = NullProgressMonitor.INSTANCE;
- if (sideBand)
+ if (sideBand && !quiet)
resolving = new SideBandProgressMonitor(msgOut);
- ObjectInserter ins = db.newObjectInserter();
- try {
+ try (ObjectInserter ins = db.newObjectInserter()) {
String lockMsg = "jgit receive-pack"; //$NON-NLS-1$
if (getRefLogIdent() != null)
lockMsg += " from " + getRefLogIdent().toExternalString(); //$NON-NLS-1$
@@ -1061,8 +1206,6 @@ public abstract class BaseReceivePack {
packLock = parser.parse(receiving, resolving);
packSize = Long.valueOf(parser.getPackSize());
ins.flush();
- } finally {
- ins.release();
}
if (timeoutIn != null)
@@ -1079,7 +1222,7 @@ public abstract class BaseReceivePack {
ObjectIdSubclassMap<ObjectId> baseObjects = null;
ObjectIdSubclassMap<ObjectId> providedObjects = null;
ProgressMonitor checking = NullProgressMonitor.INSTANCE;
- if (sideBand) {
+ if (sideBand && !quiet) {
SideBandProgressMonitor m = new SideBandProgressMonitor(msgOut);
m.setDelayStart(750, TimeUnit.MILLISECONDS);
checking = m;
@@ -1091,67 +1234,68 @@ public abstract class BaseReceivePack {
}
parser = null;
- final ObjectWalk ow = new ObjectWalk(db);
- ow.setRetainBody(false);
- if (baseObjects != null) {
- ow.sort(RevSort.TOPO);
- if (!baseObjects.isEmpty())
- ow.sort(RevSort.BOUNDARY, true);
- }
-
- for (final ReceiveCommand cmd : commands) {
- if (cmd.getResult() != Result.NOT_ATTEMPTED)
- continue;
- if (cmd.getType() == ReceiveCommand.Type.DELETE)
- continue;
- ow.markStart(ow.parseAny(cmd.getNewId()));
- }
- for (final ObjectId have : advertisedHaves) {
- RevObject o = ow.parseAny(have);
- ow.markUninteresting(o);
-
- if (baseObjects != null && !baseObjects.isEmpty()) {
- o = ow.peel(o);
- if (o instanceof RevCommit)
- o = ((RevCommit) o).getTree();
- if (o instanceof RevTree)
- ow.markUninteresting(o);
+ try (final ObjectWalk ow = new ObjectWalk(db)) {
+ if (baseObjects != null) {
+ ow.sort(RevSort.TOPO);
+ if (!baseObjects.isEmpty())
+ ow.sort(RevSort.BOUNDARY, true);
}
- }
- checking.beginTask(JGitText.get().countingObjects, ProgressMonitor.UNKNOWN);
- RevCommit c;
- while ((c = ow.next()) != null) {
- checking.update(1);
- if (providedObjects != null //
- && !c.has(RevFlag.UNINTERESTING) //
- && !providedObjects.contains(c))
- throw new MissingObjectException(c, Constants.TYPE_COMMIT);
- }
+ for (final ReceiveCommand cmd : commands) {
+ if (cmd.getResult() != Result.NOT_ATTEMPTED)
+ continue;
+ if (cmd.getType() == ReceiveCommand.Type.DELETE)
+ continue;
+ ow.markStart(ow.parseAny(cmd.getNewId()));
+ }
+ for (final ObjectId have : advertisedHaves) {
+ RevObject o = ow.parseAny(have);
+ ow.markUninteresting(o);
+
+ if (baseObjects != null && !baseObjects.isEmpty()) {
+ o = ow.peel(o);
+ if (o instanceof RevCommit)
+ o = ((RevCommit) o).getTree();
+ if (o instanceof RevTree)
+ ow.markUninteresting(o);
+ }
+ }
- RevObject o;
- while ((o = ow.nextObject()) != null) {
- checking.update(1);
- if (o.has(RevFlag.UNINTERESTING))
- continue;
+ checking.beginTask(JGitText.get().countingObjects,
+ ProgressMonitor.UNKNOWN);
+ RevCommit c;
+ while ((c = ow.next()) != null) {
+ checking.update(1);
+ if (providedObjects != null //
+ && !c.has(RevFlag.UNINTERESTING) //
+ && !providedObjects.contains(c))
+ throw new MissingObjectException(c, Constants.TYPE_COMMIT);
+ }
- if (providedObjects != null) {
- if (providedObjects.contains(o))
+ RevObject o;
+ while ((o = ow.nextObject()) != null) {
+ checking.update(1);
+ if (o.has(RevFlag.UNINTERESTING))
continue;
- else
- throw new MissingObjectException(o, o.getType());
- }
- if (o instanceof RevBlob && !db.hasObject(o))
- throw new MissingObjectException(o, Constants.TYPE_BLOB);
- }
- checking.endTask();
+ if (providedObjects != null) {
+ if (providedObjects.contains(o))
+ continue;
+ else
+ throw new MissingObjectException(o, o.getType());
+ }
- if (baseObjects != null) {
- for (ObjectId id : baseObjects) {
- o = ow.parseAny(id);
- if (!o.has(RevFlag.UNINTERESTING))
- throw new MissingObjectException(o, o.getType());
+ if (o instanceof RevBlob && !db.hasObject(o))
+ throw new MissingObjectException(o, Constants.TYPE_BLOB);
+ }
+ checking.endTask();
+
+ if (baseObjects != null) {
+ for (ObjectId id : baseObjects) {
+ o = ow.parseAny(id);
+ if (!o.has(RevFlag.UNINTERESTING))
+ throw new MissingObjectException(o, o.getType());
+ }
}
}
}
@@ -1201,16 +1345,21 @@ public abstract class BaseReceivePack {
}
}
- if (cmd.getType() == ReceiveCommand.Type.DELETE && ref != null
- && !ObjectId.zeroId().equals(cmd.getOldId())
- && !ref.getObjectId().equals(cmd.getOldId())) {
- // Delete commands can be sent with the old id matching our
- // advertised value, *OR* with the old id being 0{40}. Any
- // other requested old id is invalid.
- //
- cmd.setResult(Result.REJECTED_OTHER_REASON,
- JGitText.get().invalidOldIdSent);
- continue;
+ if (cmd.getType() == ReceiveCommand.Type.DELETE && ref != null) {
+ ObjectId id = ref.getObjectId();
+ if (id == null) {
+ id = ObjectId.zeroId();
+ }
+ if (!ObjectId.zeroId().equals(cmd.getOldId())
+ && !id.equals(cmd.getOldId())) {
+ // Delete commands can be sent with the old id matching our
+ // advertised value, *OR* with the old id being 0{40}. Any
+ // other requested old id is invalid.
+ //
+ cmd.setResult(Result.REJECTED_OTHER_REASON,
+ JGitText.get().invalidOldIdSent);
+ continue;
+ }
}
if (cmd.getType() == ReceiveCommand.Type.UPDATE) {
@@ -1220,8 +1369,15 @@ public abstract class BaseReceivePack {
cmd.setResult(Result.REJECTED_OTHER_REASON, JGitText.get().noSuchRef);
continue;
}
+ ObjectId id = ref.getObjectId();
+ if (id == null) {
+ // We cannot update unborn branch
+ cmd.setResult(Result.REJECTED_OTHER_REASON,
+ JGitText.get().cannotUpdateUnbornBranch);
+ continue;
+ }
- if (!ref.getObjectId().equals(cmd.getOldId())) {
+ if (!id.equals(cmd.getOldId())) {
// A properly functioning client will send the same
// object id we advertised.
//
@@ -1297,10 +1453,7 @@ public abstract class BaseReceivePack {
* @since 3.6
*/
protected void failPendingCommands() {
- for (ReceiveCommand cmd : commands) {
- if (cmd.getResult() == Result.NOT_ATTEMPTED)
- cmd.setResult(Result.REJECTED_OTHER_REASON, JGitText.get().transactionAborted);
- }
+ ReceiveCommand.abort(commands);
}
/**
@@ -1334,6 +1487,7 @@ public abstract class BaseReceivePack {
batch.setRefLogMessage("push", true); //$NON-NLS-1$
batch.addCommand(toApply);
try {
+ batch.setPushCertificate(getPushCertificate());
batch.execute(walk, updating);
} catch (IOException err) {
for (ReceiveCommand cmd : toApply) {
@@ -1408,9 +1562,11 @@ public abstract class BaseReceivePack {
case REJECTED_MISSING_OBJECT:
if (cmd.getMessage() == null)
r.append("missing object(s)"); //$NON-NLS-1$
- else if (cmd.getMessage().length() == Constants.OBJECT_ID_STRING_LENGTH)
- r.append("object " + cmd.getMessage() + " missing"); //$NON-NLS-1$ //$NON-NLS-2$
- else
+ else if (cmd.getMessage().length() == Constants.OBJECT_ID_STRING_LENGTH) {
+ r.append("object "); //$NON-NLS-1$
+ r.append(cmd.getMessage());
+ r.append(" missing"); //$NON-NLS-1$
+ } else
r.append(cmd.getMessage());
break;
@@ -1474,7 +1630,7 @@ public abstract class BaseReceivePack {
* the pack could not be unlocked.
*/
protected void release() throws IOException {
- walk.release();
+ walk.close();
unlockPack();
timeoutIn = null;
rawIn = null;
@@ -1483,7 +1639,9 @@ public abstract class BaseReceivePack {
pckIn = null;
pckOut = null;
refs = null;
- enabledCapabilities = null;
+ // Keep the capabilities. If responses are sent after this release
+ // we need to remember at least whether sideband communication has to be
+ // used
commands = null;
if (timer != null) {
try {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java
index e3cfd22..8038fa4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleFetchConnection.java
@@ -161,16 +161,23 @@ class BundleFetchConnection extends BaseFetchConnection {
}
private String readLine(final byte[] hdrbuf) throws IOException {
- bin.mark(hdrbuf.length);
- final int cnt = bin.read(hdrbuf);
- int lf = 0;
- while (lf < cnt && hdrbuf[lf] != '\n')
- lf++;
- bin.reset();
- IO.skipFully(bin, lf);
- if (lf < cnt && hdrbuf[lf] == '\n')
- IO.skipFully(bin, 1);
- return RawParseUtils.decode(Constants.CHARSET, hdrbuf, 0, lf);
+ StringBuilder line = new StringBuilder();
+ boolean done = false;
+ while (!done) {
+ bin.mark(hdrbuf.length);
+ final int cnt = bin.read(hdrbuf);
+ int lf = 0;
+ while (lf < cnt && hdrbuf[lf] != '\n')
+ lf++;
+ bin.reset();
+ IO.skipFully(bin, lf);
+ if (lf < cnt && hdrbuf[lf] == '\n') {
+ IO.skipFully(bin, 1);
+ done = true;
+ }
+ line.append(RawParseUtils.decode(Constants.CHARSET, hdrbuf, 0, lf));
+ }
+ return line.toString();
}
public boolean didFetchTestConnectivity() {
@@ -183,16 +190,13 @@ class BundleFetchConnection extends BaseFetchConnection {
throws TransportException {
verifyPrerequisites();
try {
- ObjectInserter ins = transport.local.newObjectInserter();
- try {
+ try (ObjectInserter ins = transport.local.newObjectInserter()) {
PackParser parser = ins.newPackParser(bin);
parser.setAllowThin(true);
parser.setObjectChecker(transport.getObjectChecker());
parser.setLockMessage(lockMessage);
packLock = parser.parse(NullProgressMonitor.INSTANCE);
ins.flush();
- } finally {
- ins.release();
}
} catch (IOException err) {
close();
@@ -217,8 +221,7 @@ class BundleFetchConnection extends BaseFetchConnection {
if (prereqs.isEmpty())
return;
- final RevWalk rw = new RevWalk(transport.local);
- try {
+ try (final RevWalk rw = new RevWalk(transport.local)) {
final RevFlag PREREQ = rw.newFlag("PREREQ"); //$NON-NLS-1$
final RevFlag SEEN = rw.newFlag("SEEN"); //$NON-NLS-1$
@@ -281,8 +284,6 @@ class BundleFetchConnection extends BaseFetchConnection {
throw new MissingBundlePrerequisiteException(transport.uri,
missing);
}
- } finally {
- rw.release();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
index d0f005c..ca624c0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
@@ -92,6 +92,8 @@ public class BundleWriter {
private PackConfig packConfig;
+ private ObjectCountCallback callback;
+
/**
* Create a writer for a bundle.
*
@@ -188,14 +190,18 @@ public class BundleWriter {
* an error occurred reading a local object's data to include in
* the bundle, or writing compressed object data to the output
* stream.
+ * @throws WriteAbortedException
+ * the write operation is aborted by
+ * {@link ObjectCountCallback}.
*/
public void writeBundle(ProgressMonitor monitor, OutputStream os)
throws IOException {
PackConfig pc = packConfig;
if (pc == null)
pc = new PackConfig(db);
- PackWriter packWriter = new PackWriter(pc, db.newObjectReader());
- try {
+ try (PackWriter packWriter = new PackWriter(pc, db.newObjectReader())) {
+ packWriter.setObjectCountCallback(callback);
+
final HashSet<ObjectId> inc = new HashSet<ObjectId>();
final HashSet<ObjectId> exc = new HashSet<ObjectId>();
inc.addAll(include.values());
@@ -233,8 +239,26 @@ public class BundleWriter {
w.write('\n');
w.flush();
packWriter.writePack(monitor, monitor, os);
- } finally {
- packWriter.release();
}
}
+
+ /**
+ * Set the {@link ObjectCountCallback}.
+ * <p>
+ * It should be set before calling
+ * {@link #writeBundle(ProgressMonitor, OutputStream)}.
+ * <p>
+ * This callback will be passed on to
+ * {@link PackWriter#setObjectCountCallback}.
+ *
+ * @param callback
+ * the callback to set
+ *
+ * @return this object for chaining.
+ * @since 4.1
+ */
+ public BundleWriter setObjectCountCallback(ObjectCountCallback callback) {
+ this.callback = callback;
+ return this;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ChainingCredentialsProvider.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ChainingCredentialsProvider.java
index 3e0ee2f..3941d3c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ChainingCredentialsProvider.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ChainingCredentialsProvider.java
@@ -113,19 +113,18 @@ public class ChainingCredentialsProvider extends CredentialsProvider {
throws UnsupportedCredentialItem {
for (CredentialsProvider p : credentialProviders) {
if (p.supports(items)) {
- p.get(uri, items);
- if (isAnyNull(items))
+ if (!p.get(uri, items)) {
+ if (p.isInteractive()) {
+ return false; // user cancelled the request
+ }
continue;
+ }
+ if (isAnyNull(items)) {
+ continue;
+ }
return true;
}
}
return false;
}
-
- private boolean isAnyNull(CredentialItem... items) {
- for (CredentialItem i : items)
- if (i == null)
- return true;
- return false;
- }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Connection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Connection.java
index e386c26..da288ec 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Connection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Connection.java
@@ -59,8 +59,7 @@ import org.eclipse.jgit.lib.Ref;
*
* @see Transport
*/
-public interface Connection {
-
+public interface Connection extends AutoCloseable {
/**
* Get the complete map of refs advertised as available for fetching or
* pushing.
@@ -108,6 +107,10 @@ public interface Connection {
* <p>
* If additional messages were produced by the remote peer, these should
* still be retained in the connection instance for {@link #getMessages()}.
+ * <p>
+ * {@code AutoClosable.close()} declares that it throws {@link Exception}.
+ * Implementers shouldn't throw checked exceptions. This override narrows
+ * the signature to prevent them from doing so.
*/
public void close();
@@ -127,4 +130,13 @@ public interface Connection {
* remote produced no additional messages.
*/
public String getMessages();
+
+ /**
+ * User agent advertised by the remote server.
+ *
+ * @return agent (version of Git) running on the remote server. Null if the
+ * server does not advertise this version.
+ * @since 4.0
+ */
+ public String getPeerUserAgent();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialsProvider.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialsProvider.java
index 464d0f9..4800f68 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialsProvider.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialsProvider.java
@@ -81,6 +81,20 @@ public abstract class CredentialsProvider {
}
/**
+ * @param items
+ * credential items to check
+ * @return {@code true} if any of the passed items is null, {@code false}
+ * otherwise
+ * @since 4.2
+ */
+ protected static boolean isAnyNull(CredentialItem... items) {
+ for (CredentialItem i : items)
+ if (i == null)
+ return true;
+ return false;
+ }
+
+ /**
* Check if the provider is interactive with the end-user.
*
* An interactive provider may try to open a dialog box, or prompt for input
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java
index 03f7c72..d9e0b93 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java
@@ -79,7 +79,7 @@ public class Daemon {
private boolean run;
- private Thread acceptThread;
+ Thread acceptThread;
private int timeout;
@@ -87,9 +87,9 @@ public class Daemon {
private volatile RepositoryResolver<DaemonClient> repositoryResolver;
- private volatile UploadPackFactory<DaemonClient> uploadPackFactory;
+ volatile UploadPackFactory<DaemonClient> uploadPackFactory;
- private volatile ReceivePackFactory<DaemonClient> receivePackFactory;
+ volatile ReceivePackFactory<DaemonClient> receivePackFactory;
/** Configure a daemon to listen on any available network port. */
public Daemon() {
@@ -326,7 +326,7 @@ public class Daemon {
}
}
- private void startClient(final Socket s) {
+ void startClient(final Socket s) {
final DaemonClient dc = new DaemonClient(this);
final SocketAddress peer = s.getRemoteSocketAddress();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
index 52a9bab..c4b3f83 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
@@ -136,6 +136,7 @@ class FetchProcess {
conn = transport.openFetch();
try {
result.setAdvertisedRefs(transport.getURI(), conn.getRefsMap());
+ result.peerUserAgent = conn.getPeerUserAgent();
final Set<Ref> matched = new HashSet<Ref>();
for (final RefSpec spec : toFetch) {
if (spec.getSource() == null)
@@ -196,8 +197,7 @@ class FetchProcess {
.newBatchUpdate()
.setAllowNonFastForwards(true)
.setRefLogMessage("fetch", true); //$NON-NLS-1$
- final RevWalk walk = new RevWalk(transport.local);
- try {
+ try (final RevWalk walk = new RevWalk(transport.local)) {
if (monitor instanceof BatchingProgressMonitor) {
((BatchingProgressMonitor) monitor).setDelayStart(
250, TimeUnit.MILLISECONDS);
@@ -226,8 +226,6 @@ class FetchProcess {
throw new TransportException(MessageFormat.format(
JGitText.get().failureUpdatingTrackingRef,
getFirstFailedRefName(batch), err.getMessage()), err);
- } finally {
- walk.release();
}
if (!fetchHeadUpdates.isEmpty()) {
@@ -338,15 +336,12 @@ class FetchProcess {
private boolean askForIsComplete() throws TransportException {
try {
- final ObjectWalk ow = new ObjectWalk(transport.local);
- try {
+ try (final ObjectWalk ow = new ObjectWalk(transport.local)) {
for (final ObjectId want : askFor.keySet())
ow.markStart(ow.parseAny(want));
for (final Ref ref : localRefs().values())
ow.markUninteresting(ow.parseAny(ref.getObjectId()));
ow.checkConnectivity();
- } finally {
- ow.release();
}
return true;
} catch (MissingObjectException e) {
@@ -402,11 +397,17 @@ class FetchProcess {
private void expandFetchTags() throws TransportException {
final Map<String, Ref> haveRefs = localRefs();
for (final Ref r : conn.getRefs()) {
- if (!isTag(r))
+ if (!isTag(r)) {
continue;
+ }
+ ObjectId id = r.getObjectId();
+ if (id == null) {
+ continue;
+ }
final Ref local = haveRefs.get(r.getName());
- if (local == null || !r.getObjectId().equals(local.getObjectId()))
+ if (local == null || !id.equals(local.getObjectId())) {
wantTag(r);
+ }
}
}
@@ -418,6 +419,11 @@ class FetchProcess {
private void want(final Ref src, final RefSpec spec)
throws TransportException {
final ObjectId newId = src.getObjectId();
+ if (newId == null) {
+ throw new NullPointerException(MessageFormat.format(
+ JGitText.get().transportProvidedRefWithNoObjectId,
+ src.getName()));
+ }
if (spec.getDestination() != null) {
final TrackingRefUpdate tru = createUpdate(spec, newId);
if (newId.equals(tru.getOldObjectId()))
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
index 976a823..efde062 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java
@@ -130,6 +130,14 @@ public class GitProtocolConstants {
public static final String OPTION_ALLOW_TIP_SHA1_IN_WANT = "allow-tip-sha1-in-want"; //$NON-NLS-1$
/**
+ * The client supports fetching objects that are reachable from a tip of a
+ * ref that is allowed to fetch.
+ *
+ * @since 4.1
+ */
+ public static final String OPTION_ALLOW_REACHABLE_SHA1_IN_WANT = "allow-reachable-sha1-in-want"; //$NON-NLS-1$
+
+ /**
* Symbolic reference support for better negotiation.
*
* @since 3.6
@@ -137,6 +145,13 @@ public class GitProtocolConstants {
public static final String OPTION_SYMREF = "symref"; //$NON-NLS-1$
/**
+ * The client will send a push certificate.
+ *
+ * @since 4.0
+ */
+ public static final String OPTION_PUSH_CERT = "push-cert"; //$NON-NLS-1$
+
+ /**
* The client supports atomic pushes. If this option is used, the server
* will update all refs within one atomic transaction.
*
@@ -145,6 +160,13 @@ public class GitProtocolConstants {
public static final String CAPABILITY_ATOMIC = "atomic"; //$NON-NLS-1$
/**
+ * The client expects less noise, e.g. no progress.
+ *
+ * @since 4.0
+ */
+ public static final String CAPABILITY_QUIET = "quiet"; //$NON-NLS-1$
+
+ /**
* The client expects a status report after the server processes the pack.
*
* @since 3.2
@@ -172,6 +194,20 @@ public class GitProtocolConstants {
*/
public static final String CAPABILITY_SIDE_BAND_64K = "side-band-64k"; //$NON-NLS-1$
+ /**
+ * The server allows recording of push certificates.
+ *
+ * @since 4.0
+ */
+ public static final String CAPABILITY_PUSH_CERT = "push-cert"; //$NON-NLS-1$
+
+ /**
+ * Implementation name and version of the client or server.
+ *
+ * @since 4.0
+ */
+ public static final String OPTION_AGENT = "agent"; //$NON-NLS-1$
+
static enum MultiAck {
OFF, CONTINUE, DETAILED;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java
new file mode 100644
index 0000000..622680a
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.transport;
+
+import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.eclipse.jgit.internal.storage.dfs.DfsRepository;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.NonceGenerator;
+import org.eclipse.jgit.transport.PushCertificate.NonceStatus;
+
+/**
+ * The nonce generator which was first introduced to git-core.
+ *
+ * @since 4.0
+ */
+public class HMACSHA1NonceGenerator implements NonceGenerator {
+
+ private Mac mac;
+
+ /**
+ * @param seed
+ * @throws IllegalStateException
+ */
+ public HMACSHA1NonceGenerator(String seed) throws IllegalStateException {
+ try {
+ byte[] keyBytes = seed.getBytes("ISO-8859-1"); //$NON-NLS-1$
+ SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA1"); //$NON-NLS-1$
+ mac = Mac.getInstance("HmacSHA1"); //$NON-NLS-1$
+ mac.init(signingKey);
+ } catch (InvalidKeyException e) {
+ throw new IllegalStateException(e);
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException(e);
+ } catch (UnsupportedEncodingException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public synchronized String createNonce(Repository repo, long timestamp)
+ throws IllegalStateException {
+ String path;
+ if (repo instanceof DfsRepository) {
+ path = ((DfsRepository) repo).getDescription().getRepositoryName();
+ } else {
+ File directory = repo.getDirectory();
+ if (directory != null) {
+ path = directory.getPath();
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ String input = path + ":" + String.valueOf(timestamp); //$NON-NLS-1$
+ byte[] rawHmac;
+ try {
+ rawHmac = mac.doFinal(input.getBytes("UTF-8")); //$NON-NLS-1$
+ } catch (UnsupportedEncodingException e) {
+ throw new IllegalStateException(e);
+ }
+ return Long.toString(timestamp) + "-" + toHex(rawHmac); //$NON-NLS-1$
+ }
+
+ @Override
+ public NonceStatus verify(String received, String sent,
+ Repository db, boolean allowSlop, int slop) {
+ if (received.isEmpty()) {
+ return NonceStatus.MISSING;
+ } else if (sent.isEmpty()) {
+ return NonceStatus.UNSOLICITED;
+ } else if (received.equals(sent)) {
+ return NonceStatus.OK;
+ }
+
+ if (!allowSlop) {
+ return NonceStatus.BAD;
+ }
+
+ /* nonce is concat(<seconds-since-epoch>, "-", <hmac>) */
+ int idxSent = sent.indexOf('-');
+ int idxRecv = received.indexOf('-');
+ if (idxSent == -1 || idxRecv == -1) {
+ return NonceStatus.BAD;
+ }
+
+ String signedStampStr = received.substring(0, idxRecv);
+ String advertisedStampStr = sent.substring(0, idxSent);
+ long signedStamp;
+ long advertisedStamp;
+ try {
+ signedStamp = Long.parseLong(signedStampStr);
+ advertisedStamp = Long.parseLong(advertisedStampStr);
+ } catch (IllegalArgumentException e) {
+ return NonceStatus.BAD;
+ }
+
+ // what we would have signed earlier
+ String expect = createNonce(db, signedStamp);
+
+ if (!expect.equals(received)) {
+ return NonceStatus.BAD;
+ }
+
+ long nonceStampSlop = Math.abs(advertisedStamp - signedStamp);
+
+ if (nonceStampSlop <= slop) {
+ return NonceStatus.OK;
+ } else {
+ return NonceStatus.SLOP;
+ }
+ }
+
+ private static final String HEX = "0123456789ABCDEF"; //$NON-NLS-1$
+
+ private static String toHex(byte[] bytes) {
+ StringBuilder builder = new StringBuilder(2 * bytes.length);
+ for (byte b : bytes) {
+ builder.append(HEX.charAt((b & 0xF0) >> 4));
+ builder.append(HEX.charAt(b & 0xF));
+ }
+ return builder.toString();
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java
index 3594ea9..998f280 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java
@@ -219,7 +219,8 @@ abstract class HttpAuthMethod {
if (credentialsProvider.supports(u, p)
&& credentialsProvider.get(uri, u, p)) {
username = u.getValue();
- password = new String(p.getValue());
+ char[] v = p.getValue();
+ password = (v == null) ? null : new String(p.getValue());
p.clear();
} else
return false;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalFetchConnection.java
new file mode 100644
index 0000000..7fc4048
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalFetchConnection.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import java.io.IOException;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+
+import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
+import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
+import org.eclipse.jgit.transport.resolver.UploadPackFactory;
+
+class InternalFetchConnection<C> extends BasePackFetchConnection {
+ private Thread worker;
+
+ public InternalFetchConnection(PackTransport transport,
+ final UploadPackFactory<C> uploadPackFactory,
+ final C req, final Repository remote) throws TransportException {
+ super(transport);
+
+ final PipedInputStream in_r;
+ final PipedOutputStream in_w;
+
+ final PipedInputStream out_r;
+ final PipedOutputStream out_w;
+ try {
+ in_r = new PipedInputStream();
+ in_w = new PipedOutputStream(in_r);
+
+ out_r = new PipedInputStream() {
+ // The client (BasePackFetchConnection) can write
+ // a huge burst before it reads again. We need to
+ // force the buffer to be big enough, otherwise it
+ // will deadlock both threads.
+ {
+ buffer = new byte[MIN_CLIENT_BUFFER];
+ }
+ };
+ out_w = new PipedOutputStream(out_r);
+ } catch (IOException err) {
+ remote.close();
+ throw new TransportException(uri, JGitText.get().cannotConnectPipes, err);
+ }
+
+ worker = new Thread("JGit-Upload-Pack") { //$NON-NLS-1$
+ public void run() {
+ try {
+ final UploadPack rp = uploadPackFactory.create(req, remote);
+ rp.upload(out_r, in_w, null);
+ } catch (ServiceNotEnabledException e) {
+ // Ignored. Client cannot use this repository.
+ } catch (ServiceNotAuthorizedException e) {
+ // Ignored. Client cannot use this repository.
+ } catch (IOException err) {
+ // Client side of the pipes should report the problem.
+ err.printStackTrace();
+ } catch (RuntimeException err) {
+ // Client side will notice we went away, and report.
+ err.printStackTrace();
+ } finally {
+ try {
+ out_r.close();
+ } catch (IOException e2) {
+ // Ignore close failure, we probably crashed above.
+ }
+
+ try {
+ in_w.close();
+ } catch (IOException e2) {
+ // Ignore close failure, we probably crashed above.
+ }
+
+ remote.close();
+ }
+ }
+ };
+ worker.start();
+
+ init(in_r, out_w);
+ readAdvertisedRefs();
+ }
+
+ @Override
+ public void close() {
+ super.close();
+
+ try {
+ if (worker != null) {
+ worker.join();
+ }
+ } catch (InterruptedException ie) {
+ // Stop waiting and return anyway.
+ } finally {
+ worker = null;
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalHttpServerGlue.java
similarity index 70%
rename from org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalHttpServerGlue.java
index 46ea2aa..fe7aaf7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java5.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalHttpServerGlue.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, Shawn O. Pearce <spearce at spearce.org>
+ * Copyright (C) 2015, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -41,53 +41,40 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.util;
-
-import java.io.File;
-
+package org.eclipse.jgit.transport;
/**
- * FS implementaton for Java5
+ * Internal API to to assist {@code org.eclipse.jgit.http.server}.
+ * <p>
+ * <b>Do not call.</b>
*
- * @since 3.0
+ * @since 4.0
*/
-public class FS_POSIX_Java5 extends FS_POSIX {
+public class InternalHttpServerGlue {
/**
- * Constructor
+ * Apply a default user agent for a request.
+ *
+ * @param up
+ * current UploadPack instance.
+ * @param agent
+ * user agent string from the HTTP headers.
*/
- public FS_POSIX_Java5() {
- super();
+ public static void setPeerUserAgent(UploadPack up, String agent) {
+ up.userAgent = agent;
}
/**
- * Constructor
+ * Apply a default user agent for a request.
*
- * @param src
- * instance whose attributes to copy
+ * @param rp
+ * current ReceivePack instance.
+ * @param agent
+ * user agent string from the HTTP headers.
*/
- public FS_POSIX_Java5(FS src) {
- super(src);
- }
-
- @Override
- public FS newInstance() {
- return new FS_POSIX_Java5(this);
- }
-
- public boolean supportsExecute() {
- return false;
- }
-
- public boolean canExecute(final File f) {
- return false;
- }
-
- public boolean setExecute(final File f, final boolean canExec) {
- return false;
+ public static void setPeerUserAgent(ReceivePack rp, String agent) {
+ rp.userAgent = agent;
}
- @Override
- public boolean retryFailedLockFileCommit() {
- return false;
+ private InternalHttpServerGlue() {
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java
new file mode 100644
index 0000000..ab76e0f
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/InternalPushConnection.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import java.io.IOException;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+
+import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
+import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
+import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
+
+class InternalPushConnection<C> extends BasePackPushConnection {
+ private Thread worker;
+
+ public InternalPushConnection(PackTransport transport,
+ final ReceivePackFactory<C> receivePackFactory,
+ final C req, final Repository remote) throws TransportException {
+ super(transport);
+
+ final PipedInputStream in_r;
+ final PipedOutputStream in_w;
+
+ final PipedInputStream out_r;
+ final PipedOutputStream out_w;
+ try {
+ in_r = new PipedInputStream();
+ in_w = new PipedOutputStream(in_r);
+
+ out_r = new PipedInputStream();
+ out_w = new PipedOutputStream(out_r);
+ } catch (IOException err) {
+ remote.close();
+ throw new TransportException(uri, JGitText.get().cannotConnectPipes, err);
+ }
+
+ worker = new Thread("JGit-Receive-Pack") { //$NON-NLS-1$
+ public void run() {
+ try {
+ final ReceivePack rp = receivePackFactory.create(req, remote);
+ rp.receive(out_r, in_w, System.err);
+ } catch (ServiceNotEnabledException e) {
+ // Ignored. Client cannot use this repository.
+ } catch (ServiceNotAuthorizedException e) {
+ // Ignored. Client cannot use this repository.
+ } catch (IOException err) {
+ // Client side of the pipes should report the problem.
+ } catch (RuntimeException err) {
+ // Clients side will notice we went away, and report.
+ } finally {
+ try {
+ out_r.close();
+ } catch (IOException e2) {
+ // Ignore close failure, we probably crashed above.
+ }
+
+ try {
+ in_w.close();
+ } catch (IOException e2) {
+ // Ignore close failure, we probably crashed above.
+ }
+
+ remote.close();
+ }
+ }
+ };
+ worker.start();
+
+ init(in_r, out_w);
+ readAdvertisedRefs();
+ }
+
+ @Override
+ public void close() {
+ super.close();
+
+ if (worker != null) {
+ try {
+ worker.join();
+ } catch (InterruptedException ie) {
+ // Stop waiting and return anyway.
+ } finally {
+ worker = null;
+ }
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java
index e5d59b5..1dfe5d9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/JschSession.java
@@ -55,6 +55,7 @@ import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.util.io.StreamCopyThread;
import com.jcraft.jsch.Channel;
@@ -70,13 +71,13 @@ import com.jcraft.jsch.Session;
* to the constructor.
*/
public class JschSession implements RemoteSession {
- private final Session sock;
- private final URIish uri;
+ final Session sock;
+ final URIish uri;
/**
* Create a new session object by passing the real Jsch session and the URI
* information.
- *
+ *
* @param session
* the real Jsch session created elsewhere.
* @param uri
@@ -118,7 +119,7 @@ public class JschSession implements RemoteSession {
private class JschProcess extends Process {
private ChannelExec channel;
- private final int timeout;
+ final int timeout;
private InputStream inputStream;
@@ -140,7 +141,7 @@ public class JschSession implements RemoteSession {
* @throws IOException
* on problems opening streams
*/
- private JschProcess(final String commandName, int tms)
+ JschProcess(final String commandName, int tms)
throws TransportException, IOException {
timeout = tms;
try {
@@ -148,13 +149,27 @@ public class JschSession implements RemoteSession {
channel.setCommand(commandName);
setupStreams();
channel.connect(timeout > 0 ? timeout * 1000 : 0);
- if (!channel.isConnected())
- throw new TransportException(uri, "connection failed");
+ if (!channel.isConnected()) {
+ closeOutputStream();
+ throw new TransportException(uri,
+ JGitText.get().connectionFailed);
+ }
} catch (JSchException e) {
+ closeOutputStream();
throw new TransportException(uri, e.getMessage(), e);
}
}
+ private void closeOutputStream() {
+ if (outputStream != null) {
+ try {
+ outputStream.close();
+ } catch (IOException ioe) {
+ // ignore
+ }
+ }
+ }
+
private void setupStreams() throws IOException {
inputStream = channel.getInputStream();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/NetRCCredentialsProvider.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/NetRCCredentialsProvider.java
index 7490999..4037545 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/NetRCCredentialsProvider.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/NetRCCredentialsProvider.java
@@ -105,12 +105,11 @@ public class NetRCCredentialsProvider extends CredentialsProvider {
throw new UnsupportedCredentialItem(uri, i.getClass().getName()
+ ":" + i.getPromptText()); //$NON-NLS-1$
}
- return true;
+ return !isAnyNull(items);
}
@Override
public boolean isInteractive() {
return false;
}
-
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/NonceGenerator.java
similarity index 51%
copy from org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/transport/NonceGenerator.java
index 3f14cc6..d1ea089 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/NonceGenerator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, Google Inc.
+ * Copyright (C) 2015, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -40,54 +40,55 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-
package org.eclipse.jgit.transport;
-import java.util.List;
-
-import org.eclipse.jgit.internal.storage.pack.PackWriter;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.PushCertificate.NonceStatus;
/**
- * {@link UploadPackLogger} that delegates to a list of other loggers.
- * <p>
- * loggers are run in the order passed to the constructor.
+ * A NonceGenerator is used to create a nonce to be sent out to the pusher who
+ * will sign the nonce to prove it is not a replay attack on the push
+ * certificate.
+ *
+ * @since 4.0
*/
-public class UploadPackLoggerChain implements UploadPackLogger {
- private final UploadPackLogger[] loggers;
- private final int count;
+public interface NonceGenerator {
/**
- * Create a new logger chaining the given loggers together.
- *
- * @param loggers
- * loggers to execute, in order.
- * @return a new logger chain of the given loggers.
+ * @param db
+ * The repository which should be used to obtain a unique String
+ * such that the pusher cannot forge nonces by pushing to another
+ * repository at the same time as well and reusing the nonce.
+ * @param timestamp
+ * The current time in seconds.
+ * @return The nonce to be signed by the pusher
+ * @throws IllegalStateException
*/
- public static UploadPackLogger newChain(
- List<? extends UploadPackLogger> loggers) {
- UploadPackLogger[] newLoggers = new UploadPackLogger[loggers.size()];
- int i = 0;
- for (UploadPackLogger logger : loggers)
- if (logger != UploadPackLogger.NULL)
- newLoggers[i++] = logger;
- if (i == 0)
- return UploadPackLogger.NULL;
- else if (i == 1)
- return newLoggers[0];
- else
- return new UploadPackLoggerChain(newLoggers, i);
- }
+ public String createNonce(Repository db, long timestamp)
+ throws IllegalStateException;
/**
- * @since 3.0
+ * @param received
+ * The nonce which was received from the server
+ * @param sent
+ * The nonce which was originally sent out to the client.
+ * @param db
+ * The repository which should be used to obtain a unique String
+ * such that the pusher cannot forge nonces by pushing to another
+ * repository at the same time as well and reusing the nonce.
+ *
+ * @param allowSlop
+ * If the receiving backend is is able to generate slop. This is
+ * the case for serving via http protocol using more than one
+ * http frontend. The client would talk to different http
+ * frontends, which may have a slight difference of time due to
+ * @param slop
+ * If `allowSlop` is true, this specifies the number of seconds
+ * which we allow as slop.
+ *
+ * @return a NonceStatus indicating the trustworthiness of the received
+ * nonce.
*/
- public void onPackStatistics(PackWriter.Statistics stats) {
- for (int i = 0; i < count; i++)
- loggers[i].onPackStatistics(stats);
- }
-
- private UploadPackLoggerChain(UploadPackLogger[] loggers, int count) {
- this.loggers = loggers;
- this.count = count;
- }
+ public NonceStatus verify(String received, String sent,
+ Repository db, boolean allowSlop, int slop);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ObjectCountCallback.java
similarity index 70%
copy from org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/transport/ObjectCountCallback.java
index 99fa6e0..cf81adb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ObjectCountCallback.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, Google Inc.
+ * Copyright (C) 2015, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -43,30 +43,32 @@
package org.eclipse.jgit.transport;
+import java.io.OutputStream;
+
import org.eclipse.jgit.internal.storage.pack.PackWriter;
+import org.eclipse.jgit.lib.ProgressMonitor;
/**
- * Logs activity that occurred within {@link UploadPack}.
- * <p>
- * Implementors of the interface are responsible for associating the current
- * thread to a particular connection, if they need to also include connection
- * information. One method is to use a {@link java.lang.ThreadLocal} to remember
- * the connection information before invoking UploadPack.
+ * A callback to tell caller the count of objects ASAP.
+ *
+ * @since 4.1
*/
-public interface UploadPackLogger {
- /** A simple no-op logger. */
- public static final UploadPackLogger NULL = new UploadPackLogger() {
- public void onPackStatistics(PackWriter.Statistics stats) {
- // Do nothing.
- }
- };
-
+public interface ObjectCountCallback {
/**
- * Notice to the logger after a pack has been sent.
+ * Invoked when the {@link PackWriter} has counted the objects to be
+ * written to pack.
+ * <p>
+ * An {@code ObjectCountCallback} can use this information to decide
+ * whether the
+ * {@link PackWriter#writePack(ProgressMonitor, ProgressMonitor, OutputStream)}
+ * operation should be aborted.
+ * <p>
+ * This callback will be called exactly once.
*
- * @param stats
- * the statistics after sending a pack to the client.
- * @since 3.0
+ * @param objectCount
+ * the count of the objects.
+ * @throws WriteAbortedException
+ * to indicate that the write operation should be aborted.
*/
- public void onPackStatistics(PackWriter.Statistics stats);
+ void setObjectCount(long objectCount) throws WriteAbortedException;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/OperationResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/OperationResult.java
index b4a48b0..ad51f3e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/OperationResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/OperationResult.java
@@ -68,6 +68,8 @@ public abstract class OperationResult {
StringBuilder messageBuffer;
+ String peerUserAgent;
+
/**
* Get the URI this result came from.
* <p>
@@ -165,4 +167,15 @@ public abstract class OperationResult {
messageBuffer.append('\n');
}
}
+
+ /**
+ * Get the user agent advertised by the peer server, if available.
+ *
+ * @return advertised user agent, e.g. {@code "JGit/4.0"}. Null if the peer
+ * did not advertise version information.
+ * @since 4.0
+ */
+ public String getPeerUserAgent() {
+ return peerUserAgent;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
index 5b54891..b96fe88 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java
@@ -122,19 +122,21 @@ public abstract class PackParser {
private InputStream in;
- private byte[] buf;
+ byte[] buf;
/** Position in the input stream of {@code buf[0]}. */
private long bBase;
private int bOffset;
- private int bAvail;
+ int bAvail;
private ObjectChecker objCheck;
private boolean allowThin;
+ private boolean checkObjectCollisions;
+
private boolean needBaseObjectIds;
private boolean checkEofAfterPackFooter;
@@ -204,6 +206,7 @@ public abstract class PackParser {
objectDigest = Constants.newMessageDigest();
tempObjectId = new MutableObjectId();
packDigest = Constants.newMessageDigest();
+ checkObjectCollisions = true;
}
/** @return true if a thin pack (missing base objects) is permitted. */
@@ -225,6 +228,39 @@ public abstract class PackParser {
}
/**
+ * @return if true received objects are verified to prevent collisions.
+ * @since 4.1
+ */
+ protected boolean isCheckObjectCollisions() {
+ return checkObjectCollisions;
+ }
+
+ /**
+ * Enable checking for collisions with existing objects.
+ * <p>
+ * By default PackParser looks for each received object in the repository.
+ * If the object already exists, the existing object is compared
+ * byte-for-byte with the newly received copy to ensure they are identical.
+ * The receive is aborted with an exception if any byte differs. This check
+ * is necessary to prevent an evil attacker from supplying a replacement
+ * object into this repository in the event that a discovery enabling SHA-1
+ * collisions is made.
+ * <p>
+ * This check may be very costly to perform, and some repositories may have
+ * other ways to segregate newly received object data. The check is enabled
+ * by default, but can be explicitly disabled if the implementation can
+ * provide the same guarantee, or is willing to accept the risks associated
+ * with bypassing the check.
+ *
+ * @param check
+ * true to enable collision checking (strongly encouraged).
+ * @since 4.1
+ */
+ protected void setCheckObjectCollisions(boolean check) {
+ checkObjectCollisions = check;
+ }
+
+ /**
* Configure this index pack instance to keep track of new objects.
* <p>
* By default an index pack doesn't save the new objects that were created
@@ -529,7 +565,7 @@ public abstract class PackParser {
} finally {
try {
if (readCurs != null)
- readCurs.release();
+ readCurs.close();
} finally {
readCurs = null;
}
@@ -812,7 +848,7 @@ public abstract class PackParser {
for (final DeltaChain base : missing) {
if (base.head != null)
- throw new MissingObjectException(base, "delta base");
+ throw new MissingObjectException(base, "delta base"); //$NON-NLS-1$
}
onEndThinPack();
@@ -988,7 +1024,8 @@ public abstract class PackParser {
}
inf.close();
tempObjectId.fromRaw(objectDigest.digest(), 0);
- checkContentLater = readCurs.has(tempObjectId);
+ checkContentLater = isCheckObjectCollisions()
+ && readCurs.has(tempObjectId);
data = null;
} else {
@@ -1012,8 +1049,11 @@ public abstract class PackParser {
final byte[] data) throws IOException {
if (objCheck != null) {
try {
- objCheck.check(type, data);
+ objCheck.check(id, type, data);
} catch (CorruptObjectException e) {
+ if (e.getErrorType() != null) {
+ throw e;
+ }
throw new CorruptObjectException(MessageFormat.format(
JGitText.get().invalidObject,
Constants.typeString(type),
@@ -1022,17 +1062,19 @@ public abstract class PackParser {
}
}
- try {
- final ObjectLoader ldr = readCurs.open(id, type);
- final byte[] existingData = ldr.getCachedBytes(data.length);
- if (!Arrays.equals(data, existingData)) {
- throw new IOException(MessageFormat.format(
- JGitText.get().collisionOn, id.name()));
+ if (isCheckObjectCollisions()) {
+ try {
+ final ObjectLoader ldr = readCurs.open(id, type);
+ final byte[] existingData = ldr.getCachedBytes(data.length);
+ if (!Arrays.equals(data, existingData)) {
+ throw new IOException(MessageFormat.format(
+ JGitText.get().collisionOn, id.name()));
+ }
+ } catch (MissingObjectException notLocal) {
+ // This is OK, we don't have a copy of the object locally
+ // but the API throws when we try to read it as usually its
+ // an error to read something that doesn't exist.
}
- } catch (MissingObjectException notLocal) {
- // This is OK, we don't have a copy of the object locally
- // but the API throws when we try to read it as usually its
- // an error to read something that doesn't exist.
}
}
@@ -1102,13 +1144,13 @@ public abstract class PackParser {
}
// Consume cnt bytes from the buffer.
- private void use(final int cnt) {
+ void use(final int cnt) {
bOffset += cnt;
bAvail -= cnt;
}
// Ensure at least need bytes are available in in {@link #buf}.
- private int fill(final Source src, final int need) throws IOException {
+ int fill(final Source src, final int need) throws IOException {
while (bAvail < need) {
int next = bOffset + bAvail;
int free = buf.length - next;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java
similarity index 80%
copy from org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java
index 99fa6e0..53eeab1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2011, Google Inc.
- * and other copyright owners as documented in the project's IP log.
+ * Copyright (C) 2015, Google Inc.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
@@ -44,29 +43,32 @@
package org.eclipse.jgit.transport;
import org.eclipse.jgit.internal.storage.pack.PackWriter;
+import org.eclipse.jgit.storage.pack.PackStatistics;
/**
- * Logs activity that occurred within {@link UploadPack}.
+ * Hook invoked by {@link UploadPack} after the pack has been uploaded.
* <p>
* Implementors of the interface are responsible for associating the current
* thread to a particular connection, if they need to also include connection
* information. One method is to use a {@link java.lang.ThreadLocal} to remember
* the connection information before invoking UploadPack.
+ *
+ * @since 4.1
*/
-public interface UploadPackLogger {
- /** A simple no-op logger. */
- public static final UploadPackLogger NULL = new UploadPackLogger() {
- public void onPackStatistics(PackWriter.Statistics stats) {
+public interface PostUploadHook {
+ /** A simple no-op hook. */
+ public static final PostUploadHook NULL = new PostUploadHook() {
+ public void onPostUpload(PackStatistics stats) {
// Do nothing.
}
};
/**
- * Notice to the logger after a pack has been sent.
+ * Notifies the hook that a pack has been sent.
*
* @param stats
- * the statistics after sending a pack to the client.
- * @since 3.0
+ * the statistics gathered by {@link PackWriter} for the uploaded
+ * pack
*/
- public void onPackStatistics(PackWriter.Statistics stats);
+ public void onPostUpload(PackStatistics stats);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHookChain.java
similarity index 63%
copy from org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java
copy to org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHookChain.java
index 3f14cc6..4e2eaea 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHookChain.java
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2011, Google Inc.
- * and other copyright owners as documented in the project's IP log.
+ * Copyright (C) 2015, Google Inc.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
@@ -45,49 +44,47 @@ package org.eclipse.jgit.transport;
import java.util.List;
-import org.eclipse.jgit.internal.storage.pack.PackWriter;
+import org.eclipse.jgit.storage.pack.PackStatistics;
/**
- * {@link UploadPackLogger} that delegates to a list of other loggers.
+ * {@link PostUploadHook} that delegates to a list of other hooks.
* <p>
- * loggers are run in the order passed to the constructor.
+ * Hooks are run in the order passed to the constructor.
+ *
+ * @since 4.1
*/
-public class UploadPackLoggerChain implements UploadPackLogger {
- private final UploadPackLogger[] loggers;
+public class PostUploadHookChain implements PostUploadHook {
+ private final PostUploadHook[] hooks;
private final int count;
/**
- * Create a new logger chaining the given loggers together.
+ * Create a new hook chaining the given hooks together.
*
- * @param loggers
- * loggers to execute, in order.
- * @return a new logger chain of the given loggers.
+ * @param hooks
+ * hooks to execute, in order.
+ * @return a new chain of the given hooks.
*/
- public static UploadPackLogger newChain(
- List<? extends UploadPackLogger> loggers) {
- UploadPackLogger[] newLoggers = new UploadPackLogger[loggers.size()];
+ public static PostUploadHook newChain(List<? extends PostUploadHook> hooks) {
+ PostUploadHook[] newHooks = new PostUploadHook[hooks.size()];
int i = 0;
- for (UploadPackLogger logger : loggers)
- if (logger != UploadPackLogger.NULL)
- newLoggers[i++] = logger;
+ for (PostUploadHook hook : hooks)
+ if (hook != PostUploadHook.NULL)
+ newHooks[i++] = hook;
if (i == 0)
- return UploadPackLogger.NULL;
+ return PostUploadHook.NULL;
else if (i == 1)
- return newLoggers[0];
+ return newHooks[0];
else
- return new UploadPackLoggerChain(newLoggers, i);
+ return new PostUploadHookChain(newHooks, i);
}
- /**
- * @since 3.0
- */
- public void onPackStatistics(PackWriter.Statistics stats) {
+ public void onPostUpload(PackStatistics stats) {
for (int i = 0; i < count; i++)
- loggers[i].onPackStatistics(stats);
+ hooks[i].onPostUpload(stats);
}
- private UploadPackLoggerChain(UploadPackLogger[] loggers, int count) {
- this.loggers = loggers;
+ private PostUploadHookChain(PostUploadHook[] hooks, int count) {
+ this.hooks = hooks;
this.count = count;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProgressSpinner.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProgressSpinner.java
new file mode 100644
index 0000000..ac048a1
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProgressSpinner.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A simple spinner connected to an {@code OutputStream}.
+ * <p>
+ * This is class is not thread-safe. The update method may only be used from a
+ * single thread. Updates are sent only as frequently as {@link #update()} is
+ * invoked by the caller, and are capped at no more than 2 times per second by
+ * requiring at least 500 milliseconds between updates.
+ *
+ * @since 4.2
+ */
+public class ProgressSpinner {
+ private static final long MIN_REFRESH_MILLIS = 500;
+ private static final char[] STATES = new char[] { '-', '\\', '|', '/' };
+
+ private final OutputStream out;
+ private String msg;
+ private int state;
+ private boolean write;
+ private boolean shown;
+ private long nextUpdateMillis;
+
+ /**
+ * Initialize a new spinner.
+ *
+ * @param out
+ * where to send output to.
+ */
+ public ProgressSpinner(OutputStream out) {
+ this.out = out;
+ this.write = true;
+ }
+
+ /**
+ * Begin a time consuming task.
+ *
+ * @param title
+ * description of the task, suitable for human viewing.
+ * @param delay
+ * delay to wait before displaying anything at all.
+ * @param delayUnits
+ * unit for {@code delay}.
+ */
+ public void beginTask(String title, long delay, TimeUnit delayUnits) {
+ msg = title;
+ state = 0;
+ shown = false;
+
+ long now = System.currentTimeMillis();
+ if (delay > 0) {
+ nextUpdateMillis = now + delayUnits.toMillis(delay);
+ } else {
+ send(now);
+ }
+ }
+
+ /** Update the spinner if it is showing. */
+ public void update() {
+ long now = System.currentTimeMillis();
+ if (now >= nextUpdateMillis) {
+ send(now);
+ state = (state + 1) % STATES.length;
+ }
+ }
+
+ private void send(long now) {
+ StringBuilder buf = new StringBuilder(msg.length() + 16);
+ buf.append('\r').append(msg).append("... ("); //$NON-NLS-1$
+ buf.append(STATES[state]);
+ buf.append(") "); //$NON-NLS-1$
+ shown = true;
+ write(buf.toString());
+ nextUpdateMillis = now + MIN_REFRESH_MILLIS;
+ }
+
+ /**
+ * Denote the current task completed.
+ *
+ * @param result
+ * text to print after the task's title
+ * {@code "$title ... $result"}.
+ */
+ public void endTask(String result) {
+ if (shown) {
+ write('\r' + msg + "... " + result + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ private void write(String s) {
+ if (write) {
+ try {
+ out.write(s.getBytes(UTF_8));
+ out.flush();
+ } catch (IOException e) {
+ write = false;
+ }
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificate.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificate.java
new file mode 100644
index 0000000..e450345
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificate.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import static org.eclipse.jgit.transport.PushCertificateParser.NONCE;
+import static org.eclipse.jgit.transport.PushCertificateParser.PUSHEE;
+import static org.eclipse.jgit.transport.PushCertificateParser.PUSHER;
+import static org.eclipse.jgit.transport.PushCertificateParser.VERSION;
+
+import java.text.MessageFormat;
+import java.util.List;
+import java.util.Objects;
+
+import org.eclipse.jgit.internal.JGitText;
+
+/**
+ * The required information to verify the push.
+ * <p>
+ * A valid certificate will not return null from any getter methods; callers may
+ * assume that any null value indicates a missing or invalid certificate.
+ *
+ * @since 4.0
+ */
+public class PushCertificate {
+ /** Verification result of the nonce returned during push. */
+ public enum NonceStatus {
+ /** Nonce was not expected, yet client sent one anyway. */
+ UNSOLICITED,
+ /** Nonce is invalid and did not match server's expectations. */
+ BAD,
+ /** Nonce is required, but was not sent by client. */
+ MISSING,
+ /**
+ * Received nonce matches sent nonce, or is valid within the accepted slop
+ * window.
+ */
+ OK,
+ /** Received nonce is valid, but outside the accepted slop window. */
+ SLOP
+ }
+
+ private final String version;
+ private final PushCertificateIdent pusher;
+ private final String pushee;
+ private final String nonce;
+ private final NonceStatus nonceStatus;
+ private final List<ReceiveCommand> commands;
+ private final String signature;
+
+ PushCertificate(String version, PushCertificateIdent pusher, String pushee,
+ String nonce, NonceStatus nonceStatus, List<ReceiveCommand> commands,
+ String signature) {
+ if (version == null || version.isEmpty()) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().pushCertificateInvalidField, VERSION));
+ }
+ if (pusher == null) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().pushCertificateInvalidField, PUSHER));
+ }
+ if (nonce == null || nonce.isEmpty()) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().pushCertificateInvalidField, NONCE));
+ }
+ if (nonceStatus == null) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().pushCertificateInvalidField,
+ "nonce status")); //$NON-NLS-1$
+ }
+ if (commands == null || commands.isEmpty()) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().pushCertificateInvalidField,
+ "command")); //$NON-NLS-1$
+ }
+ if (signature == null || signature.isEmpty()) {
+ throw new IllegalArgumentException(
+ JGitText.get().pushCertificateInvalidSignature);
+ }
+ if (!signature.startsWith(PushCertificateParser.BEGIN_SIGNATURE)
+ || !signature.endsWith(PushCertificateParser.END_SIGNATURE + '\n')) {
+ throw new IllegalArgumentException(
+ JGitText.get().pushCertificateInvalidSignature);
+ }
+ this.version = version;
+ this.pusher = pusher;
+ this.pushee = pushee;
+ this.nonce = nonce;
+ this.nonceStatus = nonceStatus;
+ this.commands = commands;
+ this.signature = signature;
+ }
+
+ /**
+ * @return the certificate version string.
+ * @since 4.1
+ */
+ public String getVersion() {
+ return version;
+ }
+
+ /**
+ * @return the raw line that signed the cert, as a string.
+ * @since 4.0
+ */
+ public String getPusher() {
+ return pusher.getRaw();
+ }
+
+ /**
+ * @return identity of the pusher who signed the cert.
+ * @since 4.1
+ */
+ public PushCertificateIdent getPusherIdent() {
+ return pusher;
+ }
+
+ /**
+ * @return URL of the repository the push was originally sent to.
+ * @since 4.0
+ */
+ public String getPushee() {
+ return pushee;
+ }
+
+ /**
+ * @return the raw nonce value that was presented by the pusher.
+ * @since 4.1
+ */
+ public String getNonce() {
+ return nonce;
+ }
+
+ /**
+ * @return verification status of the nonce embedded in the certificate.
+ * @since 4.0
+ */
+ public NonceStatus getNonceStatus() {
+ return nonceStatus;
+ }
+
+ /**
+ * @return the list of commands as one string to be feed into the signature
+ * verifier.
+ * @since 4.1
+ */
+ public List<ReceiveCommand> getCommands() {
+ return commands;
+ }
+
+ /**
+ * @return the raw signature, consisting of the lines received between the
+ * lines {@code "----BEGIN GPG SIGNATURE-----\n"} and
+ * {@code "----END GPG SIGNATURE-----\n}", inclusive.
+ * @since 4.0
+ */
+ public String getSignature() {
+ return signature;
+ }
+
+ /**
+ * @return text payload of the certificate for the signature verifier.
+ * @since 4.1
+ */
+ public String toText() {
+ return toStringBuilder().toString();
+ }
+
+ /**
+ * @return original text payload plus signature; the final output will be
+ * valid as input to {@link PushCertificateParser#fromString(String)}.
+ * @since 4.1
+ */
+ public String toTextWithSignature() {
+ return toStringBuilder().append(signature).toString();
+ }
+
+ private StringBuilder toStringBuilder() {
+ StringBuilder sb = new StringBuilder()
+ .append(VERSION).append(' ').append(version).append('\n')
+ .append(PUSHER).append(' ').append(getPusher())
+ .append('\n');
+ if (pushee != null) {
+ sb.append(PUSHEE).append(' ').append(pushee).append('\n');
+ }
+ sb.append(NONCE).append(' ').append(nonce).append('\n')
+ .append('\n');
+ for (ReceiveCommand cmd : commands) {
+ sb.append(cmd.getOldId().name())
+ .append(' ').append(cmd.getNewId().name())
+ .append(' ').append(cmd.getRefName()).append('\n');
+ }
+ return sb;
+ }
+
+ @Override
+ public int hashCode() {
+ return signature.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof PushCertificate)) {
+ return false;
+ }
+ PushCertificate p = (PushCertificate) o;
+ return version.equals(p.version)
+ && pusher.equals(p.pusher)
+ && Objects.equals(pushee, p.pushee)
+ && nonceStatus == p.nonceStatus
+ && signature.equals(p.signature)
+ && commandsEqual(this, p);
+ }
+
+ private static boolean commandsEqual(PushCertificate c1, PushCertificate c2) {
+ if (c1.commands.size() != c2.commands.size()) {
+ return false;
+ }
+ for (int i = 0; i < c1.commands.size(); i++) {
+ ReceiveCommand cmd1 = c1.commands.get(i);
+ ReceiveCommand cmd2 = c2.commands.get(i);
+ if (!cmd1.getOldId().equals(cmd2.getOldId())
+ || !cmd1.getNewId().equals(cmd2.getNewId())
+ || !cmd1.getRefName().equals(cmd2.getRefName())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + '['
+ + toTextWithSignature() + ']';
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateIdent.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateIdent.java
new file mode 100644
index 0000000..871a6f7
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateIdent.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import static org.eclipse.jgit.util.RawParseUtils.lastIndexOfTrim;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.util.MutableInteger;
+import org.eclipse.jgit.util.RawParseUtils;
+
+/**
+ * Identity in a push certificate.
+ * <p>
+ * This is similar to a {@link PersonIdent} in that it contains a name,
+ * timestamp, and timezone offset, but differs in the following ways:
+ * <ul>
+ * <li>It is always parsed from a UTF-8 string, rather than a raw commit
+ * buffer.</li>
+ * <li>It is not guaranteed to contain a name and email portion, since any UTF-8
+ * string is a valid OpenPGP User ID (RFC4880 5.1.1). The raw User ID is
+ * always available as {@link #getUserId()}, but {@link #getEmailAddress()}
+ * may return null.</li>
+ * <li>The raw text from which the identity was parsed is available with {@link
+ * #getRaw()}. This is necessary for losslessly reconstructing the signed push
+ * certificate payload.</li>
+ * <li>
+ * </ul>
+ *
+ * @since 4.1
+ */
+public class PushCertificateIdent {
+ /**
+ * Parse an identity from a string.
+ * <p>
+ * Spaces are trimmed when parsing the timestamp and timezone offset, with one
+ * exception. The timestamp must be preceded by a single space, and the rest
+ * of the string prior to that space (including any additional whitespace) is
+ * treated as the OpenPGP User ID.
+ * <p>
+ * If either the timestamp or timezone offsets are missing, mimics {@link
+ * RawParseUtils#parsePersonIdent(String)} behavior and sets them both to
+ * zero.
+ *
+ * @param str
+ * string to parse.
+ * @return identity, never null.
+ */
+ public static PushCertificateIdent parse(String str) {
+ MutableInteger p = new MutableInteger();
+ byte[] raw = str.getBytes(UTF_8);
+ int tzBegin = raw.length - 1;
+ tzBegin = lastIndexOfTrim(raw, ' ', tzBegin);
+ if (tzBegin < 0 || raw[tzBegin] != ' ') {
+ return new PushCertificateIdent(str, str, 0, 0);
+ }
+ int whenBegin = tzBegin++;
+ int tz = RawParseUtils.parseTimeZoneOffset(raw, tzBegin, p);
+ boolean hasTz = p.value != tzBegin;
+
+ whenBegin = lastIndexOfTrim(raw, ' ', whenBegin);
+ if (whenBegin < 0 || raw[whenBegin] != ' ') {
+ return new PushCertificateIdent(str, str, 0, 0);
+ }
+ int idEnd = whenBegin++;
+ long when = RawParseUtils.parseLongBase10(raw, whenBegin, p);
+ boolean hasWhen = p.value != whenBegin;
+
+ if (hasTz && hasWhen) {
+ idEnd = whenBegin - 1;
+ } else {
+ // If either tz or when are non-numeric, mimic parsePersonIdent behavior and
+ // set them both to zero.
+ tz = 0;
+ when = 0;
+ if (hasTz && !hasWhen) {
+ // Only one trailing numeric field; assume User ID ends before this
+ // field, but discard its value.
+ idEnd = tzBegin - 1;
+ } else {
+ // No trailing numeric fields; User ID is whole raw value.
+ idEnd = raw.length;
+ }
+ }
+ String id = new String(raw, 0, idEnd, UTF_8);
+
+ return new PushCertificateIdent(str, id, when * 1000L, tz);
+ }
+
+ private final String raw;
+ private final String userId;
+ private final long when;
+ private final int tzOffset;
+
+ /**
+ * Construct a new identity from an OpenPGP User ID.
+ *
+ * @param userId
+ * OpenPGP User ID; any UTF-8 string.
+ * @param when
+ * local time.
+ * @param tzOffset
+ * timezone offset; see {@link #getTimeZoneOffset()}.
+ */
+ public PushCertificateIdent(String userId, long when, int tzOffset) {
+ this.userId = userId;
+ this.when = when;
+ this.tzOffset = tzOffset;
+ StringBuilder sb = new StringBuilder(userId).append(' ').append(when / 1000)
+ .append(' ');
+ PersonIdent.appendTimezone(sb, tzOffset);
+ raw = sb.toString();
+ }
+
+ private PushCertificateIdent(String raw, String userId, long when,
+ int tzOffset) {
+ this.raw = raw;
+ this.userId = userId;
+ this.when = when;
+ this.tzOffset = tzOffset;
+ }
+
+ /**
+ * Get the raw string from which this identity was parsed.
+ * <p>
+ * If the string was constructed manually, a suitable canonical string is
+ * returned.
+ * <p>
+ * For the purposes of bytewise comparisons with other OpenPGP IDs, the string
+ * must be encoded as UTF-8.
+ *
+ * @return the raw string.
+ */
+ public String getRaw() {
+ return raw;
+ }
+
+ /** @return the OpenPGP User ID, which may be any string. */
+ public String getUserId() {
+ return userId;
+ }
+
+ /**
+ * @return the name portion of the User ID. If no email address would be
+ * parsed by {@link #getEmailAddress()}, returns the full User ID with
+ * spaces trimmed.
+ */
+ public String getName() {
+ int nameEnd = userId.indexOf('<');
+ if (nameEnd < 0 || userId.indexOf('>', nameEnd) < 0) {
+ nameEnd = userId.length();
+ }
+ nameEnd--;
+ while (nameEnd >= 0 && userId.charAt(nameEnd) == ' ') {
+ nameEnd--;
+ }
+ int nameBegin = 0;
+ while (nameBegin < nameEnd && userId.charAt(nameBegin) == ' ') {
+ nameBegin++;
+ }
+ return userId.substring(nameBegin, nameEnd + 1);
+ }
+
+ /**
+ * @return the email portion of the User ID, if one was successfully parsed
+ * from {@link #getUserId()}, or null.
+ */
+ public String getEmailAddress() {
+ int emailBegin = userId.indexOf('<');
+ if (emailBegin < 0) {
+ return null;
+ }
+ int emailEnd = userId.indexOf('>', emailBegin);
+ if (emailEnd < 0) {
+ return null;
+ }
+ return userId.substring(emailBegin + 1, emailEnd);
+ }
+
+ /** @return the timestamp of the identity. */
+ public Date getWhen() {
+ return new Date(when);
+ }
+
+ /**
+ * @return this person's declared time zone; null if the timezone is unknown.
+ */
+ public TimeZone getTimeZone() {
+ return PersonIdent.getTimeZone(tzOffset);
+ }
+
+ /**
+ * @return this person's declared time zone as minutes east of UTC. If the
+ * timezone is to the west of UTC it is negative.
+ */
+ public int getTimeZoneOffset() {
+ return tzOffset;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return (o instanceof PushCertificateIdent)
+ && raw.equals(((PushCertificateIdent) o).raw);
+ }
+
+ @Override
+ public int hashCode() {
+ return raw.hashCode();
+ }
+
+ @SuppressWarnings("nls")
+ @Override
+ public String toString() {
+ SimpleDateFormat fmt;
+ fmt = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy Z", Locale.US);
+ fmt.setTimeZone(getTimeZone());
+ return getClass().getSimpleName()
+ + "[raw=\"" + raw + "\","
+ + " userId=\"" + userId + "\","
+ + " " + fmt.format(Long.valueOf(when)) + "]";
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateParser.java
new file mode 100644
index 0000000..5174f85
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateParser.java
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import static org.eclipse.jgit.transport.BaseReceivePack.parseCommand;
+import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_PUSH_CERT;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.Reader;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jgit.errors.PackProtocolException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.PushCertificate.NonceStatus;
+import org.eclipse.jgit.util.IO;
+
+/**
+ * Parser for signed push certificates.
+ *
+ * @since 4.0
+ */
+public class PushCertificateParser {
+ static final String BEGIN_SIGNATURE =
+ "-----BEGIN PGP SIGNATURE-----"; //$NON-NLS-1$
+ static final String END_SIGNATURE =
+ "-----END PGP SIGNATURE-----"; //$NON-NLS-1$
+
+ static final String VERSION = "certificate version"; //$NON-NLS-1$
+
+ static final String PUSHER = "pusher"; //$NON-NLS-1$
+
+ static final String PUSHEE = "pushee"; //$NON-NLS-1$
+
+ static final String NONCE = "nonce"; //$NON-NLS-1$
+
+ static final String END_CERT = "push-cert-end"; //$NON-NLS-1$
+
+ private static final String VERSION_0_1 = "0.1"; //$NON-NLS-1$
+
+ private static interface StringReader {
+ /**
+ * @return the next string from the input, up to an optional newline, with
+ * newline stripped if present
+ *
+ * @throws EOFException
+ * if EOF was reached.
+ * @throws IOException
+ * if an error occurred during reading.
+ */
+ String read() throws EOFException, IOException;
+ }
+
+ private static class PacketLineReader implements StringReader {
+ private final PacketLineIn pckIn;
+
+ private PacketLineReader(PacketLineIn pckIn) {
+ this.pckIn = pckIn;
+ }
+
+ @Override
+ public String read() throws IOException {
+ return pckIn.readString();
+ }
+ }
+
+ private static class StreamReader implements StringReader {
+ private final Reader reader;
+
+ private StreamReader(Reader reader) {
+ this.reader = reader;
+ }
+
+ @Override
+ public String read() throws IOException {
+ // Presize for a command containing 2 SHA-1s and some refname.
+ String line = IO.readLine(reader, 41 * 2 + 64);
+ if (line.isEmpty()) {
+ throw new EOFException();
+ } else if (line.charAt(line.length() - 1) == '\n') {
+ line = line.substring(0, line.length() - 1);
+ }
+ return line;
+ }
+ }
+
+ /**
+ * Parse a push certificate from a reader.
+ * <p>
+ * Differences from the {@link PacketLineIn} receiver methods:
+ * <ul>
+ * <li>Does not use pkt-line framing.</li>
+ * <li>Reads an entire cert in one call rather than depending on a loop in
+ * the caller.</li>
+ * <li>Does not assume a {@code "push-cert-end"} line.</li>
+ * </ul>
+ *
+ * @param r
+ * input reader; consumed only up until the end of the next
+ * signature in the input.
+ * @return the parsed certificate, or null if the reader was at EOF.
+ * @throws PackProtocolException
+ * if the certificate is malformed.
+ * @throws IOException
+ * if there was an error reading from the input.
+ * @since 4.1
+ */
+ public static PushCertificate fromReader(Reader r)
+ throws PackProtocolException, IOException {
+ return new PushCertificateParser().parse(r);
+ }
+
+ /**
+ * Parse a push certificate from a string.
+ *
+ * @see #fromReader(Reader)
+ * @param str
+ * input string.
+ * @return the parsed certificate.
+ * @throws PackProtocolException
+ * if the certificate is malformed.
+ * @throws IOException
+ * if there was an error reading from the input.
+ * @since 4.1
+ */
+ public static PushCertificate fromString(String str)
+ throws PackProtocolException, IOException {
+ return fromReader(new java.io.StringReader(str));
+ }
+
+ private boolean received;
+ private String version;
+ private PushCertificateIdent pusher;
+ private String pushee;
+
+ /** The nonce that was sent to the client. */
+ private String sentNonce;
+
+ /**
+ * The nonce the pusher signed.
+ * <p>
+ * This may vary from {@link #sentNonce}; see git-core documentation for
+ * reasons.
+ */
+ private String receivedNonce;
+
+ private NonceStatus nonceStatus;
+ private String signature;
+
+ /** Database we write the push certificate into. */
+ private final Repository db;
+
+ /**
+ * The maximum time difference which is acceptable between advertised nonce
+ * and received signed nonce.
+ */
+ private final int nonceSlopLimit;
+
+ private final boolean enabled;
+ private final NonceGenerator nonceGenerator;
+ private final List<ReceiveCommand> commands = new ArrayList<>();
+
+ /**
+ * @param into
+ * destination repository for the push.
+ * @param cfg
+ * configuration for signed push.
+ * @since 4.1
+ */
+ public PushCertificateParser(Repository into, SignedPushConfig cfg) {
+ if (cfg != null) {
+ nonceSlopLimit = cfg.getCertNonceSlopLimit();
+ nonceGenerator = cfg.getNonceGenerator();
+ } else {
+ nonceSlopLimit = 0;
+ nonceGenerator = null;
+ }
+ db = into;
+ enabled = nonceGenerator != null;
+ }
+
+ private PushCertificateParser() {
+ db = null;
+ nonceSlopLimit = 0;
+ nonceGenerator = null;
+ enabled = true;
+ }
+
+ /**
+ * Parse a push certificate from a reader.
+ *
+ * @see #fromReader(Reader)
+ * @param r
+ * input reader; consumed only up until the end of the next
+ * signature in the input.
+ * @return the parsed certificate, or null if the reader was at EOF.
+ * @throws PackProtocolException
+ * if the certificate is malformed.
+ * @throws IOException
+ * if there was an error reading from the input.
+ * @since 4.1
+ */
+ public PushCertificate parse(Reader r)
+ throws PackProtocolException, IOException {
+ StreamReader reader = new StreamReader(r);
+ receiveHeader(reader, true);
+ String line;
+ try {
+ while (!(line = reader.read()).isEmpty()) {
+ if (line.equals(BEGIN_SIGNATURE)) {
+ receiveSignature(reader);
+ break;
+ }
+ addCommand(line);
+ }
+ } catch (EOFException e) {
+ // EOF reached, but might have been at a valid state. Let build call below
+ // sort it out.
+ }
+ return build();
+ }
+
+ /**
+ * @return the parsed certificate, or null if push certificates are disabled.
+ * @throws IOException
+ * if the push certificate has missing or invalid fields.
+ * @since 4.1
+ */
+ public PushCertificate build() throws IOException {
+ if (!received || !enabled) {
+ return null;
+ }
+ try {
+ return new PushCertificate(version, pusher, pushee, receivedNonce,
+ nonceStatus, Collections.unmodifiableList(commands), signature);
+ } catch (IllegalArgumentException e) {
+ throw new IOException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * @return if the repository is configured to use signed pushes in this
+ * context.
+ * @since 4.0
+ */
+ public boolean enabled() {
+ return enabled;
+ }
+
+ /**
+ * @return the whole string for the nonce to be included into the capability
+ * advertisement, or null if push certificates are disabled.
+ * @since 4.0
+ */
+ public String getAdvertiseNonce() {
+ String nonce = sentNonce();
+ if (nonce == null) {
+ return null;
+ }
+ return CAPABILITY_PUSH_CERT + '=' + nonce;
+ }
+
+ private String sentNonce() {
+ if (sentNonce == null && nonceGenerator != null) {
+ sentNonce = nonceGenerator.createNonce(db,
+ TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()));
+ }
+ return sentNonce;
+ }
+
+ private static String parseHeader(StringReader reader, String header)
+ throws IOException {
+ return parseHeader(reader.read(), header);
+ }
+
+ private static String parseHeader(String s, String header)
+ throws IOException {
+ if (s.isEmpty()) {
+ throw new EOFException();
+ }
+ if (s.length() <= header.length()
+ || !s.startsWith(header)
+ || s.charAt(header.length()) != ' ') {
+ throw new PackProtocolException(MessageFormat.format(
+ JGitText.get().pushCertificateInvalidField, header));
+ }
+ return s.substring(header.length() + 1);
+ }
+
+ /**
+ * Receive a list of commands from the input encapsulated in a push
+ * certificate.
+ * <p>
+ * This method doesn't parse the first line {@code "push-cert \NUL
+ * <capabilities>"}, but assumes the first line including the
+ * capabilities has already been handled by the caller.
+ *
+ * @param pckIn
+ * where we take the push certificate header from.
+ * @param stateless
+ * affects nonce verification. When {@code stateless = true} the
+ * {@code NonceGenerator} will allow for some time skew caused by
+ * clients disconnected and reconnecting in the stateless smart
+ * HTTP protocol.
+ * @throws IOException
+ * if the certificate from the client is badly malformed or the
+ * client disconnects before sending the entire certificate.
+ * @since 4.0
+ */
+ public void receiveHeader(PacketLineIn pckIn, boolean stateless)
+ throws IOException {
+ receiveHeader(new PacketLineReader(pckIn), stateless);
+ }
+
+ private void receiveHeader(StringReader reader, boolean stateless)
+ throws IOException {
+ try {
+ try {
+ version = parseHeader(reader, VERSION);
+ } catch (EOFException e) {
+ return;
+ }
+ received = true;
+ if (!version.equals(VERSION_0_1)) {
+ throw new PackProtocolException(MessageFormat.format(
+ JGitText.get().pushCertificateInvalidFieldValue, VERSION, version));
+ }
+ String rawPusher = parseHeader(reader, PUSHER);
+ pusher = PushCertificateIdent.parse(rawPusher);
+ if (pusher == null) {
+ throw new PackProtocolException(MessageFormat.format(
+ JGitText.get().pushCertificateInvalidFieldValue,
+ PUSHER, rawPusher));
+ }
+ String next = reader.read();
+ if (next.startsWith(PUSHEE)) {
+ pushee = parseHeader(next, PUSHEE);
+ receivedNonce = parseHeader(reader, NONCE);
+ } else {
+ receivedNonce = parseHeader(next, NONCE);
+ }
+ nonceStatus = nonceGenerator != null
+ ? nonceGenerator.verify(
+ receivedNonce, sentNonce(), db, stateless, nonceSlopLimit)
+ : NonceStatus.UNSOLICITED;
+ // An empty line.
+ if (!reader.read().isEmpty()) {
+ throw new PackProtocolException(
+ JGitText.get().pushCertificateInvalidHeader);
+ }
+ } catch (EOFException eof) {
+ throw new PackProtocolException(
+ JGitText.get().pushCertificateInvalidHeader, eof);
+ }
+ }
+
+ /**
+ * Read the PGP signature.
+ * <p>
+ * This method assumes the line
+ * {@code "-----BEGIN PGP SIGNATURE-----"} has already been parsed,
+ * and continues parsing until an {@code "-----END PGP SIGNATURE-----"} is
+ * found, followed by {@code "push-cert-end"}.
+ *
+ * @param pckIn
+ * where we read the signature from.
+ * @throws IOException
+ * if the signature is invalid.
+ * @since 4.0
+ */
+ public void receiveSignature(PacketLineIn pckIn) throws IOException {
+ StringReader reader = new PacketLineReader(pckIn);
+ receiveSignature(reader);
+ if (!reader.read().equals(END_CERT)) {
+ throw new PackProtocolException(
+ JGitText.get().pushCertificateInvalidSignature);
+ }
+ }
+
+ private void receiveSignature(StringReader reader) throws IOException {
+ received = true;
+ try {
+ StringBuilder sig = new StringBuilder(BEGIN_SIGNATURE).append('\n');
+ String line;
+ while (!(line = reader.read()).equals(END_SIGNATURE)) {
+ sig.append(line).append('\n');
+ }
+ signature = sig.append(END_SIGNATURE).append('\n').toString();
+ } catch (EOFException eof) {
+ throw new PackProtocolException(
+ JGitText.get().pushCertificateInvalidSignature, eof);
+ }
+ }
+
+ /**
+ * Add a command to the signature.
+ *
+ * @param cmd
+ * the command.
+ * @since 4.1
+ */
+ public void addCommand(ReceiveCommand cmd) {
+ commands.add(cmd);
+ }
+
+ /**
+ * Add a command to the signature.
+ *
+ * @param line
+ * the line read from the wire that produced this
+ * command, with optional trailing newline already trimmed.
+ * @throws PackProtocolException
+ * if the raw line cannot be parsed to a command.
+ * @since 4.0
+ */
+ public void addCommand(String line) throws PackProtocolException {
+ commands.add(parseCommand(line));
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
new file mode 100644
index 0000000..d436e08
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
@@ -0,0 +1,548 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
+import static org.eclipse.jgit.lib.FileMode.TYPE_FILE;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheEditor;
+import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
+import org.eclipse.jgit.treewalk.filter.PathFilter;
+import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
+import org.eclipse.jgit.treewalk.filter.TreeFilter;
+
+/**
+ * Storage for recorded push certificates.
+ * <p>
+ * Push certificates are stored in a special ref {@code refs/meta/push-certs}.
+ * The filenames in the tree are ref names followed by the special suffix
+ * <code>@{cert}</code>, and the contents are the latest push cert affecting
+ * that ref. The special suffix allows storing certificates for both refs/foo
+ * and refs/foo/bar in case those both existed at some point.
+ *
+ * @since 4.1
+ */
+public class PushCertificateStore implements AutoCloseable {
+ /** Ref name storing push certificates. */
+ static final String REF_NAME =
+ Constants.R_REFS + "meta/push-certs"; //$NON-NLS-1$
+
+ private static class PendingCert {
+ PushCertificate cert;
+ PersonIdent ident;
+ Collection<ReceiveCommand> matching;
+
+ PendingCert(PushCertificate cert, PersonIdent ident,
+ Collection<ReceiveCommand> matching) {
+ this.cert = cert;
+ this.ident = ident;
+ this.matching = matching;
+ }
+ }
+
+ private final Repository db;
+ private final List<PendingCert> pending;
+ ObjectReader reader;
+ RevCommit commit;
+
+ /**
+ * Create a new store backed by the given repository.
+ *
+ * @param db
+ * the repository.
+ */
+ public PushCertificateStore(Repository db) {
+ this.db = db;
+ pending = new ArrayList<>();
+ }
+
+ /**
+ * Close resources opened by this store.
+ * <p>
+ * If {@link #get(String)} was called, closes the cached object reader created
+ * by that method. Does not close the underlying repository.
+ */
+ public void close() {
+ if (reader != null) {
+ reader.close();
+ reader = null;
+ commit = null;
+ }
+ }
+
+ /**
+ * Get latest push certificate associated with a ref.
+ * <p>
+ * Lazily opens {@code refs/meta/push-certs} and reads from the repository as
+ * necessary. The state is cached between calls to {@code get}; to reread the,
+ * call {@link #close()} first.
+ *
+ * @param refName
+ * the ref name to get the certificate for.
+ * @return last certificate affecting the ref, or null if no cert was recorded
+ * for the last update to this ref.
+ * @throws IOException
+ * if a problem occurred reading the repository.
+ */
+ public PushCertificate get(String refName) throws IOException {
+ if (reader == null) {
+ load();
+ }
+ try (TreeWalk tw = newTreeWalk(refName)) {
+ return read(tw);
+ }
+ }
+
+ /**
+ * Iterate over all push certificates affecting a ref.
+ * <p>
+ * Only includes push certificates actually stored in the tree; see class
+ * Javadoc for conditions where this might not include all push certs ever
+ * seen for this ref.
+ * <p>
+ * The returned iterable may be iterated multiple times, and push certs will
+ * be re-read from the current state of the store on each call to {@link
+ * Iterable#iterator()}. However, method calls on the returned iterator may
+ * fail if {@code save} or {@code close} is called on the enclosing store
+ * during iteration.
+ *
+ * @param refName
+ * the ref name to get certificates for.
+ * @return iterable over certificates; must be fully iterated in order to
+ * close resources.
+ */
+ public Iterable<PushCertificate> getAll(final String refName) {
+ return new Iterable<PushCertificate>() {
+ @Override
+ public Iterator<PushCertificate> iterator() {
+ return new Iterator<PushCertificate>() {
+ private final String path = pathName(refName);
+ private PushCertificate next;
+
+ private RevWalk rw;
+ {
+ try {
+ if (reader == null) {
+ load();
+ }
+ if (commit != null) {
+ rw = new RevWalk(reader);
+ rw.setTreeFilter(AndTreeFilter.create(
+ PathFilterGroup.create(
+ Collections.singleton(PathFilter.create(path))),
+ TreeFilter.ANY_DIFF));
+ rw.setRewriteParents(false);
+ rw.markStart(rw.parseCommit(commit));
+ } else {
+ rw = null;
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ try {
+ if (next == null) {
+ if (rw == null) {
+ return false;
+ }
+ try {
+ RevCommit c = rw.next();
+ if (c != null) {
+ try (TreeWalk tw = TreeWalk.forPath(
+ rw.getObjectReader(), path, c.getTree())) {
+ next = read(tw);
+ }
+ } else {
+ next = null;
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return next != null;
+ } finally {
+ if (next == null && rw != null) {
+ rw.close();
+ rw = null;
+ }
+ }
+ }
+
+ @Override
+ public PushCertificate next() {
+ hasNext();
+ PushCertificate n = next;
+ if (n == null) {
+ throw new NoSuchElementException();
+ }
+ next = null;
+ return n;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+ };
+ }
+
+ void load() throws IOException {
+ close();
+ reader = db.newObjectReader();
+ Ref ref = db.getRefDatabase().exactRef(REF_NAME);
+ if (ref == null) {
+ // No ref, same as empty.
+ return;
+ }
+ try (RevWalk rw = new RevWalk(reader)) {
+ commit = rw.parseCommit(ref.getObjectId());
+ }
+ }
+
+ static PushCertificate read(TreeWalk tw) throws IOException {
+ if (tw == null || (tw.getRawMode(0) & TYPE_FILE) != TYPE_FILE) {
+ return null;
+ }
+ ObjectLoader loader =
+ tw.getObjectReader().open(tw.getObjectId(0), OBJ_BLOB);
+ try (InputStream in = loader.openStream();
+ Reader r = new BufferedReader(new InputStreamReader(in, UTF_8))) {
+ return PushCertificateParser.fromReader(r);
+ }
+ }
+
+ /**
+ * Put a certificate to be saved to the store.
+ * <p>
+ * Writes the contents of this certificate for each ref mentioned. It is up to
+ * the caller to ensure this certificate accurately represents the state of
+ * the ref.
+ * <p>
+ * Pending certificates added to this method are not returned by {@link
+ * #get(String)} and {@link #getAll(String)} until after calling {@link
+ * #save()}.
+ *
+ * @param cert
+ * certificate to store.
+ * @param ident
+ * identity for the commit that stores this certificate. Pending
+ * certificates are sorted by identity timestamp during {@link
+ * #save()}.
+ */
+ public void put(PushCertificate cert, PersonIdent ident) {
+ put(cert, ident, null);
+ }
+
+ /**
+ * Put a certificate to be saved to the store, matching a set of commands.
+ * <p>
+ * Like {@link #put(PushCertificate, PersonIdent)}, except a value is only
+ * stored for a push certificate if there is a corresponding command in the
+ * list that exactly matches the old/new values mentioned in the push
+ * certificate.
+ * <p>
+ * Pending certificates added to this method are not returned by {@link
+ * #get(String)} and {@link #getAll(String)} until after calling {@link
+ * #save()}.
+ *
+ * @param cert
+ * certificate to store.
+ * @param ident
+ * identity for the commit that stores this certificate. Pending
+ * certificates are sorted by identity timestamp during {@link
+ * #save()}.
+ * @param matching
+ * only store certs for the refs listed in this list whose values
+ * match the commands in the cert.
+ */
+ public void put(PushCertificate cert, PersonIdent ident,
+ Collection<ReceiveCommand> matching) {
+ pending.add(new PendingCert(cert, ident, matching));
+ }
+
+ /**
+ * Save pending certificates to the store.
+ * <p>
+ * One commit is created per certificate added with {@link
+ * #put(PushCertificate, PersonIdent)}, in order of identity timestamps, and
+ * a single ref update is performed.
+ * <p>
+ * The pending list is cleared if and only the ref update fails, which allows
+ * for easy retries in case of lock failure.
+ *
+ * @return the result of attempting to update the ref.
+ * @throws IOException
+ * if there was an error reading from or writing to the
+ * repository.
+ */
+ public RefUpdate.Result save() throws IOException {
+ ObjectId newId = write();
+ if (newId == null) {
+ return RefUpdate.Result.NO_CHANGE;
+ }
+ try (ObjectInserter inserter = db.newObjectInserter()) {
+ RefUpdate.Result result = updateRef(newId);
+ switch (result) {
+ case FAST_FORWARD:
+ case NEW:
+ case NO_CHANGE:
+ pending.clear();
+ break;
+ default:
+ break;
+ }
+ return result;
+ } finally {
+ close();
+ }
+ }
+
+ /**
+ * Save pending certificates to the store in an existing batch ref update.
+ * <p>
+ * One commit is created per certificate added with {@link
+ * #put(PushCertificate, PersonIdent)}, in order of identity timestamps, all
+ * commits are flushed, and a single command is added to the batch.
+ * <p>
+ * The cached ref value and pending list are <em>not</em> cleared. If the ref
+ * update succeeds, the caller is responsible for calling {@link #close()}
+ * and/or {@link #clear()}.
+ *
+ * @param batch
+ * update to save to.
+ * @return whether a command was added to the batch.
+ * @throws IOException
+ * if there was an error reading from or writing to the
+ * repository.
+ */
+ public boolean save(BatchRefUpdate batch) throws IOException {
+ ObjectId newId = write();
+ if (newId == null || newId.equals(commit)) {
+ return false;
+ }
+ batch.addCommand(new ReceiveCommand(
+ commit != null ? commit : ObjectId.zeroId(), newId, REF_NAME));
+ return true;
+ }
+
+ /**
+ * Clear pending certificates added with {@link #put(PushCertificate,
+ * PersonIdent)}.
+ */
+ public void clear() {
+ pending.clear();
+ }
+
+ private ObjectId write() throws IOException {
+ if (pending.isEmpty()) {
+ return null;
+ }
+ if (reader == null) {
+ load();
+ }
+ sortPending(pending);
+
+ ObjectId curr = commit;
+ DirCache dc = newDirCache();
+ try (ObjectInserter inserter = db.newObjectInserter()) {
+ for (PendingCert pc : pending) {
+ curr = saveCert(inserter, dc, pc, curr);
+ }
+ inserter.flush();
+ return curr;
+ }
+ }
+
+ private static void sortPending(List<PendingCert> pending) {
+ Collections.sort(pending, new Comparator<PendingCert>() {
+ @Override
+ public int compare(PendingCert a, PendingCert b) {
+ return Long.signum(
+ a.ident.getWhen().getTime() - b.ident.getWhen().getTime());
+ }
+ });
+ }
+
+ private DirCache newDirCache() throws IOException {
+ if (commit != null) {
+ return DirCache.read(reader, commit.getTree());
+ }
+ return DirCache.newInCore();
+ }
+
+ private ObjectId saveCert(ObjectInserter inserter, DirCache dc,
+ PendingCert pc, ObjectId curr) throws IOException {
+ Map<String, ReceiveCommand> byRef;
+ if (pc.matching != null) {
+ byRef = new HashMap<>();
+ for (ReceiveCommand cmd : pc.matching) {
+ if (byRef.put(cmd.getRefName(), cmd) != null) {
+ throw new IllegalStateException();
+ }
+ }
+ } else {
+ byRef = null;
+ }
+
+ DirCacheEditor editor = dc.editor();
+ String certText = pc.cert.toText() + pc.cert.getSignature();
+ final ObjectId certId = inserter.insert(OBJ_BLOB, certText.getBytes(UTF_8));
+ boolean any = false;
+ for (ReceiveCommand cmd : pc.cert.getCommands()) {
+ if (byRef != null && !commandsEqual(cmd, byRef.get(cmd.getRefName()))) {
+ continue;
+ }
+ any = true;
+ editor.add(new PathEdit(pathName(cmd.getRefName())) {
+ @Override
+ public void apply(DirCacheEntry ent) {
+ ent.setFileMode(FileMode.REGULAR_FILE);
+ ent.setObjectId(certId);
+ }
+ });
+ }
+ if (!any) {
+ return curr;
+ }
+ editor.finish();
+ CommitBuilder cb = new CommitBuilder();
+ cb.setAuthor(pc.ident);
+ cb.setCommitter(pc.ident);
+ cb.setTreeId(dc.writeTree(inserter));
+ if (curr != null) {
+ cb.setParentId(curr);
+ } else {
+ cb.setParentIds(Collections.<ObjectId> emptyList());
+ }
+ cb.setMessage(buildMessage(pc.cert));
+ return inserter.insert(OBJ_COMMIT, cb.build());
+ }
+
+ private static boolean commandsEqual(ReceiveCommand c1, ReceiveCommand c2) {
+ if (c1 == null || c2 == null) {
+ return c1 == c2;
+ }
+ return c1.getRefName().equals(c2.getRefName())
+ && c1.getOldId().equals(c2.getOldId())
+ && c1.getNewId().equals(c2.getNewId());
+ }
+
+ private RefUpdate.Result updateRef(ObjectId newId) throws IOException {
+ RefUpdate ru = db.updateRef(REF_NAME);
+ ru.setExpectedOldObjectId(commit != null ? commit : ObjectId.zeroId());
+ ru.setNewObjectId(newId);
+ ru.setRefLogIdent(pending.get(pending.size() - 1).ident);
+ ru.setRefLogMessage(JGitText.get().storePushCertReflog, false);
+ try (RevWalk rw = new RevWalk(reader)) {
+ return ru.update(rw);
+ }
+ }
+
+ private TreeWalk newTreeWalk(String refName) throws IOException {
+ if (commit == null) {
+ return null;
+ }
+ return TreeWalk.forPath(reader, pathName(refName), commit.getTree());
+ }
+
+ static String pathName(String refName) {
+ return refName + "@{cert}"; //$NON-NLS-1$
+ }
+
+ private static String buildMessage(PushCertificate cert) {
+ StringBuilder sb = new StringBuilder();
+ if (cert.getCommands().size() == 1) {
+ sb.append(MessageFormat.format(
+ JGitText.get().storePushCertOneRef,
+ cert.getCommands().get(0).getRefName()));
+ } else {
+ sb.append(MessageFormat.format(
+ JGitText.get().storePushCertMultipleRefs,
+ Integer.valueOf(cert.getCommands().size())));
+ }
+ return sb.append('\n').toString();
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java
index 53fba55..5cea882 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java
@@ -47,6 +47,7 @@ import java.io.IOException;
import java.io.OutputStream;
import java.text.MessageFormat;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -104,7 +105,7 @@ class PushProcess {
/**
* Create process for specified transport and refs updates specification.
- *
+ *
* @param transport
* transport between remote and local repository, used to create
* connection.
@@ -155,6 +156,7 @@ class PushProcess {
try {
res.setAdvertisedRefs(transport.getURI(), connection
.getRefsMap());
+ res.peerUserAgent = connection.getPeerUserAgent();
res.setRemoteUpdates(toPush);
monitor.endTask();
@@ -176,17 +178,23 @@ class PushProcess {
}
return res;
} finally {
- walker.release();
+ walker.close();
}
}
private Map<String, RemoteRefUpdate> prepareRemoteUpdates()
throws TransportException {
+ boolean atomic = transport.isPushAtomic();
final Map<String, RemoteRefUpdate> result = new HashMap<String, RemoteRefUpdate>();
for (final RemoteRefUpdate rru : toPush.values()) {
final Ref advertisedRef = connection.getRef(rru.getRemoteName());
- final ObjectId advertisedOld = (advertisedRef == null ? ObjectId
- .zeroId() : advertisedRef.getObjectId());
+ ObjectId advertisedOld = null;
+ if (advertisedRef != null) {
+ advertisedOld = advertisedRef.getObjectId();
+ }
+ if (advertisedOld == null) {
+ advertisedOld = ObjectId.zeroId();
+ }
if (rru.getNewObjectId().equals(advertisedOld)) {
if (rru.isDelete()) {
@@ -204,8 +212,14 @@ class PushProcess {
if (rru.isExpectingOldObjectId()
&& !rru.getExpectedOldObjectId().equals(advertisedOld)) {
rru.setStatus(Status.REJECTED_REMOTE_CHANGED);
+ if (atomic) {
+ return rejectAll();
+ }
continue;
}
+ if (!rru.isExpectingOldObjectId()) {
+ rru.setExpectedOldObjectId(advertisedOld);
+ }
// create ref (hasn't existed on remote side) and delete ref
// are always fast-forward commands, feasible at this level
@@ -235,14 +249,28 @@ class PushProcess {
JGitText.get().readingObjectsFromLocalRepositoryFailed, x.getMessage()), x);
}
rru.setFastForward(fastForward);
- if (!fastForward && !rru.isForceUpdate())
+ if (!fastForward && !rru.isForceUpdate()) {
rru.setStatus(Status.REJECTED_NONFASTFORWARD);
- else
+ if (atomic) {
+ return rejectAll();
+ }
+ } else {
result.put(rru.getRemoteName(), rru);
+ }
}
return result;
}
+ private Map<String, RemoteRefUpdate> rejectAll() {
+ for (RemoteRefUpdate rru : toPush.values()) {
+ if (rru.getStatus() == Status.NOT_ATTEMPTED) {
+ rru.setStatus(RemoteRefUpdate.Status.REJECTED_OTHER_REASON);
+ rru.setMessage(JGitText.get().transactionAborted);
+ }
+ }
+ return Collections.emptyMap();
+ }
+
private void modifyUpdatesForDryRun() {
for (final RemoteRefUpdate rru : toPush.values())
if (rru.getStatus() == Status.NOT_ATTEMPTED)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java
index 7c44dba..2b21c4a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java
@@ -43,9 +43,13 @@
package org.eclipse.jgit.transport;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
+import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
+
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import org.eclipse.jgit.internal.JGitText;
@@ -127,6 +131,31 @@ public class ReceiveCommand {
}
/**
+ * Filter a collection of commands according to result.
+ *
+ * @param in
+ * commands to filter.
+ * @param want
+ * desired status to filter by.
+ * @return a copy of the command list containing only those commands with
+ * the desired status.
+ * @since 4.2
+ */
+ public static List<ReceiveCommand> filter(Iterable<ReceiveCommand> in,
+ Result want) {
+ List<ReceiveCommand> r;
+ if (in instanceof Collection)
+ r = new ArrayList<>(((Collection<?>) in).size());
+ else
+ r = new ArrayList<>();
+ for (ReceiveCommand cmd : in) {
+ if (cmd.getResult() == want)
+ r.add(cmd);
+ }
+ return r;
+ }
+
+ /**
* Filter a list of commands according to result.
*
* @param commands
@@ -138,13 +167,27 @@ public class ReceiveCommand {
* @since 2.0
*/
public static List<ReceiveCommand> filter(List<ReceiveCommand> commands,
- final Result want) {
- List<ReceiveCommand> r = new ArrayList<ReceiveCommand>(commands.size());
- for (final ReceiveCommand cmd : commands) {
- if (cmd.getResult() == want)
- r.add(cmd);
+ Result want) {
+ return filter((Iterable<ReceiveCommand>) commands, want);
+ }
+
+ /**
+ * Set unprocessed commands as failed due to transaction aborted.
+ * <p>
+ * If a command is still {@link Result#NOT_ATTEMPTED} it will be set to
+ * {@link Result#REJECTED_OTHER_REASON}.
+ *
+ * @param commands
+ * commands to mark as failed.
+ * @since 4.2
+ */
+ public static void abort(Iterable<ReceiveCommand> commands) {
+ for (ReceiveCommand c : commands) {
+ if (c.getResult() == NOT_ATTEMPTED) {
+ c.setResult(REJECTED_OTHER_REASON,
+ JGitText.get().transactionAborted);
+ }
}
- return r;
}
private final ObjectId oldId;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
index e5eb822..6ed9bc8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
@@ -240,10 +240,17 @@ public class ReceivePack extends BaseReceivePack {
});
}
- postReceive.onPostReceive(this, filterCommands(Result.OK));
-
- if (unpackError != null)
+ if (unpackError != null) {
+ // we already know which exception to throw. Ignore
+ // potential additional exceptions raised in postReceiveHooks
+ try {
+ postReceive.onPostReceive(this, filterCommands(Result.OK));
+ } catch (Throwable e) {
+ // empty
+ }
throw new UnpackException(unpackError);
+ }
+ postReceive.onPostReceive(this, filterCommands(Result.OK));
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
index 76547a6..f72a4b2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefAdvertiser.java
@@ -148,6 +148,21 @@ public abstract class RefAdvertiser {
}
/**
+ * Add one protocol capability with a value ({@code "name=value"}).
+ *
+ * @param name
+ * name of the capability.
+ * @param value
+ * value. If null the capability will not be added.
+ * @since 4.0
+ */
+ public void advertiseCapability(String name, String value) {
+ if (value != null) {
+ capablities.add(name + '=' + value);
+ }
+ }
+
+ /**
* Add a symbolic ref to capabilities.
* <p>
* This method must be invoked prior to any of the following:
@@ -164,8 +179,7 @@ public abstract class RefAdvertiser {
* @since 3.6
*/
public void addSymref(String from, String to) {
- String symref = String.format("%s=%s:%s", OPTION_SYMREF, from, to); //$NON-NLS-1$
- advertiseCapability(symref);
+ advertiseCapability(OPTION_SYMREF, from + ':' + to);
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java
index 66ffc3a..0e803bd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java
@@ -457,10 +457,6 @@ public class RefSpec implements Serializable {
if (i != -1) {
if (s.indexOf('*', i + 1) > i)
return false;
- if (i > 0 && s.charAt(i - 1) != '/')
- return false;
- if (i < s.length() - 1 && s.charAt(i + 1) != '/')
- return false;
}
return true;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java
index 1b82a36..5c58346 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java
@@ -125,7 +125,7 @@ public class RemoteRefUpdate {
OK;
}
- private final ObjectId expectedOldObjectId;
+ private ObjectId expectedOldObjectId;
private final ObjectId newObjectId;
@@ -440,6 +440,10 @@ public class RemoteRefUpdate {
return message;
}
+ void setExpectedOldObjectId(ObjectId id) {
+ expectedOldObjectId = id;
+ }
+
void setStatus(final Status status) {
this.status = status;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java
index cf388e2..fe9f2a3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SideBandInputStream.java
@@ -78,12 +78,8 @@ import org.eclipse.jgit.util.RawParseUtils;
* @see SideBandOutputStream
*/
class SideBandInputStream extends InputStream {
- private static final String PFX_REMOTE = JGitText.get().prefixRemote;
-
static final int CH_DATA = 1;
-
static final int CH_PROGRESS = 2;
-
static final int CH_ERROR = 3;
private static Pattern P_UNBOUNDED = Pattern
@@ -174,7 +170,7 @@ class SideBandInputStream extends InputStream {
continue;
case CH_ERROR:
eof = true;
- throw new TransportException(PFX_REMOTE + readString(available));
+ throw new TransportException(remote(readString(available)));
default:
throw new PackProtocolException(
MessageFormat.format(JGitText.get().invalidChannel,
@@ -241,7 +237,18 @@ class SideBandInputStream extends InputStream {
}
private void beginTask(final int totalWorkUnits) {
- monitor.beginTask(PFX_REMOTE + currentTask, totalWorkUnits);
+ monitor.beginTask(remote(currentTask), totalWorkUnits);
+ }
+
+ private static String remote(String msg) {
+ String prefix = JGitText.get().prefixRemote;
+ StringBuilder r = new StringBuilder(prefix.length() + msg.length() + 1);
+ r.append(prefix);
+ if (prefix.length() > 0 && prefix.charAt(prefix.length() - 1) != ' ') {
+ r.append(' ');
+ }
+ r.append(msg);
+ return r.toString();
}
private String readString(final int len) throws IOException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SignedPushConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SignedPushConfig.java
new file mode 100644
index 0000000..942e7d7
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SignedPushConfig.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Config.SectionParser;
+
+/**
+ * Configuration for server-side signed push verification.
+ *
+ * @since 4.1
+ */
+public class SignedPushConfig {
+ /** Key for {@link Config#get(SectionParser)}. */
+ public static final SectionParser<SignedPushConfig> KEY =
+ new SectionParser<SignedPushConfig>() {
+ public SignedPushConfig parse(Config cfg) {
+ return new SignedPushConfig(cfg);
+ }
+ };
+
+ private String certNonceSeed;
+ private int certNonceSlopLimit;
+ private NonceGenerator nonceGenerator;
+
+ /** Create a new config with default values disabling push verification. */
+ public SignedPushConfig() {
+ }
+
+ SignedPushConfig(Config cfg) {
+ setCertNonceSeed(cfg.getString("receive", null, "certnonceseed")); //$NON-NLS-1$ //$NON-NLS-2$
+ certNonceSlopLimit = cfg.getInt("receive", "certnonceslop", 0); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Set the seed used by the nonce verifier.
+ * <p>
+ * Setting this to a non-null value enables push certificate verification
+ * using the default {@link HMACSHA1NonceGenerator} implementation, if a
+ * different implementation was not set using {@link
+ * #setNonceGenerator(NonceGenerator)}.
+ *
+ * @param seed
+ * new seed value.
+ */
+ public void setCertNonceSeed(String seed) {
+ certNonceSeed = seed;
+ }
+
+ /** @return the configured seed. */
+ public String getCertNonceSeed() {
+ return certNonceSeed;
+ }
+
+ /**
+ * Set the nonce slop limit.
+ * <p>
+ * Old but valid nonces within this limit will be accepted.
+ *
+ * @param limit
+ * new limit in seconds.
+ */
+ public void setCertNonceSlopLimit(int limit) {
+ certNonceSlopLimit = limit;
+ }
+
+ /** @return the configured nonce slop limit. */
+ public int getCertNonceSlopLimit() {
+ return certNonceSlopLimit;
+ }
+
+ /**
+ * Set the {@link NonceGenerator} used for signed pushes.
+ * <p>
+ * Setting this to a non-null value enables push certificate verification. If
+ * this method is called, this implementation will be used instead of the
+ * default {@link HMACSHA1NonceGenerator} even if {@link
+ * #setCertNonceSeed(String)} was called.
+ *
+ * @param generator
+ * new nonce generator.
+ */
+ public void setNonceGenerator(NonceGenerator generator) {
+ nonceGenerator = generator;
+ }
+
+ /**
+ * Get the {@link NonceGenerator} used for signed pushes.
+ * <p>
+ * If {@link #setNonceGenerator(NonceGenerator)} was used to set a non-null
+ * implementation, that will be returned. If no custom implementation was set
+ * but {@link #setCertNonceSeed(String)} was called, returns a newly-created
+ * {@link HMACSHA1NonceGenerator}.
+ *
+ * @return the configured nonce generator.
+ */
+ public NonceGenerator getNonceGenerator() {
+ if (nonceGenerator != null) {
+ return nonceGenerator;
+ } else if (certNonceSeed != null) {
+ return new HMACSHA1NonceGenerator(certNonceSeed);
+ }
+ return null;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TestProtocol.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TestProtocol.java
new file mode 100644
index 0000000..5fd2f84
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TestProtocol.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import java.net.URISyntaxException;
+import java.text.MessageFormat;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Set;
+
+import org.eclipse.jgit.errors.NotSupportedException;
+import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
+import org.eclipse.jgit.transport.resolver.UploadPackFactory;
+
+/**
+ * Protocol for transport between manually-specified repositories in tests.
+ * <p>
+ * Remote repositories are registered using {@link #register(Object,
+ * Repository)}, after which they can be accessed using the returned URI. As
+ * this class provides both the client side (the protocol) and the server side,
+ * the caller is responsible for setting up and passing the connection context,
+ * whatever form that may take.
+ * <p>
+ * Unlike the other built-in protocols, which are automatically-registered
+ * singletons, callers are expected to register/unregister specific protocol
+ * instances on demand with {@link Transport#register(TransportProtocol)}.
+ *
+ * @param <C>
+ * the connection type
+ * @since 4.0
+ */
+public class TestProtocol<C> extends TransportProtocol {
+ private static final String SCHEME = "test"; //$NON-NLS-1$
+
+ private class Handle {
+ final C req;
+ final Repository remote;
+
+ Handle(C req, Repository remote) {
+ this.req = req;
+ this.remote = remote;
+ }
+ }
+
+ final UploadPackFactory<C> uploadPackFactory;
+ final ReceivePackFactory<C> receivePackFactory;
+ private final HashMap<URIish, Handle> handles;
+
+ /**
+ * @param uploadPackFactory
+ * factory for creating {@link UploadPack} used by all connections
+ * from this protocol instance.
+ * @param receivePackFactory
+ * factory for creating {@link ReceivePack} used by all connections
+ * from this protocol instance.
+ */
+ public TestProtocol(UploadPackFactory<C> uploadPackFactory,
+ ReceivePackFactory<C> receivePackFactory) {
+ this.uploadPackFactory = uploadPackFactory;
+ this.receivePackFactory = receivePackFactory;
+ this.handles = new HashMap<URIish, Handle>();
+ }
+
+ @Override
+ public String getName() {
+ return JGitText.get().transportProtoTest;
+ }
+
+ @Override
+ public Set<String> getSchemes() {
+ return Collections.singleton(SCHEME);
+ }
+
+ @Override
+ public Transport open(URIish uri, Repository local, String remoteName)
+ throws NotSupportedException, TransportException {
+ Handle h = handles.get(uri);
+ if (h == null) {
+ throw new NotSupportedException(MessageFormat.format(
+ JGitText.get().URINotSupported, uri));
+ }
+ return new TransportInternal(local, uri, h);
+ }
+
+ @Override
+ public Set<URIishField> getRequiredFields() {
+ return EnumSet.of(URIishField.HOST, URIishField.PATH);
+ }
+
+ @Override
+ public Set<URIishField> getOptionalFields() {
+ return Collections.emptySet();
+ }
+
+ /**
+ * Register a repository connection over the internal test protocol.
+ *
+ * @param req
+ * connection context. This instance is reused for all connections
+ * made using this protocol; if it is stateful and usable only for
+ * one connection, the same repository should be registered
+ * multiple times.
+ * @param remote
+ * remote repository to connect to.
+ * @return a URI that can be used to connect to this repository for both fetch
+ * and push.
+ */
+ public synchronized URIish register(C req, Repository remote) {
+ URIish uri;
+ try {
+ int n = handles.size();
+ uri = new URIish(SCHEME + "://test/conn" + n); //$NON-NLS-1$
+ } catch (URISyntaxException e) {
+ throw new IllegalStateException();
+ }
+ handles.put(uri, new Handle(req, remote));
+ return uri;
+ }
+
+ private class TransportInternal extends Transport implements PackTransport {
+ private final Handle handle;
+
+ TransportInternal(Repository local, URIish uri, Handle handle) {
+ super(local, uri);
+ this.handle = handle;
+ }
+
+ @Override
+ public FetchConnection openFetch() throws NotSupportedException,
+ TransportException {
+ handle.remote.incrementOpen();
+ return new InternalFetchConnection<C>(
+ this, uploadPackFactory, handle.req, handle.remote);
+ }
+
+ @Override
+ public PushConnection openPush() throws NotSupportedException,
+ TransportException {
+ handle.remote.incrementOpen();
+ return new InternalPushConnection<C>(
+ this, receivePackFactory, handle.req, handle.remote);
+ }
+
+ @Override
+ public void close() {
+ // Resources must be established per-connection.
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TrackingRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TrackingRefUpdate.java
index 1ef3fbf..5aae5ea 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TrackingRefUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TrackingRefUpdate.java
@@ -52,10 +52,10 @@ import org.eclipse.jgit.lib.RefUpdate;
/** Update of a locally stored tracking branch. */
public class TrackingRefUpdate {
private final String remoteName;
- private final String localName;
- private boolean forceUpdate;
- private ObjectId oldObjectId;
- private ObjectId newObjectId;
+ final String localName;
+ boolean forceUpdate;
+ ObjectId oldObjectId;
+ ObjectId newObjectId;
private RefUpdate.Result result;
private ReceiveCommand cmd;
@@ -142,7 +142,7 @@ public class TrackingRefUpdate {
}
final class Command extends ReceiveCommand {
- private Command() {
+ Command() {
super(oldObjectId, newObjectId, localName);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
index 1de91a5..72c9c8b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
@@ -43,12 +43,20 @@
package org.eclipse.jgit.transport;
+import static org.eclipse.jgit.util.StringUtils.equalsIgnoreCase;
+import static org.eclipse.jgit.util.StringUtils.toLowerCase;
+
+import java.io.File;
+import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.internal.storage.file.LazyObjectIdSetFile;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Config.SectionParser;
import org.eclipse.jgit.lib.ObjectChecker;
+import org.eclipse.jgit.lib.ObjectIdSet;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.util.SystemReader;
@@ -58,6 +66,8 @@ import org.eclipse.jgit.util.SystemReader;
* parameters.
*/
public class TransferConfig {
+ private static final String FSCK = "fsck"; //$NON-NLS-1$
+
/** Key for {@link Config#get(SectionParser)}. */
public static final Config.SectionParser<TransferConfig> KEY = new SectionParser<TransferConfig>() {
public TransferConfig parse(final Config cfg) {
@@ -65,56 +75,111 @@ public class TransferConfig {
}
};
- private final boolean checkReceivedObjects;
- private final boolean allowLeadingZeroFileMode;
+ enum FsckMode {
+ ERROR, WARN, IGNORE;
+ }
+
+ private final boolean fetchFsck;
+ private final boolean receiveFsck;
+ private final String fsckSkipList;
+ private final EnumSet<ObjectChecker.ErrorType> ignore;
+ private final boolean allowInvalidPersonIdent;
private final boolean safeForWindows;
private final boolean safeForMacOS;
private final boolean allowTipSha1InWant;
- private final String[] hideRefs;
+ private final boolean allowReachableSha1InWant;
+ final String[] hideRefs;
TransferConfig(final Repository db) {
this(db.getConfig());
}
- private TransferConfig(final Config rc) {
- checkReceivedObjects = rc.getBoolean(
- "fetch", "fsckobjects", //$NON-NLS-1$ //$NON-NLS-2$
- rc.getBoolean("transfer", "fsckobjects", false)); //$NON-NLS-1$ //$NON-NLS-2$
- allowLeadingZeroFileMode = checkReceivedObjects
- && rc.getBoolean("fsck", "allowLeadingZeroFileMode", false); //$NON-NLS-1$ //$NON-NLS-2$
- safeForWindows = checkReceivedObjects
- && rc.getBoolean("fsck", "safeForWindows", //$NON-NLS-1$ //$NON-NLS-2$
+ TransferConfig(final Config rc) {
+ boolean fsck = rc.getBoolean("transfer", "fsckobjects", false); //$NON-NLS-1$ //$NON-NLS-2$
+ fetchFsck = rc.getBoolean("fetch", "fsckobjects", fsck); //$NON-NLS-1$ //$NON-NLS-2$
+ receiveFsck = rc.getBoolean("receive", "fsckobjects", fsck); //$NON-NLS-1$ //$NON-NLS-2$
+ fsckSkipList = rc.getString(FSCK, null, "skipList"); //$NON-NLS-1$
+ allowInvalidPersonIdent = rc.getBoolean(FSCK, "allowInvalidPersonIdent", false); //$NON-NLS-1$
+ safeForWindows = rc.getBoolean(FSCK, "safeForWindows", //$NON-NLS-1$
SystemReader.getInstance().isWindows());
- safeForMacOS = checkReceivedObjects
- && rc.getBoolean("fsck", "safeForMacOS", //$NON-NLS-1$ //$NON-NLS-2$
+ safeForMacOS = rc.getBoolean(FSCK, "safeForMacOS", //$NON-NLS-1$
SystemReader.getInstance().isMacOS());
+ ignore = EnumSet.noneOf(ObjectChecker.ErrorType.class);
+ EnumSet<ObjectChecker.ErrorType> set = EnumSet
+ .noneOf(ObjectChecker.ErrorType.class);
+ for (String key : rc.getNames(FSCK)) {
+ if (equalsIgnoreCase(key, "skipList") //$NON-NLS-1$
+ || equalsIgnoreCase(key, "allowLeadingZeroFileMode") //$NON-NLS-1$
+ || equalsIgnoreCase(key, "allowInvalidPersonIdent") //$NON-NLS-1$
+ || equalsIgnoreCase(key, "safeForWindows") //$NON-NLS-1$
+ || equalsIgnoreCase(key, "safeForMacOS")) { //$NON-NLS-1$
+ continue;
+ }
+
+ ObjectChecker.ErrorType id = FsckKeyNameHolder.parse(key);
+ if (id != null) {
+ switch (rc.getEnum(FSCK, null, key, FsckMode.ERROR)) {
+ case ERROR:
+ ignore.remove(id);
+ break;
+ case WARN:
+ case IGNORE:
+ ignore.add(id);
+ break;
+ }
+ set.add(id);
+ }
+ }
+ if (!set.contains(ObjectChecker.ErrorType.ZERO_PADDED_FILEMODE)
+ && rc.getBoolean(FSCK, "allowLeadingZeroFileMode", false)) { //$NON-NLS-1$
+ ignore.add(ObjectChecker.ErrorType.ZERO_PADDED_FILEMODE);
+ }
+
allowTipSha1InWant = rc.getBoolean(
"uploadpack", "allowtipsha1inwant", false); //$NON-NLS-1$ //$NON-NLS-2$
+ allowReachableSha1InWant = rc.getBoolean(
+ "uploadpack", "allowreachablesha1inwant", false); //$NON-NLS-1$ //$NON-NLS-2$
hideRefs = rc.getStringList("uploadpack", null, "hiderefs"); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
- * @return strictly verify received objects?
- * @deprecated use {@link #newObjectChecker()} instead.
- */
- @Deprecated
- public boolean isFsckObjects() {
- return checkReceivedObjects;
- }
-
- /**
* @return checker to verify fetched objects, or null if checking is not
* enabled in the repository configuration.
* @since 3.6
*/
+ @Nullable
public ObjectChecker newObjectChecker() {
- if (!checkReceivedObjects)
+ return newObjectChecker(fetchFsck);
+ }
+
+ /**
+ * @return checker to verify objects pushed into this repository, or null if
+ * checking is not enabled in the repository configuration.
+ * @since 4.2
+ */
+ @Nullable
+ public ObjectChecker newReceiveObjectChecker() {
+ return newObjectChecker(receiveFsck);
+ }
+
+ private ObjectChecker newObjectChecker(boolean check) {
+ if (!check) {
return null;
+ }
return new ObjectChecker()
- .setAllowLeadingZeroFileMode(allowLeadingZeroFileMode)
+ .setIgnore(ignore)
+ .setAllowInvalidPersonIdent(allowInvalidPersonIdent)
.setSafeForWindows(safeForWindows)
- .setSafeForMacOS(safeForMacOS);
+ .setSafeForMacOS(safeForMacOS)
+ .setSkipList(skipList());
+ }
+
+ private ObjectIdSet skipList() {
+ if (fsckSkipList != null && !fsckSkipList.isEmpty()) {
+ return new LazyObjectIdSetFile(new File(fsckSkipList));
+ }
+ return null;
}
/**
@@ -126,6 +191,14 @@ public class TransferConfig {
}
/**
+ * @return allow clients to request non-tip SHA-1s?
+ * @since 4.1
+ */
+ public boolean isAllowReachableSha1InWant() {
+ return allowReachableSha1InWant;
+ }
+
+ /**
* @return {@link RefFilter} respecting configured hidden refs.
* @since 3.1
*/
@@ -155,4 +228,34 @@ public class TransferConfig {
}
};
}
+
+ static class FsckKeyNameHolder {
+ private static final Map<String, ObjectChecker.ErrorType> errors;
+
+ static {
+ errors = new HashMap<>();
+ for (ObjectChecker.ErrorType m : ObjectChecker.ErrorType.values()) {
+ errors.put(keyNameFor(m.name()), m);
+ }
+ }
+
+ @Nullable
+ static ObjectChecker.ErrorType parse(String key) {
+ return errors.get(toLowerCase(key));
+ }
+
+ private static String keyNameFor(String name) {
+ StringBuilder r = new StringBuilder(name.length());
+ for (int i = 0; i < name.length(); i++) {
+ char c = name.charAt(i);
+ if (c != '_') {
+ r.append(c);
+ }
+ }
+ return toLowerCase(r.toString());
+ }
+
+ private FsckKeyNameHolder() {
+ }
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
index 2185622..9e6d1f6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
@@ -53,6 +53,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
+import java.io.PrintStream;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
@@ -70,8 +71,11 @@ import java.util.Map;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;
+import org.eclipse.jgit.api.errors.AbortedByHookException;
import org.eclipse.jgit.errors.NotSupportedException;
import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.hooks.Hooks;
+import org.eclipse.jgit.hooks.PrePushHook;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
@@ -94,7 +98,7 @@ import org.eclipse.jgit.storage.pack.PackConfig;
* Transport instances and the connections they create are not thread-safe.
* Callers must ensure a transport is accessed by only one thread at a time.
*/
-public abstract class Transport {
+public abstract class Transport implements AutoCloseable {
/** Type of operation a Transport is being opened for. */
public enum Operation {
/** Transport is to fetch objects locally. */
@@ -557,8 +561,13 @@ public abstract class Transport {
continue;
}
- if (proto.canHandle(uri, local, remoteName))
- return proto.open(uri, local, remoteName);
+ if (proto.canHandle(uri, local, remoteName)) {
+ Transport tn = proto.open(uri, local, remoteName);
+ tn.prePush = Hooks.prePush(local, tn.hookOutRedirect);
+ tn.prePush.setRemoteLocation(uri.toString());
+ tn.prePush.setRemoteName(remoteName);
+ return tn;
+ }
}
throw new NotSupportedException(MessageFormat.format(JGitText.get().URINotSupported, uri));
@@ -743,6 +752,9 @@ public abstract class Transport {
/** Should push produce thin-pack when sending objects to remote repository. */
private boolean pushThin = DEFAULT_PUSH_THIN;
+ /** Should push be all-or-nothing atomic behavior? */
+ private boolean pushAtomic;
+
/** Should push just check for operation result, not really push. */
private boolean dryRun;
@@ -761,6 +773,9 @@ public abstract class Transport {
/** Assists with authentication the connection. */
private CredentialsProvider credentialsProvider;
+ private PrintStream hookOutRedirect;
+
+ private PrePushHook prePush;
/**
* Create a new transport instance.
*
@@ -778,6 +793,7 @@ public abstract class Transport {
this.uri = uri;
this.objectChecker = tc.newObjectChecker();
this.credentialsProvider = CredentialsProvider.getDefault();
+ prePush = Hooks.prePush(local, hookOutRedirect);
}
/**
@@ -957,6 +973,31 @@ public abstract class Transport {
}
/**
+ * Default setting is false.
+ *
+ * @return true if push requires all-or-nothing atomic behavior.
+ * @since 4.2
+ */
+ public boolean isPushAtomic() {
+ return pushAtomic;
+ }
+
+ /**
+ * Request atomic push (all references succeed, or none do).
+ * <p>
+ * Server must also support atomic push. If the server does not support the
+ * feature the push will abort without making changes.
+ *
+ * @param atomic
+ * true when push should be an all-or-nothing operation.
+ * @see PackTransport
+ * @since 4.2
+ */
+ public void setPushAtomic(final boolean atomic) {
+ this.pushAtomic = atomic;
+ }
+
+ /**
* @return true if destination refs should be removed if they no longer
* exist at the source repository.
*/
@@ -1196,6 +1237,15 @@ public abstract class Transport {
if (toPush.isEmpty())
throw new TransportException(JGitText.get().nothingToPush);
}
+ if (prePush != null) {
+ try {
+ prePush.setRefs(toPush);
+ prePush.call();
+ } catch (AbortedByHookException | IOException e) {
+ throw new TransportException(e.getMessage(), e);
+ }
+ }
+
final PushProcess pushProcess = new PushProcess(this, toPush, out);
return pushProcess.execute(monitor);
}
@@ -1303,6 +1353,10 @@ public abstract class Transport {
* must close that network socket, disconnecting the two peers. If the
* remote repository is actually local (same system) this method must close
* any open file handles used to read the "remote" repository.
+ * <p>
+ * {@code AutoClosable.close()} declares that it throws {@link Exception}.
+ * Implementers shouldn't throw checked exceptions. This override narrows
+ * the signature to prevent them from doing so.
*/
public abstract void close();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
index afaaa69..23c506b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
@@ -125,10 +125,10 @@ public class TransportAmazonS3 extends HttpTransport implements WalkTransport {
};
/** User information necessary to connect to S3. */
- private final AmazonS3 s3;
+ final AmazonS3 s3;
/** Bucket the remote repository is stored in. */
- private final String bucket;
+ final String bucket;
/**
* Key prefix which all objects related to the repository start with.
@@ -148,8 +148,9 @@ public class TransportAmazonS3 extends HttpTransport implements WalkTransport {
super(local, uri);
Properties props = loadProperties();
- if (!props.contains("tmpdir") && local.getDirectory() != null) //$NON-NLS-1$
- props.put("tmpdir", local.getDirectory().getPath()); //$NON-NLS-1$
+ File directory = local.getDirectory();
+ if (!props.containsKey("tmpdir") && directory != null) //$NON-NLS-1$
+ props.put("tmpdir", directory.getPath()); //$NON-NLS-1$
s3 = new AmazonS3(props);
bucket = uri.getHost();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
index b27fa0d..52f0f04 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
@@ -46,6 +46,7 @@
package org.eclipse.jgit.transport;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
@@ -235,9 +236,10 @@ public class TransportGitSsh extends SshTransport implements PackTransport {
ProcessBuilder pb = new ProcessBuilder();
pb.command(args);
- if (local.getDirectory() != null)
+ File directory = local.getDirectory();
+ if (directory != null)
pb.environment().put(Constants.GIT_DIR_KEY,
- local.getDirectory().getPath());
+ directory.getPath());
try {
return pb.start();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
index 82d1737..5948278 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
@@ -134,8 +134,6 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
private static final String SVC_RECEIVE_PACK = "git-receive-pack"; //$NON-NLS-1$
- private static final String userAgent = computeUserAgent();
-
static final TransportProtocol PROTO_HTTP = new TransportProtocol() {
private final String[] schemeNames = { "http", "https" }; //$NON-NLS-1$ //$NON-NLS-2$
@@ -204,17 +202,6 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
}
};
- private static String computeUserAgent() {
- String version;
- final Package pkg = TransportHttp.class.getPackage();
- if (pkg != null && pkg.getImplementationVersion() != null) {
- version = pkg.getImplementationVersion();
- } else {
- version = "unknown"; //$NON-NLS-1$
- }
- return "JGit/" + version; //$NON-NLS-1$
- }
-
private static final Config.SectionParser<HttpConfig> HTTP_KEY = new SectionParser<HttpConfig>() {
public HttpConfig parse(final Config cfg) {
return new HttpConfig(cfg);
@@ -231,16 +218,16 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
sslVerify = rc.getBoolean("http", "sslVerify", true); //$NON-NLS-1$ //$NON-NLS-2$
}
- private HttpConfig() {
+ HttpConfig() {
this(new Config());
}
}
- private final URL baseUrl;
+ final URL baseUrl;
private final URL objectsUrl;
- private final HttpConfig http;
+ final HttpConfig http;
private final ProxySelector proxySelector;
@@ -309,16 +296,17 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
final HttpConnection c = connect(service);
final InputStream in = openInputStream(c);
try {
+ BaseConnection f;
if (isSmartHttp(c, service)) {
readSmartHeaders(in, service);
- return new SmartHttpFetchConnection(in);
-
+ f = new SmartHttpFetchConnection(in);
} else {
// Assume this server doesn't support smart HTTP fetch
// and fall back on dumb object walking.
- //
- return newDumbConnection(in);
+ f = newDumbConnection(in);
}
+ f.setPeerUserAgent(c.getHeaderField(HttpSupport.HDR_SERVER));
+ return (FetchConnection) f;
} finally {
in.close();
}
@@ -331,7 +319,7 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
}
}
- private FetchConnection newDumbConnection(InputStream in)
+ private WalkFetchConnection newDumbConnection(InputStream in)
throws IOException, PackProtocolException {
HttpObjectDB d = new HttpObjectDB(objectsUrl);
BufferedReader br = toBufferedReader(in);
@@ -400,9 +388,7 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
final InputStream in = openInputStream(c);
try {
if (isSmartHttp(c, service)) {
- readSmartHeaders(in, service);
- return new SmartHttpPushConnection(in);
-
+ return smartPush(service, c, in);
} else if (!useSmartHttp) {
final String msg = JGitText.get().smartHTTPPushDisabled;
throw new NotSupportedException(msg);
@@ -423,6 +409,14 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
}
}
+ private PushConnection smartPush(String service, HttpConnection c,
+ InputStream in) throws IOException, TransportException {
+ readSmartHeaders(in, service);
+ SmartHttpPushConnection p = new SmartHttpPushConnection(in);
+ p.setPeerUserAgent(c.getHeaderField(HttpSupport.HDR_SERVER));
+ return p;
+ }
+
@Override
public void close() {
// No explicit connections are maintained.
@@ -551,7 +545,9 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
conn.setUseCaches(false);
conn.setRequestProperty(HDR_ACCEPT_ENCODING, ENCODING_GZIP);
conn.setRequestProperty(HDR_PRAGMA, "no-cache"); //$NON-NLS-1$
- conn.setRequestProperty(HDR_USER_AGENT, userAgent);
+ if (UserAgent.get() != null) {
+ conn.setRequestProperty(HDR_USER_AGENT, UserAgent.get());
+ }
int timeOut = getTimeout();
if (timeOut != -1) {
int effTimeOut = timeOut * 1000;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
index 81caf66..292ba7e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java
@@ -52,8 +52,6 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.io.PipedInputStream;
-import java.io.PipedOutputStream;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
@@ -65,6 +63,8 @@ import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryBuilder;
import org.eclipse.jgit.lib.RepositoryCache;
+import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
+import org.eclipse.jgit.transport.resolver.UploadPackFactory;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.io.MessageWriter;
import org.eclipse.jgit.util.io.SafeBufferedOutputStream;
@@ -168,21 +168,44 @@ class TransportLocal extends Transport implements PackTransport {
return new ReceivePack(dst);
}
+ private Repository openRepo() throws TransportException {
+ try {
+ return new RepositoryBuilder().setGitDir(remoteGitDir).build();
+ } catch (IOException err) {
+ throw new TransportException(uri, JGitText.get().notAGitDirectory);
+ }
+ }
+
@Override
public FetchConnection openFetch() throws TransportException {
final String up = getOptionUploadPack();
- if ("git-upload-pack".equals(up) || "git upload-pack".equals(up)) //$NON-NLS-1$ //$NON-NLS-2$
- return new InternalLocalFetchConnection();
- return new ForkLocalFetchConnection();
+ if (!"git-upload-pack".equals(up) //$NON-NLS-1$
+ && !"git upload-pack".equals(up)) //$NON-NLS-1$
+ return new ForkLocalFetchConnection();
+
+ UploadPackFactory<Void> upf = new UploadPackFactory<Void>() {
+ @Override
+ public UploadPack create(Void req, Repository db) {
+ return createUploadPack(db);
+ }
+ };
+ return new InternalFetchConnection<Void>(this, upf, null, openRepo());
}
@Override
- public PushConnection openPush() throws NotSupportedException,
- TransportException {
+ public PushConnection openPush() throws TransportException {
final String rp = getOptionReceivePack();
- if ("git-receive-pack".equals(rp) || "git receive-pack".equals(rp)) //$NON-NLS-1$ //$NON-NLS-2$
- return new InternalLocalPushConnection();
- return new ForkLocalPushConnection();
+ if (!"git-receive-pack".equals(rp) //$NON-NLS-1$
+ && !"git receive-pack".equals(rp)) //$NON-NLS-1$
+ return new ForkLocalPushConnection();
+
+ ReceivePackFactory<Void> rpf = new ReceivePackFactory<Void>() {
+ @Override
+ public ReceivePack create(Void req, Repository db) {
+ return createReceivePack(db);
+ }
+ };
+ return new InternalPushConnection<Void>(this, rpf, null, openRepo());
}
@Override
@@ -214,93 +237,6 @@ class TransportLocal extends Transport implements PackTransport {
}
}
- class InternalLocalFetchConnection extends BasePackFetchConnection {
- private Thread worker;
-
- InternalLocalFetchConnection() throws TransportException {
- super(TransportLocal.this);
-
- final Repository dst;
- try {
- dst = new RepositoryBuilder().setGitDir(remoteGitDir).build();
- } catch (IOException err) {
- throw new TransportException(uri, JGitText.get().notAGitDirectory);
- }
-
- final PipedInputStream in_r;
- final PipedOutputStream in_w;
-
- final PipedInputStream out_r;
- final PipedOutputStream out_w;
- try {
- in_r = new PipedInputStream();
- in_w = new PipedOutputStream(in_r);
-
- out_r = new PipedInputStream() {
- // The client (BasePackFetchConnection) can write
- // a huge burst before it reads again. We need to
- // force the buffer to be big enough, otherwise it
- // will deadlock both threads.
- {
- buffer = new byte[MIN_CLIENT_BUFFER];
- }
- };
- out_w = new PipedOutputStream(out_r);
- } catch (IOException err) {
- dst.close();
- throw new TransportException(uri, JGitText.get().cannotConnectPipes, err);
- }
-
- worker = new Thread("JGit-Upload-Pack") { //$NON-NLS-1$
- public void run() {
- try {
- final UploadPack rp = createUploadPack(dst);
- rp.upload(out_r, in_w, null);
- } catch (IOException err) {
- // Client side of the pipes should report the problem.
- err.printStackTrace();
- } catch (RuntimeException err) {
- // Clients side will notice we went away, and report.
- err.printStackTrace();
- } finally {
- try {
- out_r.close();
- } catch (IOException e2) {
- // Ignore close failure, we probably crashed above.
- }
-
- try {
- in_w.close();
- } catch (IOException e2) {
- // Ignore close failure, we probably crashed above.
- }
-
- dst.close();
- }
- }
- };
- worker.start();
-
- init(in_r, out_w);
- readAdvertisedRefs();
- }
-
- @Override
- public void close() {
- super.close();
-
- if (worker != null) {
- try {
- worker.join();
- } catch (InterruptedException ie) {
- // Stop waiting and return anyway.
- } finally {
- worker = null;
- }
- }
- }
- }
-
class ForkLocalFetchConnection extends BasePackFetchConnection {
private Process uploadPack;
@@ -354,83 +290,6 @@ class TransportLocal extends Transport implements PackTransport {
}
}
- class InternalLocalPushConnection extends BasePackPushConnection {
- private Thread worker;
-
- InternalLocalPushConnection() throws TransportException {
- super(TransportLocal.this);
-
- final Repository dst;
- try {
- dst = new RepositoryBuilder().setGitDir(remoteGitDir).build();
- } catch (IOException err) {
- throw new TransportException(uri, JGitText.get().notAGitDirectory);
- }
-
- final PipedInputStream in_r;
- final PipedOutputStream in_w;
-
- final PipedInputStream out_r;
- final PipedOutputStream out_w;
- try {
- in_r = new PipedInputStream();
- in_w = new PipedOutputStream(in_r);
-
- out_r = new PipedInputStream();
- out_w = new PipedOutputStream(out_r);
- } catch (IOException err) {
- dst.close();
- throw new TransportException(uri, JGitText.get().cannotConnectPipes, err);
- }
-
- worker = new Thread("JGit-Receive-Pack") { //$NON-NLS-1$
- public void run() {
- try {
- final ReceivePack rp = createReceivePack(dst);
- rp.receive(out_r, in_w, System.err);
- } catch (IOException err) {
- // Client side of the pipes should report the problem.
- } catch (RuntimeException err) {
- // Clients side will notice we went away, and report.
- } finally {
- try {
- out_r.close();
- } catch (IOException e2) {
- // Ignore close failure, we probably crashed above.
- }
-
- try {
- in_w.close();
- } catch (IOException e2) {
- // Ignore close failure, we probably crashed above.
- }
-
- dst.close();
- }
- }
- };
- worker.start();
-
- init(in_r, out_w);
- readAdvertisedRefs();
- }
-
- @Override
- public void close() {
- super.close();
-
- if (worker != null) {
- try {
- worker.join();
- } catch (InterruptedException ie) {
- // Stop waiting and return anyway.
- } finally {
- worker = null;
- }
- }
- }
- }
-
class ForkLocalPushConnection extends BasePackPushConnection {
private Process receivePack;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java
index 756a161..fa073ae 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportSftp.java
@@ -47,6 +47,7 @@ import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
+import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -179,8 +180,9 @@ public class TransportSftp extends SshTransport implements WalkTransport {
close();
throw err;
} catch (SftpException je) {
- throw new TransportException("Can't enter " + path + "/objects"
- + ": " + je.getMessage(), je); //$NON-NLS-1$
+ throw new TransportException(MessageFormat.format(
+ JGitText.get().cannotEnterObjectsPath, path,
+ je.getMessage()), je);
}
}
@@ -195,8 +197,9 @@ public class TransportSftp extends SshTransport implements WalkTransport {
close();
throw err;
} catch (SftpException je) {
- throw new TransportException("Can't enter " + p + " from "
- + parent.objectsPath + ": " + je.getMessage(), je); //$NON-NLS-1$
+ throw new TransportException(MessageFormat.format(
+ JGitText.get().cannotEnterPathFromParent, p,
+ parent.objectsPath, je.getMessage()), je);
}
}
@@ -224,6 +227,7 @@ public class TransportSftp extends SshTransport implements WalkTransport {
Collection<String> getPackNames() throws IOException {
final List<String> packs = new ArrayList<String>();
try {
+ @SuppressWarnings("unchecked")
final Collection<ChannelSftp.LsEntry> list = ftp.ls("pack"); //$NON-NLS-1$
final HashMap<String, ChannelSftp.LsEntry> files;
final HashMap<String, Integer> mtimes;
@@ -253,8 +257,10 @@ public class TransportSftp extends SshTransport implements WalkTransport {
}
});
} catch (SftpException je) {
- throw new TransportException("Can't ls " + objectsPath
- + "/pack: " + je.getMessage(), je);
+ throw new TransportException(
+ MessageFormat.format(JGitText.get().cannotListPackPath,
+ objectsPath, je.getMessage()),
+ je);
}
return packs;
}
@@ -267,8 +273,9 @@ public class TransportSftp extends SshTransport implements WalkTransport {
} catch (SftpException je) {
if (je.id == ChannelSftp.SSH_FX_NO_SUCH_FILE)
throw new FileNotFoundException(path);
- throw new TransportException("Can't get " + objectsPath + "/" //$NON-NLS-2$
- + path + ": " + je.getMessage(), je); //$NON-NLS-1$
+ throw new TransportException(MessageFormat.format(
+ JGitText.get().cannotGetObjectsPath, objectsPath, path,
+ je.getMessage()), je);
}
}
@@ -279,8 +286,9 @@ public class TransportSftp extends SshTransport implements WalkTransport {
} catch (SftpException je) {
if (je.id == ChannelSftp.SSH_FX_NO_SUCH_FILE)
return;
- throw new TransportException("Can't delete " + objectsPath
- + "/" + path + ": " + je.getMessage(), je); //$NON-NLS-1$//$NON-NLS-2$
+ throw new TransportException(MessageFormat.format(
+ JGitText.get().cannotDeleteObjectsPath, objectsPath,
+ path, je.getMessage()), je);
}
// Prune any now empty directories.
@@ -318,8 +326,9 @@ public class TransportSftp extends SshTransport implements WalkTransport {
}
}
- throw new TransportException("Can't write " + objectsPath + "/" //$NON-NLS-2$
- + path + ": " + je.getMessage(), je); //$NON-NLS-1$
+ throw new TransportException(MessageFormat.format(
+ JGitText.get().cannotWriteObjectsPath, objectsPath,
+ path, je.getMessage()), je);
}
}
@@ -331,8 +340,9 @@ public class TransportSftp extends SshTransport implements WalkTransport {
try {
ftp.rename(lock, path);
} catch (SftpException je) {
- throw new TransportException("Can't write " + objectsPath
- + "/" + path + ": " + je.getMessage(), je); //$NON-NLS-1$//$NON-NLS-2$
+ throw new TransportException(MessageFormat.format(
+ JGitText.get().cannotWriteObjectsPath, objectsPath,
+ path, je.getMessage()), je);
}
} catch (IOException err) {
try {
@@ -364,8 +374,9 @@ public class TransportSftp extends SshTransport implements WalkTransport {
}
}
- throw new TransportException("Can't mkdir " + objectsPath + "/"
- + path + ": " + je.getMessage(), je);
+ throw new TransportException(MessageFormat.format(
+ JGitText.get().cannotMkdirObjectPath, objectsPath, path,
+ je.getMessage()), je);
}
}
@@ -385,8 +396,9 @@ public class TransportSftp extends SshTransport implements WalkTransport {
try {
list = ftp.ls(dir);
} catch (SftpException je) {
- throw new TransportException("Can't ls " + objectsPath + "/" //$NON-NLS-2$
- + dir + ": " + je.getMessage(), je); //$NON-NLS-1$
+ throw new TransportException(MessageFormat.format(
+ JGitText.get().cannotListObjectsPath, objectsPath, dir,
+ je.getMessage()), je);
}
for (final ChannelSftp.LsEntry ent : list) {
@@ -415,12 +427,14 @@ public class TransportSftp extends SshTransport implements WalkTransport {
} catch (FileNotFoundException noRef) {
return null;
} catch (IOException err) {
- throw new TransportException("Cannot read " + objectsPath + "/" //$NON-NLS-2$
- + path + ": " + err.getMessage(), err); //$NON-NLS-1$
+ throw new TransportException(MessageFormat.format(
+ JGitText.get().cannotReadObjectsPath, objectsPath, path,
+ err.getMessage()), err);
}
if (line == null)
- throw new TransportException("Empty ref: " + name);
+ throw new TransportException(
+ MessageFormat.format(JGitText.get().emptyRef, name));
if (line.startsWith("ref: ")) { //$NON-NLS-1$
final String target = line.substring("ref: ".length()); //$NON-NLS-1$
@@ -441,7 +455,8 @@ public class TransportSftp extends SshTransport implements WalkTransport {
return r;
}
- throw new TransportException("Bad ref: " + name + ": " + line); //$NON-NLS-2$
+ throw new TransportException(
+ MessageFormat.format(JGitText.get().badRef, name, line));
}
private Storage loose(final Ref r) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
index 91e212b..3ee2feb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
@@ -4,6 +4,7 @@
* Copyright (C) 2008, Shawn O. Pearce <spearce at spearce.org>
* Copyright (C) 2010, Christian Halstrick <christian.halstrick at sap.com>
* Copyright (C) 2013, Robin Stocker <robin at nibor.org>
+ * Copyright (C) 2015, Patrick Steinhardt <ps at pks.im>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -82,7 +83,7 @@ public class URIish implements Serializable {
* capturing groups: the first containing the user and the second containing
* the password
*/
- private static final String OPT_USER_PWD_P = "(?:([^/:@]+)(?::([^\\\\/]+))?@)?"; //$NON-NLS-1$
+ private static final String OPT_USER_PWD_P = "(?:([^/:]+)(?::([^\\\\/]+))?@)?"; //$NON-NLS-1$
/**
* Part of a pattern which matches the host part of URIs. Defines one
@@ -134,11 +135,15 @@ public class URIish implements Serializable {
+ OPT_USER_PWD_P //
+ HOST_P //
+ OPT_PORT_P //
- + "(" // open a catpuring group the the user-home-dir part //$NON-NLS-1$
- + (USER_HOME_P + "?") // //$NON-NLS-1$
- + "[\\\\/])" // //$NON-NLS-1$
+ + "(" // open a group capturing the user-home-dir-part //$NON-NLS-1$
+ + (USER_HOME_P + "?") //$NON-NLS-1$
+ + "(?:" // start non capturing group for host //$NON-NLS-1$
+ // separator or end of line
+ + "[\\\\/])|$" //$NON-NLS-1$
+ + ")" // close non capturing group for the host//$NON-NLS-1$
+ // separator or end of line
+ ")?" // close the optional group containing hostname //$NON-NLS-1$
- + "(.+)?" // //$NON-NLS-1$
+ + "(.+)?" //$NON-NLS-1$
+ "$"); //$NON-NLS-1$
/**
@@ -592,6 +597,8 @@ public class URIish implements Serializable {
private static boolean eq(final String a, final String b) {
if (a == b)
return true;
+ if (StringUtils.isEmptyOrNull(a) && StringUtils.isEmptyOrNull(b))
+ return true;
if (a == null || b == null)
return false;
return a.equals(b);
@@ -637,7 +644,7 @@ public class URIish implements Serializable {
if (getPath() != null) {
if (getScheme() != null) {
- if (!getPath().startsWith("/")) //$NON-NLS-1$
+ if (!getPath().startsWith("/") && !getPath().isEmpty()) //$NON-NLS-1$
r.append('/');
} else if (getHost() != null)
r.append(':');
@@ -690,6 +697,10 @@ public class URIish implements Serializable {
* <td><code>/path/to/repo/</code></td>
* </tr>
* <tr>
+ * <td><code>localhost</code></td>
+ * <td><code>ssh://localhost/</code></td>
+ * </tr>
+ * <tr>
* <td><code>/path//to</code></td>
* <td>an empty string</td>
* </tr>
@@ -703,9 +714,12 @@ public class URIish implements Serializable {
* @see #getPath
*/
public String getHumanishName() throws IllegalArgumentException {
- if ("".equals(getPath()) || getPath() == null) //$NON-NLS-1$
- throw new IllegalArgumentException();
String s = getPath();
+ if ("/".equals(s) || "".equals(s)) //$NON-NLS-1$ //$NON-NLS-2$
+ s = getHost();
+ if (s == null) // $NON-NLS-1$
+ throw new IllegalArgumentException();
+
String[] elements;
if ("file".equals(scheme) || LOCAL_FILE.matcher(s).matches()) //$NON-NLS-1$
elements = s.split("[\\" + File.separatorChar + "/]"); //$NON-NLS-1$ //$NON-NLS-2$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
index 1a653bd..101057f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -44,7 +44,9 @@
package org.eclipse.jgit.transport;
import static org.eclipse.jgit.lib.RefDatabase.ALL;
+import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_TIP_SHA1_IN_WANT;
+import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_REACHABLE_SHA1_IN_WANT;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_INCLUDE_TAG;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_MULTI_ACK;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_MULTI_ACK_DETAILED;
@@ -92,6 +94,7 @@ import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter;
import org.eclipse.jgit.storage.pack.PackConfig;
+import org.eclipse.jgit.storage.pack.PackStatistics;
import org.eclipse.jgit.transport.GitProtocolConstants.MultiAck;
import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
import org.eclipse.jgit.util.io.InterruptTimer;
@@ -251,8 +254,12 @@ public class UploadPack {
/** Hook handling the various upload phases. */
private PreUploadHook preUploadHook = PreUploadHook.NULL;
+ /** Hook for taking post upload actions. */
+ private PostUploadHook postUploadHook = PostUploadHook.NULL;
+
/** Capabilities requested by the client. */
private Set<String> options;
+ String userAgent;
/** Raw ObjectIds the client has asked for, before validating them. */
private final Set<ObjectId> wantIds = new HashSet<ObjectId>();
@@ -303,7 +310,7 @@ public class UploadPack {
private boolean noDone;
- private PackWriter.Statistics statistics;
+ private PackStatistics statistics;
private UploadPackLogger logger = UploadPackLogger.NULL;
@@ -516,7 +523,7 @@ public class UploadPack {
this.refFilter = refFilter != null ? refFilter : RefFilter.DEFAULT;
}
- /** @return the configured upload hook. */
+ /** @return the configured pre upload hook. */
public PreUploadHook getPreUploadHook() {
return preUploadHook;
}
@@ -532,6 +539,25 @@ public class UploadPack {
}
/**
+ * @return the configured post upload hook.
+ * @since 4.1
+ */
+ public PostUploadHook getPostUploadHook() {
+ return postUploadHook;
+ }
+
+ /**
+ * Set the hook for post upload actions (logging, repacking).
+ *
+ * @param hook
+ * the hook; if null no special actions are taken.
+ * @since 4.1
+ */
+ public void setPostUploadHook(PostUploadHook hook) {
+ postUploadHook = hook != null ? hook : PostUploadHook.NULL;
+ }
+
+ /**
* Set the configuration used by the pack generator.
*
* @param pc
@@ -550,11 +576,21 @@ public class UploadPack {
*/
public void setTransferConfig(TransferConfig tc) {
this.transferConfig = tc != null ? tc : new TransferConfig(db);
- setRequestPolicy(transferConfig.isAllowTipSha1InWant()
- ? RequestPolicy.TIP : RequestPolicy.ADVERTISED);
+ if (transferConfig.isAllowTipSha1InWant()) {
+ setRequestPolicy(transferConfig.isAllowReachableSha1InWant()
+ ? RequestPolicy.REACHABLE_COMMIT_TIP : RequestPolicy.TIP);
+ } else {
+ setRequestPolicy(transferConfig.isAllowReachableSha1InWant()
+ ? RequestPolicy.REACHABLE_COMMIT : RequestPolicy.ADVERTISED);
+ }
}
- /** @return the configured logger. */
+ /**
+ * @return the configured logger.
+ *
+ * @deprecated Use {@link #getPreUploadHook()}.
+ */
+ @Deprecated
public UploadPackLogger getLogger() {
return logger;
}
@@ -564,7 +600,9 @@ public class UploadPack {
*
* @param logger
* the logger instance. If null, no logging occurs.
+ * @deprecated Use {@link #setPreUploadHook(PreUploadHook)}.
*/
+ @Deprecated
public void setLogger(UploadPackLogger logger) {
this.logger = logger;
}
@@ -628,7 +666,7 @@ public class UploadPack {
service();
} finally {
msgOut = NullOutputStream.INSTANCE;
- walk.release();
+ walk.close();
if (timer != null) {
try {
timer.terminate();
@@ -646,8 +684,23 @@ public class UploadPack {
* was sent, such as during the negotation phase of a smart HTTP
* connection, or if the client was already up-to-date.
* @since 3.0
+ * @deprecated Use {@link #getStatistics()}.
*/
+ @Deprecated
public PackWriter.Statistics getPackStatistics() {
+ return statistics == null ? null
+ : new PackWriter.Statistics(statistics);
+ }
+
+ /**
+ * Get the PackWriter's statistics if a pack was sent to the client.
+ *
+ * @return statistics about pack output, if a pack was sent. Null if no pack
+ * was sent, such as during the negotation phase of a smart HTTP
+ * connection, or if the client was already up-to-date.
+ * @since 4.1
+ */
+ public PackStatistics getStatistics() {
return statistics;
}
@@ -682,6 +735,8 @@ public class UploadPack {
else
multiAck = MultiAck.OFF;
+ if (!clientShallowCommits.isEmpty())
+ verifyClientShallow();
if (depth != 0)
processShallow();
if (!clientShallowCommits.isEmpty())
@@ -735,38 +790,67 @@ public class UploadPack {
}
private void processShallow() throws IOException {
- DepthWalk.RevWalk depthWalk =
- new DepthWalk.RevWalk(walk.getObjectReader(), depth);
+ try (DepthWalk.RevWalk depthWalk = new DepthWalk.RevWalk(
+ walk.getObjectReader(), depth)) {
- // Find all the commits which will be shallow
- for (ObjectId o : wantIds) {
- try {
- depthWalk.markRoot(depthWalk.parseCommit(o));
- } catch (IncorrectObjectTypeException notCommit) {
- // Ignore non-commits in this loop.
+ // Find all the commits which will be shallow
+ for (ObjectId o : wantIds) {
+ try {
+ depthWalk.markRoot(depthWalk.parseCommit(o));
+ } catch (IncorrectObjectTypeException notCommit) {
+ // Ignore non-commits in this loop.
+ }
}
- }
- RevCommit o;
- while ((o = depthWalk.next()) != null) {
- DepthWalk.Commit c = (DepthWalk.Commit) o;
+ RevCommit o;
+ while ((o = depthWalk.next()) != null) {
+ DepthWalk.Commit c = (DepthWalk.Commit) o;
- // Commits at the boundary which aren't already shallow in
- // the client need to be marked as such
- if (c.getDepth() == depth && !clientShallowCommits.contains(c))
- pckOut.writeString("shallow " + o.name()); //$NON-NLS-1$
+ // Commits at the boundary which aren't already shallow in
+ // the client need to be marked as such
+ if (c.getDepth() == depth && !clientShallowCommits.contains(c))
+ pckOut.writeString("shallow " + o.name()); //$NON-NLS-1$
- // Commits not on the boundary which are shallow in the client
- // need to become unshallowed
- if (c.getDepth() < depth && clientShallowCommits.remove(c)) {
- unshallowCommits.add(c.copy());
- pckOut.writeString("unshallow " + c.name()); //$NON-NLS-1$
+ // Commits not on the boundary which are shallow in the client
+ // need to become unshallowed
+ if (c.getDepth() < depth && clientShallowCommits.remove(c)) {
+ unshallowCommits.add(c.copy());
+ pckOut.writeString("unshallow " + c.name()); //$NON-NLS-1$
+ }
}
}
-
pckOut.end();
}
+ private void verifyClientShallow()
+ throws IOException, PackProtocolException {
+ AsyncRevObjectQueue q = walk.parseAny(clientShallowCommits, true);
+ try {
+ for (;;) {
+ try {
+ // Shallow objects named by the client must be commits.
+ RevObject o = q.next();
+ if (o == null) {
+ break;
+ }
+ if (!(o instanceof RevCommit)) {
+ throw new PackProtocolException(
+ MessageFormat.format(
+ JGitText.get().invalidShallowObject,
+ o.name()));
+ }
+ } catch (MissingObjectException notCommit) {
+ // shallow objects not known at the server are ignored
+ // by git-core upload-pack, match that behavior.
+ clientShallowCommits.remove(notCommit.getObjectId());
+ continue;
+ }
+ }
+ } finally {
+ q.release();
+ }
+ }
+
/**
* Generate an advertisement of available refs and capabilities.
*
@@ -806,10 +890,15 @@ public class UploadPack {
|| policy == RequestPolicy.REACHABLE_COMMIT_TIP
|| policy == null)
adv.advertiseCapability(OPTION_ALLOW_TIP_SHA1_IN_WANT);
+ if (policy == RequestPolicy.REACHABLE_COMMIT
+ || policy == RequestPolicy.REACHABLE_COMMIT_TIP
+ || policy == null)
+ adv.advertiseCapability(OPTION_ALLOW_REACHABLE_SHA1_IN_WANT);
+ adv.advertiseCapability(OPTION_AGENT, UserAgent.get());
adv.setDerefTags(true);
- Map<String, Ref> refs = getAdvertisedOrDefaultRefs();
- findSymrefs(adv, refs);
- advertised = adv.send(refs);
+ Map<String, Ref> advertisedOrDefaultRefs = getAdvertisedOrDefaultRefs();
+ findSymrefs(adv, advertisedOrDefaultRefs);
+ advertised = adv.send(advertisedOrDefaultRefs);
if (adv.isEmpty())
adv.advertiseId(ObjectId.zeroId(), "capabilities^{}"); //$NON-NLS-1$
adv.end();
@@ -884,6 +973,37 @@ public class UploadPack {
}
}
+ /**
+ * Returns the clone/fetch depth. Valid only after calling recvWants().
+ *
+ * @return the depth requested by the client, or 0 if unbounded.
+ * @since 4.0
+ */
+ public int getDepth() {
+ if (options == null)
+ throw new RequestNotYetReadException();
+ return depth;
+ }
+
+ /**
+ * Get the user agent of the client.
+ * <p>
+ * If the client is new enough to use {@code agent=} capability that value
+ * will be returned. Older HTTP clients may also supply their version using
+ * the HTTP {@code User-Agent} header. The capability overrides the HTTP
+ * header if both are available.
+ * <p>
+ * When an HTTP request has been received this method returns the HTTP
+ * {@code User-Agent} header value until capabilities have been parsed.
+ *
+ * @return user agent supplied by the client. Available only if the client
+ * is new enough to advertise its user agent.
+ * @since 4.0
+ */
+ public String getPeerUserAgent() {
+ return UserAgent.getAgent(options, userAgent);
+ }
+
private boolean negotiate() throws IOException {
okToGiveUp = Boolean.FALSE;
@@ -1355,6 +1475,7 @@ public class UploadPack {
pw.setIndexDisabled(true);
pw.setUseCachedPacks(true);
pw.setUseBitmaps(depth == 0 && clientShallowCommits.isEmpty());
+ pw.setClientShallowCommits(clientShallowCommits);
pw.setReuseDeltaCommits(true);
pw.setDeltaBaseAsOffset(options.contains(OPTION_OFS_DELTA));
pw.setThin(options.contains(OPTION_THIN_PACK));
@@ -1424,16 +1545,18 @@ public class UploadPack {
} finally {
statistics = pw.getStatistics();
- if (statistics != null)
- logger.onPackStatistics(statistics);
- pw.release();
+ if (statistics != null) {
+ postUploadHook.onPostUpload(statistics);
+ logger.onPackStatistics(new PackWriter.Statistics(statistics));
+ }
+ pw.close();
}
if (sideband)
pckOut.end();
}
- private void findSymrefs(
+ private static void findSymrefs(
final RefAdvertiser adv, final Map<String, Ref> refs) {
Ref head = refs.get(Constants.HEAD);
if (head != null && head.isSymbolic()) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java
index 99fa6e0..85ebecc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java
@@ -52,7 +52,10 @@ import org.eclipse.jgit.internal.storage.pack.PackWriter;
* thread to a particular connection, if they need to also include connection
* information. One method is to use a {@link java.lang.ThreadLocal} to remember
* the connection information before invoking UploadPack.
+ *
+ * @deprecated use {@link PostUploadHook} instead
*/
+ at Deprecated
public interface UploadPackLogger {
/** A simple no-op logger. */
public static final UploadPackLogger NULL = new UploadPackLogger() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java
index 3f14cc6..4ea0319 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java
@@ -48,10 +48,13 @@ import java.util.List;
import org.eclipse.jgit.internal.storage.pack.PackWriter;
/**
- * {@link UploadPackLogger} that delegates to a list of other loggers.
+ * UploadPackLogger that delegates to a list of other loggers.
* <p>
* loggers are run in the order passed to the constructor.
+ *
+ * @deprecated Use {@link PostUploadHookChain} instead.
*/
+ at Deprecated
public class UploadPackLoggerChain implements UploadPackLogger {
private final UploadPackLogger[] loggers;
private final int count;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UserAgent.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UserAgent.java
new file mode 100644
index 0000000..eadb92d
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UserAgent.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
+
+import java.util.Set;
+
+import org.eclipse.jgit.util.StringUtils;
+
+/**
+ * User agent to be reported by this JGit client and server on the network.
+ * <p>
+ * On HTTP transports this user agent string is always supplied by the JGit
+ * client in the {@code User-Agent} HTTP header.
+ * <p>
+ * On native transports this user agent string is always sent when JGit is a
+ * server. When JGit is a client the user agent string will be supplied to the
+ * remote server only if the remote server advertises its own agent identity.
+ *
+ * @since 4.0
+ */
+public class UserAgent {
+ private static volatile String userAgent = computeUserAgent();
+
+ private static String computeUserAgent() {
+ return clean("JGit/" + computeVersion()); //$NON-NLS-1$
+ }
+
+ private static String computeVersion() {
+ Package pkg = UserAgent.class.getPackage();
+ if (pkg != null) {
+ String ver = pkg.getImplementationVersion();
+ if (!StringUtils.isEmptyOrNull(ver)) {
+ return ver;
+ }
+ }
+ return "unknown"; //$NON-NLS-1$
+ }
+
+ private static String clean(String s) {
+ s = s.trim();
+ StringBuilder b = new StringBuilder(s.length());
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (c <= 32 || c >= 127) {
+ if (b.length() > 0 && b.charAt(b.length() - 1) == '.')
+ continue;
+ c = '.';
+ }
+ b.append(c);
+ }
+ return b.length() > 0 ? b.toString() : null;
+ }
+
+ /**
+ * Get the user agent string advertised by JGit.
+ *
+ * @return a string similar to {@code "JGit/4.0"}; null if the agent has
+ * been cleared and should not be shared with a peer.
+ */
+ public static String get() {
+ return userAgent;
+ }
+
+ /**
+ * Change the user agent string advertised by JGit.
+ * <p>
+ * The new string should start with {@code "JGit/"} (for example
+ * {@code "JGit/4.0"}) to advertise the implementation as JGit based.
+ * <p>
+ * Spaces and other whitespace should be avoided as these will be
+ * automatically converted to {@code "."}.
+ * <p>
+ * User agent strings are restricted to printable ASCII.
+ *
+ * @param agent
+ * new user agent string for this running JGit library. Setting
+ * to null or empty string will avoid sending any identification
+ * to the peer.
+ */
+ public static void set(String agent) {
+ userAgent = StringUtils.isEmptyOrNull(agent) ? null : clean(agent);
+ }
+
+ static String getAgent(Set<String> options, String transportAgent) {
+ if (options == null || options.isEmpty()) {
+ return transportAgent;
+ }
+ for (String o : options) {
+ if (o.startsWith(OPTION_AGENT)
+ && o.length() > OPTION_AGENT.length()
+ && o.charAt(OPTION_AGENT.length()) == '=') {
+ return o.substring(OPTION_AGENT.length() + 1);
+ }
+ }
+ return transportAgent;
+ }
+
+ static boolean hasAgent(Set<String> options) {
+ return getAgent(options, null) != null;
+ }
+
+ private UserAgent() {
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkEncryption.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkEncryption.java
index e55b984..fe03bdc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkEncryption.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkEncryption.java
@@ -47,22 +47,28 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.spec.InvalidKeySpecException;
+import java.security.AlgorithmParameters;
+import java.security.GeneralSecurityException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.KeySpec;
import java.text.MessageFormat;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
-import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import javax.xml.bind.DatatypeConverter;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.util.Base64;
abstract class WalkEncryption {
static final WalkEncryption NONE = new NoEncryption();
@@ -71,29 +77,40 @@ abstract class WalkEncryption {
static final String JETS3T_CRYPTO_ALG = "jets3t-crypto-alg"; //$NON-NLS-1$
- abstract OutputStream encrypt(OutputStream os) throws IOException;
+ // Note: encrypt -> request state machine, step 1.
+ abstract OutputStream encrypt(OutputStream output) throws IOException;
- abstract InputStream decrypt(InputStream in) throws IOException;
+ // Note: encrypt -> request state machine, step 2.
+ abstract void request(HttpURLConnection conn, String prefix) throws IOException;
- abstract void request(HttpURLConnection u, String prefix);
+ // Note: validate -> decrypt state machine, step 1.
+ abstract void validate(HttpURLConnection conn, String prefix) throws IOException;
- abstract void validate(HttpURLConnection u, String p) throws IOException;
+ // Note: validate -> decrypt state machine, step 2.
+ abstract InputStream decrypt(InputStream input) throws IOException;
- protected void validateImpl(final HttpURLConnection u, final String p,
+
+ // TODO mixed ciphers
+ // consider permitting mixed ciphers to facilitate algorithm migration
+ // i.e. user keeps the password, but changes the algorithm
+ // then existing remote entries will still be readable
+ protected void validateImpl(final HttpURLConnection u, final String prefix,
final String version, final String name) throws IOException {
String v;
- v = u.getHeaderField(p + JETS3T_CRYPTO_VER);
+ v = u.getHeaderField(prefix + JETS3T_CRYPTO_VER);
if (v == null)
v = ""; //$NON-NLS-1$
if (!version.equals(v))
throw new IOException(MessageFormat.format(JGitText.get().unsupportedEncryptionVersion, v));
- v = u.getHeaderField(p + JETS3T_CRYPTO_ALG);
+ v = u.getHeaderField(prefix + JETS3T_CRYPTO_ALG);
if (v == null)
v = ""; //$NON-NLS-1$
- if (!name.equals(v))
- throw new IOException(JGitText.get().unsupportedEncryptionAlgorithm + v);
+ // Standard names are not case-sensitive.
+ // http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html
+ if (!name.equalsIgnoreCase(v))
+ throw new IOException(MessageFormat.format(JGitText.get().unsupportedEncryptionAlgorithm, v));
}
IOException error(final Throwable why) {
@@ -110,9 +127,9 @@ abstract class WalkEncryption {
}
@Override
- void validate(final HttpURLConnection u, final String p)
+ void validate(final HttpURLConnection u, final String prefix)
throws IOException {
- validateImpl(u, p, "", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ validateImpl(u, prefix, "", ""); //$NON-NLS-1$ //$NON-NLS-2$
}
@Override
@@ -126,53 +143,139 @@ abstract class WalkEncryption {
}
}
- static class ObjectEncryptionV2 extends WalkEncryption {
- private static int ITERATION_COUNT = 5000;
+ // PBEParameterSpec factory for Java (version <= 7).
+ // Does not support AlgorithmParameterSpec.
+ static PBEParameterSpec java7PBEParameterSpec(byte[] salt,
+ int iterationCount) {
+ return new PBEParameterSpec(salt, iterationCount);
+ }
+
+ // PBEParameterSpec factory for Java (version >= 8).
+ // Adds support for AlgorithmParameterSpec.
+ static PBEParameterSpec java8PBEParameterSpec(byte[] salt,
+ int iterationCount, AlgorithmParameterSpec paramSpec) {
+ try {
+ @SuppressWarnings("boxing")
+ PBEParameterSpec instance = PBEParameterSpec.class
+ .getConstructor(byte[].class, int.class,
+ AlgorithmParameterSpec.class)
+ .newInstance(salt, iterationCount, paramSpec);
+ return instance;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ // Current runtime version.
+ // https://docs.oracle.com/javase/7/docs/technotes/guides/versioning/spec/versioning2.html
+ static double javaVersion() {
+ return Double.parseDouble(System.getProperty("java.specification.version")); //$NON-NLS-1$
+ }
+
+ /**
+ * JetS3t compatibility reference: <a href=
+ * "https://bitbucket.org/jmurty/jets3t/src/156c00eb160598c2e9937fd6873f00d3190e28ca/src/org/jets3t/service/security/EncryptionUtil.java">
+ * EncryptionUtil.java</a>
+ * <p>
+ * Note: EncryptionUtil is inadequate:
+ * <li>EncryptionUtil.isCipherAvailableForUse checks encryption only which
+ * "always works", but in JetS3t both encryption and decryption use non-IV
+ * aware algorithm parameters for all PBE specs, which breaks in case of AES
+ * <li>that means that only non-IV algorithms will work round trip in
+ * JetS3t, such as PBEWithMD5AndDES and PBEWithSHAAndTwofish-CBC
+ * <li>any AES based algorithms such as "PBE...With...And...AES" will not
+ * work, since they need proper IV setup
+ */
+ static class JetS3tV2 extends WalkEncryption {
+
+ static final String VERSION = "2"; //$NON-NLS-1$
+
+ static final String ALGORITHM = "PBEWithMD5AndDES"; //$NON-NLS-1$
+
+ static final int ITERATIONS = 5000;
+
+ static final int KEY_SIZE = 32;
+
+ static final byte[] SALT = { //
+ (byte) 0xA4, (byte) 0x0B, (byte) 0xC8, (byte) 0x34, //
+ (byte) 0xD6, (byte) 0x95, (byte) 0xF3, (byte) 0x13 //
+ };
+
+ // Size 16, see com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE
+ static final byte[] ZERO_AES_IV = new byte[16];
+
+ private static final String cryptoVer = VERSION;
- private static byte[] salt = { (byte) 0xA4, (byte) 0x0B, (byte) 0xC8,
- (byte) 0x34, (byte) 0xD6, (byte) 0x95, (byte) 0xF3, (byte) 0x13 };
+ private final String cryptoAlg;
- private final String algorithmName;
+ private final SecretKey secretKey;
- private final SecretKey skey;
+ private final AlgorithmParameterSpec paramSpec;
- private final PBEParameterSpec aspec;
+ JetS3tV2(final String algo, final String key)
+ throws GeneralSecurityException {
+ cryptoAlg = algo;
- ObjectEncryptionV2(final String algo, final String key)
- throws InvalidKeySpecException, NoSuchAlgorithmException {
- algorithmName = algo;
+ // Verify if cipher is present.
+ Cipher cipher = Cipher.getInstance(cryptoAlg);
+
+ // Standard names are not case-sensitive.
+ // http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html
+ String cryptoName = cryptoAlg.toUpperCase();
+
+ if (!cryptoName.startsWith("PBE")) //$NON-NLS-1$
+ throw new GeneralSecurityException(JGitText.get().encryptionOnlyPBE);
+
+ PBEKeySpec keySpec = new PBEKeySpec(key.toCharArray(), SALT, ITERATIONS, KEY_SIZE);
+ secretKey = SecretKeyFactory.getInstance(algo).generateSecret(keySpec);
+
+ // Detect algorithms which require initialization vector.
+ boolean useIV = cryptoName.contains("AES"); //$NON-NLS-1$
+
+ // PBEParameterSpec algorithm parameters are supported from Java 8.
+ boolean isJava8 = javaVersion() >= 1.8;
+
+ if (useIV && isJava8) {
+ // Support IV where possible:
+ // * since JCE provider uses random IV for PBE/AES
+ // * and there is no place to store dynamic IV in JetS3t V2
+ // * we use static IV, and tolerate increased security risk
+ // TODO back port this change to JetS3t V2
+ // See:
+ // https://bitbucket.org/jmurty/jets3t/raw/156c00eb160598c2e9937fd6873f00d3190e28ca/src/org/jets3t/service/security/EncryptionUtil.java
+ // http://cr.openjdk.java.net/~mullan/webrevs/ascarpin/webrev.00/raw_files/new/src/share/classes/com/sun/crypto/provider/PBES2Core.java
+ IvParameterSpec paramIV = new IvParameterSpec(ZERO_AES_IV);
+ paramSpec = java8PBEParameterSpec(SALT, ITERATIONS, paramIV);
+ } else {
+ // Strict legacy JetS3t V2 compatibility, with no IV support.
+ paramSpec = java7PBEParameterSpec(SALT, ITERATIONS);
+ }
+
+ // Verify if cipher + key are allowed by policy.
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey, paramSpec);
+ cipher.doFinal();
- final PBEKeySpec s;
- s = new PBEKeySpec(key.toCharArray(), salt, ITERATION_COUNT, 32);
- skey = SecretKeyFactory.getInstance(algo).generateSecret(s);
- aspec = new PBEParameterSpec(salt, ITERATION_COUNT);
}
@Override
void request(final HttpURLConnection u, final String prefix) {
- u.setRequestProperty(prefix + JETS3T_CRYPTO_VER, "2"); //$NON-NLS-1$
- u.setRequestProperty(prefix + JETS3T_CRYPTO_ALG, algorithmName);
+ u.setRequestProperty(prefix + JETS3T_CRYPTO_VER, cryptoVer);
+ u.setRequestProperty(prefix + JETS3T_CRYPTO_ALG, cryptoAlg);
}
@Override
- void validate(final HttpURLConnection u, final String p)
+ void validate(final HttpURLConnection u, final String prefix)
throws IOException {
- validateImpl(u, p, "2", algorithmName); //$NON-NLS-1$
+ validateImpl(u, prefix, cryptoVer, cryptoAlg);
}
@Override
OutputStream encrypt(final OutputStream os) throws IOException {
try {
- final Cipher c = Cipher.getInstance(algorithmName);
- c.init(Cipher.ENCRYPT_MODE, skey, aspec);
- return new CipherOutputStream(os, c);
- } catch (NoSuchAlgorithmException e) {
- throw error(e);
- } catch (NoSuchPaddingException e) {
- throw error(e);
- } catch (InvalidKeyException e) {
- throw error(e);
- } catch (InvalidAlgorithmParameterException e) {
+ final Cipher cipher = Cipher.getInstance(cryptoAlg);
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey, paramSpec);
+ return new CipherOutputStream(os, cipher);
+ } catch (GeneralSecurityException e) {
throw error(e);
}
}
@@ -180,18 +283,311 @@ abstract class WalkEncryption {
@Override
InputStream decrypt(final InputStream in) throws IOException {
try {
- final Cipher c = Cipher.getInstance(algorithmName);
- c.init(Cipher.DECRYPT_MODE, skey, aspec);
- return new CipherInputStream(in, c);
- } catch (NoSuchAlgorithmException e) {
+ final Cipher cipher = Cipher.getInstance(cryptoAlg);
+ cipher.init(Cipher.DECRYPT_MODE, secretKey, paramSpec);
+ return new CipherInputStream(in, cipher);
+ } catch (GeneralSecurityException e) {
throw error(e);
- } catch (NoSuchPaddingException e) {
- throw error(e);
- } catch (InvalidKeyException e) {
+ }
+ }
+ }
+
+ /** Encryption property names. */
+ interface Keys {
+ // Remote S3 meta: V1 algorithm name or V2 profile name.
+ String JGIT_PROFILE = "jgit-crypto-profile"; //$NON-NLS-1$
+
+ // Remote S3 meta: JGit encryption implementation version.
+ String JGIT_VERSION = "jgit-crypto-version"; //$NON-NLS-1$
+
+ // Remote S3 meta: base-64 encoded cipher algorithm parameters.
+ String JGIT_CONTEXT = "jgit-crypto-context"; //$NON-NLS-1$
+
+ // Amazon S3 connection configuration file profile property suffixes:
+ String X_ALGO = ".algo"; //$NON-NLS-1$
+ String X_KEY_ALGO = ".key.algo"; //$NON-NLS-1$
+ String X_KEY_SIZE = ".key.size"; //$NON-NLS-1$
+ String X_KEY_ITER = ".key.iter"; //$NON-NLS-1$
+ String X_KEY_SALT = ".key.salt"; //$NON-NLS-1$
+ }
+
+ /** Encryption constants and defaults. */
+ interface Vals {
+ // Compatibility defaults.
+ String DEFAULT_VERS = "0"; //$NON-NLS-1$
+ String DEFAULT_ALGO = JetS3tV2.ALGORITHM;
+ String DEFAULT_KEY_ALGO = JetS3tV2.ALGORITHM;
+ String DEFAULT_KEY_SIZE = Integer.toString(JetS3tV2.KEY_SIZE);
+ String DEFAULT_KEY_ITER = Integer.toString(JetS3tV2.ITERATIONS);
+ String DEFAULT_KEY_SALT = DatatypeConverter.printHexBinary(JetS3tV2.SALT);
+
+ String EMPTY = ""; //$NON-NLS-1$
+
+ // Match white space.
+ String REGEX_WS = "\\s+"; //$NON-NLS-1$
+
+ // Match PBE ciphers, i.e: PBEWithMD5AndDES
+ String REGEX_PBE = "(PBE).*(WITH).+(AND).+"; //$NON-NLS-1$
+
+ // Match transformation ciphers, i.e: AES/CBC/PKCS5Padding
+ String REGEX_TRANS = "(.+)/(.+)/(.+)"; //$NON-NLS-1$
+ }
+
+ static GeneralSecurityException securityError(String message) {
+ return new GeneralSecurityException(
+ MessageFormat.format(JGitText.get().encryptionError, message));
+ }
+
+ /**
+ * Base implementation of JGit symmetric encryption. Supports V2 properties
+ * format.
+ */
+ static abstract class SymmetricEncryption extends WalkEncryption
+ implements Keys, Vals {
+
+ /** Encryption profile, root name of group of related properties. */
+ final String profile;
+
+ /** Encryption version, reflects actual implementation class. */
+ final String version;
+
+ /** Full cipher algorithm name. */
+ final String cipherAlgo;
+
+ /** Cipher algorithm name for parameters lookup. */
+ final String paramsAlgo;
+
+ /** Generated secret key. */
+ final SecretKey secretKey;
+
+ SymmetricEncryption(Properties props) throws GeneralSecurityException {
+
+ profile = props.getProperty(AmazonS3.Keys.CRYPTO_ALG);
+ version = props.getProperty(AmazonS3.Keys.CRYPTO_VER);
+ String pass = props.getProperty(AmazonS3.Keys.PASSWORD);
+
+ cipherAlgo = props.getProperty(profile + X_ALGO, DEFAULT_ALGO);
+
+ String keyAlgo = props.getProperty(profile + X_KEY_ALGO, DEFAULT_KEY_ALGO);
+ String keySize = props.getProperty(profile + X_KEY_SIZE, DEFAULT_KEY_SIZE);
+ String keyIter = props.getProperty(profile + X_KEY_ITER, DEFAULT_KEY_ITER);
+ String keySalt = props.getProperty(profile + X_KEY_SALT, DEFAULT_KEY_SALT);
+
+ // Verify if cipher is present.
+ Cipher cipher = Cipher.getInstance(cipherAlgo);
+
+ // Verify if key factory is present.
+ SecretKeyFactory factory = SecretKeyFactory.getInstance(keyAlgo);
+
+ final int size;
+ try {
+ size = Integer.parseInt(keySize);
+ } catch (Exception e) {
+ throw securityError(X_KEY_SIZE + EMPTY + keySize);
+ }
+
+ final int iter;
+ try {
+ iter = Integer.parseInt(keyIter);
+ } catch (Exception e) {
+ throw securityError(X_KEY_ITER + EMPTY + keyIter);
+ }
+
+ final byte[] salt;
+ try {
+ salt = DatatypeConverter
+ .parseHexBinary(keySalt.replaceAll(REGEX_WS, EMPTY));
+ } catch (Exception e) {
+ throw securityError(X_KEY_SALT + EMPTY + keySalt);
+ }
+
+ KeySpec keySpec = new PBEKeySpec(pass.toCharArray(), salt, iter, size);
+
+ SecretKey keyBase = factory.generateSecret(keySpec);
+
+ String name = cipherAlgo.toUpperCase();
+ Matcher matcherPBE = Pattern.compile(REGEX_PBE).matcher(name);
+ Matcher matcherTrans = Pattern.compile(REGEX_TRANS).matcher(name);
+ if (matcherPBE.matches()) {
+ paramsAlgo = cipherAlgo;
+ secretKey = keyBase;
+ } else if (matcherTrans.find()) {
+ paramsAlgo = matcherTrans.group(1);
+ secretKey = new SecretKeySpec(keyBase.getEncoded(), paramsAlgo);
+ } else {
+ throw new GeneralSecurityException(MessageFormat.format(
+ JGitText.get().unsupportedEncryptionAlgorithm,
+ cipherAlgo));
+ }
+
+ // Verify if cipher + key are allowed by policy.
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+ cipher.doFinal();
+
+ }
+
+ // Shared state encrypt -> request.
+ volatile String context;
+
+ @Override
+ OutputStream encrypt(OutputStream output) throws IOException {
+ try {
+ Cipher cipher = Cipher.getInstance(cipherAlgo);
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+ AlgorithmParameters params = cipher.getParameters();
+ if (params == null) {
+ context = EMPTY;
+ } else {
+ context = Base64.encodeBytes(params.getEncoded());
+ }
+ return new CipherOutputStream(output, cipher);
+ } catch (Exception e) {
throw error(e);
- } catch (InvalidAlgorithmParameterException e) {
+ }
+ }
+
+ @Override
+ void request(HttpURLConnection conn, String prefix) throws IOException {
+ conn.setRequestProperty(prefix + JGIT_PROFILE, profile);
+ conn.setRequestProperty(prefix + JGIT_VERSION, version);
+ conn.setRequestProperty(prefix + JGIT_CONTEXT, context);
+ // No cleanup:
+ // single encrypt can be followed by several request
+ // from the AmazonS3.putImpl() multiple retry attempts
+ // context = null; // Cleanup encrypt -> request transition.
+ // TODO re-factor AmazonS3.putImpl to be more transaction-like
+ }
+
+ // Shared state validate -> decrypt.
+ volatile Cipher decryptCipher;
+
+ @Override
+ void validate(HttpURLConnection conn, String prefix)
+ throws IOException {
+ String prof = conn.getHeaderField(prefix + JGIT_PROFILE);
+ String vers = conn.getHeaderField(prefix + JGIT_VERSION);
+ String cont = conn.getHeaderField(prefix + JGIT_CONTEXT);
+
+ if (prof == null) {
+ throw new IOException(MessageFormat
+ .format(JGitText.get().encryptionError, JGIT_PROFILE));
+ }
+ if (vers == null) {
+ throw new IOException(MessageFormat
+ .format(JGitText.get().encryptionError, JGIT_VERSION));
+ }
+ if (cont == null) {
+ throw new IOException(MessageFormat
+ .format(JGitText.get().encryptionError, JGIT_CONTEXT));
+ }
+ if (!profile.equals(prof)) {
+ throw new IOException(MessageFormat.format(
+ JGitText.get().unsupportedEncryptionAlgorithm, prof));
+ }
+ if (!version.equals(vers)) {
+ throw new IOException(MessageFormat.format(
+ JGitText.get().unsupportedEncryptionVersion, vers));
+ }
+ try {
+ decryptCipher = Cipher.getInstance(cipherAlgo);
+ if (cont.isEmpty()) {
+ decryptCipher.init(Cipher.DECRYPT_MODE, secretKey);
+ } else {
+ AlgorithmParameters params = AlgorithmParameters
+ .getInstance(paramsAlgo);
+ params.init(Base64.decode(cont));
+ decryptCipher.init(Cipher.DECRYPT_MODE, secretKey, params);
+ }
+ } catch (Exception e) {
throw error(e);
}
}
+
+ @Override
+ InputStream decrypt(InputStream input) throws IOException {
+ try {
+ return new CipherInputStream(input, decryptCipher);
+ } finally {
+ decryptCipher = null; // Cleanup validate -> decrypt transition.
+ }
+ }
+ }
+
+ /**
+ * Provides JetS3t-like encryption with AES support. Uses V1 connection file
+ * format. For reference, see: 'jgit-s3-connection-v-1.properties'.
+ */
+ static class JGitV1 extends SymmetricEncryption {
+
+ static final String VERSION = "1"; //$NON-NLS-1$
+
+ // Re-map connection properties V1 -> V2.
+ static Properties wrap(String algo, String pass) {
+ Properties props = new Properties();
+ props.put(AmazonS3.Keys.CRYPTO_ALG, algo);
+ props.put(AmazonS3.Keys.CRYPTO_VER, VERSION);
+ props.put(AmazonS3.Keys.PASSWORD, pass);
+ props.put(algo + Keys.X_ALGO, algo);
+ props.put(algo + Keys.X_KEY_ALGO, algo);
+ props.put(algo + Keys.X_KEY_ITER, DEFAULT_KEY_ITER);
+ props.put(algo + Keys.X_KEY_SIZE, DEFAULT_KEY_SIZE);
+ props.put(algo + Keys.X_KEY_SALT, DEFAULT_KEY_SALT);
+ return props;
+ }
+
+ JGitV1(String algo, String pass)
+ throws GeneralSecurityException {
+ super(wrap(algo, pass));
+ String name = cipherAlgo.toUpperCase();
+ Matcher matcherPBE = Pattern.compile(REGEX_PBE).matcher(name);
+ if (!matcherPBE.matches())
+ throw new GeneralSecurityException(
+ JGitText.get().encryptionOnlyPBE);
+ }
+
+ }
+
+ /**
+ * Supports both PBE and non-PBE algorithms. Uses V2 connection file format.
+ * For reference, see: 'jgit-s3-connection-v-2.properties'.
+ */
+ static class JGitV2 extends SymmetricEncryption {
+
+ static final String VERSION = "2"; //$NON-NLS-1$
+
+ JGitV2(Properties props)
+ throws GeneralSecurityException {
+ super(props);
+ }
+ }
+
+ /**
+ * Encryption factory.
+ *
+ * @param props
+ * @return instance
+ * @throws GeneralSecurityException
+ */
+ static WalkEncryption instance(Properties props)
+ throws GeneralSecurityException {
+
+ String algo = props.getProperty(AmazonS3.Keys.CRYPTO_ALG, Vals.DEFAULT_ALGO);
+ String vers = props.getProperty(AmazonS3.Keys.CRYPTO_VER, Vals.DEFAULT_VERS);
+ String pass = props.getProperty(AmazonS3.Keys.PASSWORD);
+
+ if (pass == null) // Disable encryption.
+ return WalkEncryption.NONE;
+
+ switch (vers) {
+ case Vals.DEFAULT_VERS:
+ return new JetS3tV2(algo, pass);
+ case JGitV1.VERSION:
+ return new JGitV1(algo, pass);
+ case JGitV2.VERSION:
+ return new JGitV2(props);
+ default:
+ throw new GeneralSecurityException(MessageFormat.format(
+ JGitText.get().unsupportedEncryptionVersion, vers));
+ }
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
index 6b7183b..17edfdc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkFetchConnection.java
@@ -115,10 +115,10 @@ import org.eclipse.jgit.util.FileUtils;
*/
class WalkFetchConnection extends BaseFetchConnection {
/** The repository this transport fetches into, or pushes out of. */
- private final Repository local;
+ final Repository local;
/** If not null the validator for received objects. */
- private final ObjectChecker objCheck;
+ final ObjectChecker objCheck;
/**
* List of all remote repositories we may need to get objects out of.
@@ -180,12 +180,12 @@ class WalkFetchConnection extends BaseFetchConnection {
*/
private final HashMap<ObjectId, List<Throwable>> fetchErrors;
- private String lockMessage;
+ String lockMessage;
- private final List<PackLock> packLocks;
+ final List<PackLock> packLocks;
/** Inserter to write objects onto {@link #local}. */
- private final ObjectInserter inserter;
+ final ObjectInserter inserter;
/** Inserter to read objects from {@link #local}. */
private final ObjectReader reader;
@@ -252,8 +252,8 @@ class WalkFetchConnection extends BaseFetchConnection {
@Override
public void close() {
- inserter.release();
- reader.release();
+ inserter.close();
+ reader.close();
for (final RemotePack p : unfetchedPacks) {
if (p.tmpIdx != null)
p.tmpIdx.delete();
@@ -267,6 +267,10 @@ class WalkFetchConnection extends BaseFetchConnection {
final HashSet<ObjectId> inWorkQueue = new HashSet<ObjectId>();
for (final Ref r : want) {
final ObjectId id = r.getObjectId();
+ if (id == null) {
+ throw new NullPointerException(MessageFormat.format(
+ JGitText.get().transportProvidedRefWithNoObjectId, r.getName()));
+ }
try {
final RevObject obj = revWalk.parseAny(id);
if (obj.has(COMPLETE))
@@ -430,7 +434,8 @@ class WalkFetchConnection extends BaseFetchConnection {
final WalkRemoteObjectDatabase wrr = noPacksYet.removeFirst();
final Collection<String> packNameList;
try {
- pm.beginTask("Listing packs", ProgressMonitor.UNKNOWN);
+ pm.beginTask(JGitText.get().listingPacks,
+ ProgressMonitor.UNKNOWN);
packNameList = wrr.getPackNames();
} catch (IOException e) {
// Try another repository.
@@ -632,10 +637,11 @@ class WalkFetchConnection extends BaseFetchConnection {
final byte[] raw = uol.getCachedBytes();
if (objCheck != null) {
try {
- objCheck.check(type, raw);
+ objCheck.check(id, type, raw);
} catch (CorruptObjectException e) {
- throw new TransportException(MessageFormat.format(JGitText.get().transportExceptionInvalid
- , Constants.typeString(type), id.name(), e.getMessage()));
+ throw new TransportException(MessageFormat.format(
+ JGitText.get().transportExceptionInvalid,
+ Constants.typeString(type), id.name(), e.getMessage()));
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java
index 02960bf..4eaf3f9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WalkPushConnection.java
@@ -103,7 +103,7 @@ class WalkPushConnection extends BaseConnection implements PushConnection {
private final URIish uri;
/** Database connection to the remote repository. */
- private final WalkRemoteObjectDatabase dest;
+ final WalkRemoteObjectDatabase dest;
/** The configured transport we were constructed by. */
private final Transport transport;
@@ -220,9 +220,9 @@ class WalkPushConnection extends BaseConnection implements PushConnection {
String pathPack = null;
String pathIdx = null;
- final PackWriter writer = new PackWriter(transport.getPackConfig(),
- local.newObjectReader());
- try {
+ try (final PackWriter writer = new PackWriter(transport.getPackConfig(),
+ local.newObjectReader())) {
+
final Set<ObjectId> need = new HashSet<ObjectId>();
final Set<ObjectId> have = new HashSet<ObjectId>();
for (final RemoteRefUpdate r : updates)
@@ -293,8 +293,6 @@ class WalkPushConnection extends BaseConnection implements PushConnection {
safeDelete(pathPack);
throw new TransportException(uri, JGitText.get().cannotStoreObjects, err);
- } finally {
- writer.release();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackMayNotContinueException.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WriteAbortedException.java
similarity index 71%
rename from org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackMayNotContinueException.java
rename to org.eclipse.jgit/src/org/eclipse/jgit/transport/WriteAbortedException.java
index 5392a01..267bf7a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackMayNotContinueException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/WriteAbortedException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, Google Inc.
+ * Copyright (C) 2015, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -43,26 +43,43 @@
package org.eclipse.jgit.transport;
+import java.io.IOException;
+
/**
- * Indicates UploadPack may not continue execution.
+ * An exception to be thrown when the write operation is aborted.
+ * <p>
+ * That can be thrown inside
+ * {@link ObjectCountCallback#setObjectCount(long)}.
*
- * @deprecated use {@link ServiceMayNotContinueException} instead.
+ * @since 4.1
*/
- at Deprecated
-public class UploadPackMayNotContinueException extends ServiceMayNotContinueException {
+public class WriteAbortedException extends IOException {
private static final long serialVersionUID = 1L;
- /** Initialize with no message. */
- public UploadPackMayNotContinueException() {
- // Do not set a message.
+ /**
+ * Construct a {@code WriteAbortedException}.
+ */
+ public WriteAbortedException() {
+ }
+
+ /**
+ * Construct a {@code WriteAbortedException}.
+ *
+ * @param s message describing the issue
+ */
+ public WriteAbortedException(String s) {
+ super(s);
}
/**
- * @param msg
- * a message explaining why it cannot continue. This message may
- * be shown to an end-user.
+ * Construct a {@code WriteAbortedException}.
+ *
+ * @param s
+ * message describing the issue
+ * @param why
+ * a lower level implementation specific issue.
*/
- public UploadPackMayNotContinueException(String msg) {
- super(msg);
+ public WriteAbortedException(String s, Throwable why) {
+ super(s, why);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ServiceNotAuthorizedException.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ServiceNotAuthorizedException.java
index a2c6edc..57a6192 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ServiceNotAuthorizedException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ServiceNotAuthorizedException.java
@@ -45,12 +45,35 @@ package org.eclipse.jgit.transport.resolver;
import org.eclipse.jgit.internal.JGitText;
-/** Indicates the request service is not authorized for current user. */
+/**
+ * Indicates that the requested service requires authentication that
+ * the current user has not provided.
+ * <p>
+ * This corresponds to response code
+ * {@code HttpServletResponse.SC_UNAUTHORIZED}.
+ */
public class ServiceNotAuthorizedException extends Exception {
private static final long serialVersionUID = 1L;
- /** Indicates the request service is not available. */
+ /**
+ * @param message
+ * @param cause
+ * @since 4.1
+ */
+ public ServiceNotAuthorizedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * @param message
+ * @since 4.1
+ */
+ public ServiceNotAuthorizedException(String message) {
+ super(message);
+ }
+
+ /** Indicates that the requested service requires authentication. */
public ServiceNotAuthorizedException() {
- super(JGitText.get().serviceNotPermittedNoName);
+ super(JGitText.get().unauthorized);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ServiceNotEnabledException.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ServiceNotEnabledException.java
index d0fa758..78ae303 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ServiceNotEnabledException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ServiceNotEnabledException.java
@@ -49,6 +49,23 @@ import org.eclipse.jgit.internal.JGitText;
public class ServiceNotEnabledException extends Exception {
private static final long serialVersionUID = 1L;
+ /**
+ * @param message
+ * @param cause
+ * @since 4.1
+ */
+ public ServiceNotEnabledException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * @param message
+ * @since 4.1
+ */
+ public ServiceNotEnabledException(String message) {
+ super(message);
+ }
+
/** Indicates the request service is not available. */
public ServiceNotEnabledException() {
super(JGitText.get().serviceNotEnabledNoName);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java
index 41e593e..5813635 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java
@@ -49,6 +49,7 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
+import org.eclipse.jgit.attributes.AttributesNode;
import org.eclipse.jgit.dircache.DirCacheCheckout;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -58,6 +59,7 @@ import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
+import org.eclipse.jgit.util.Paths;
/**
* Walks a Git tree (directory) in Git sort order.
@@ -93,6 +95,13 @@ public abstract class AbstractTreeIterator {
AbstractTreeIterator matches;
/**
+ * Parsed rules of .gitattributes file if it exists.
+ *
+ * @since 4.2
+ */
+ protected AttributesNode attributesNode;
+
+ /**
* Number of entries we moved forward to force a D/F conflict match.
*
* @see NameConflictTreeWalk
@@ -320,6 +329,42 @@ public abstract class AbstractTreeIterator {
}
/**
+ * Seek the iterator on a file, if present.
+ *
+ * @param name
+ * file name to find (will not find a directory).
+ * @return true if the file exists in this tree; false otherwise.
+ * @throws CorruptObjectException
+ * tree is invalid.
+ * @since 4.2
+ */
+ public boolean findFile(String name) throws CorruptObjectException {
+ return findFile(Constants.encode(name));
+ }
+
+ /**
+ * Seek the iterator on a file, if present.
+ *
+ * @param name
+ * file name to find (will not find a directory).
+ * @return true if the file exists in this tree; false otherwise.
+ * @throws CorruptObjectException
+ * tree is invalid.
+ * @since 4.2
+ */
+ public boolean findFile(byte[] name) throws CorruptObjectException {
+ for (; !eof(); next(1)) {
+ int cmp = pathCompare(name, 0, name.length, 0, pathOffset);
+ if (cmp == 0) {
+ return true;
+ } else if (cmp > 0) {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ /**
* Compare the path of this current entry to a raw buffer.
*
* @param buf
@@ -338,20 +383,9 @@ public abstract class AbstractTreeIterator {
}
private int pathCompare(byte[] b, int bPos, int bEnd, int bMode, int aPos) {
- final byte[] a = path;
- final int aEnd = pathLen;
-
- for (; aPos < aEnd && bPos < bEnd; aPos++, bPos++) {
- final int cmp = (a[aPos] & 0xff) - (b[bPos] & 0xff);
- if (cmp != 0)
- return cmp;
- }
-
- if (aPos < aEnd)
- return (a[aPos] & 0xff) - lastPathChar(bMode);
- if (bPos < bEnd)
- return lastPathChar(mode) - (b[bPos] & 0xff);
- return lastPathChar(mode) - lastPathChar(bMode);
+ return Paths.compare(
+ path, aPos, pathLen, mode,
+ b, bPos, bEnd, bMode);
}
private static int alreadyMatch(AbstractTreeIterator a,
@@ -368,10 +402,6 @@ public abstract class AbstractTreeIterator {
}
}
- private static int lastPathChar(final int mode) {
- return FileMode.TREE.equals(mode) ? '/' : '\0';
- }
-
/**
* Check if the current entry of both iterators has the same id.
* <p>
@@ -648,6 +678,14 @@ public abstract class AbstractTreeIterator {
}
/**
+ * @return true if the iterator implements {@link #stopWalk()}.
+ * @since 4.2
+ */
+ protected boolean needsStopWalk() {
+ return false;
+ }
+
+ /**
* @return the length of the name component of the path for the current entry
*/
public int getNameLength() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java
index 0805e50..c038f07 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java
@@ -44,13 +44,23 @@
package org.eclipse.jgit.treewalk;
+import static org.eclipse.jgit.lib.Constants.DOT_GIT_ATTRIBUTES;
+import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
+import static org.eclipse.jgit.lib.Constants.TYPE_TREE;
+import static org.eclipse.jgit.lib.Constants.encode;
+
import java.io.IOException;
+import java.io.InputStream;
import java.util.Arrays;
+import java.util.Collections;
+import org.eclipse.jgit.attributes.AttributesNode;
+import org.eclipse.jgit.attributes.AttributesRule;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectId;
@@ -59,6 +69,7 @@ import org.eclipse.jgit.lib.ObjectReader;
/** Parses raw Git trees from the canonical semi-text/semi-binary format. */
public class CanonicalTreeParser extends AbstractTreeIterator {
private static final byte[] EMPTY = {};
+ private static final byte[] ATTRS = encode(DOT_GIT_ATTRIBUTES);
private byte[] raw;
@@ -124,6 +135,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
* the raw tree content.
*/
public void reset(final byte[] treeData) {
+ attributesNode = null;
raw = treeData;
prevPtr = -1;
currPtr = 0;
@@ -199,7 +211,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
*/
public void reset(final ObjectReader reader, final AnyObjectId id)
throws IncorrectObjectTypeException, IOException {
- reset(reader.open(id, Constants.OBJ_TREE).getCachedBytes());
+ reset(reader.open(id, OBJ_TREE).getCachedBytes());
}
@Override
@@ -209,7 +221,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
idBuffer.fromRaw(idBuffer(), idOffset());
if (!FileMode.TREE.equals(mode)) {
final ObjectId me = idBuffer.toObjectId();
- throw new IncorrectObjectTypeException(me, Constants.TYPE_TREE);
+ throw new IncorrectObjectTypeException(me, TYPE_TREE);
}
return createSubtreeIterator0(reader, idBuffer);
}
@@ -254,7 +266,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
@Override
public int idOffset() {
- return nextPtr - Constants.OBJECT_ID_LENGTH;
+ return nextPtr - OBJECT_ID_LENGTH;
}
@Override
@@ -292,7 +304,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
prevPtr = ptr;
while (raw[ptr] != 0)
ptr++;
- ptr += Constants.OBJECT_ID_LENGTH + 1;
+ ptr += OBJECT_ID_LENGTH + 1;
}
if (delta != 0)
throw new ArrayIndexOutOfBoundsException(delta);
@@ -328,7 +340,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
trace[delta] = ptr;
while (raw[ptr] != 0)
ptr++;
- ptr += Constants.OBJECT_ID_LENGTH + 1;
+ ptr += OBJECT_ID_LENGTH + 1;
}
if (trace[1] == -1)
throw new ArrayIndexOutOfBoundsException(delta);
@@ -363,6 +375,46 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
}
}
pathLen = tmp;
- nextPtr = ptr + Constants.OBJECT_ID_LENGTH;
+ nextPtr = ptr + OBJECT_ID_LENGTH;
+ }
+
+ /**
+ * Retrieve the {@link AttributesNode} for the current entry.
+ *
+ * @param reader
+ * {@link ObjectReader} used to parse the .gitattributes entry.
+ * @return {@link AttributesNode} for the current entry.
+ * @throws IOException
+ * @since 4.2
+ */
+ public AttributesNode getEntryAttributesNode(ObjectReader reader)
+ throws IOException {
+ if (attributesNode == null) {
+ attributesNode = findAttributes(reader);
+ }
+ return attributesNode.getRules().isEmpty() ? null : attributesNode;
+ }
+
+ private AttributesNode findAttributes(ObjectReader reader)
+ throws IOException {
+ CanonicalTreeParser itr = new CanonicalTreeParser();
+ itr.reset(raw);
+ if (itr.findFile(ATTRS)) {
+ return loadAttributes(reader, itr.getEntryObjectId());
+ }
+ return noAttributes();
+ }
+
+ private static AttributesNode loadAttributes(ObjectReader reader,
+ AnyObjectId id) throws IOException {
+ AttributesNode r = new AttributesNode();
+ try (InputStream in = reader.open(id, OBJ_BLOB).openStream()) {
+ r.parse(in);
+ }
+ return r.getRules().isEmpty() ? noAttributes() : r;
+ }
+
+ private static AttributesNode noAttributes() {
+ return new AttributesNode(Collections.<AttributesRule> emptyList());
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/EmptyTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/EmptyTreeIterator.java
index 8dbf80e..ec4a84e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/EmptyTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/EmptyTreeIterator.java
@@ -142,4 +142,9 @@ public class EmptyTreeIterator extends AbstractTreeIterator {
if (parent != null)
parent.stopWalk();
}
+
+ @Override
+ protected boolean needsStopWalk() {
+ return parent != null && parent.needsStopWalk();
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
index 8d2cb1d..accf495 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
@@ -67,8 +67,8 @@ import org.eclipse.jgit.util.FS;
*/
public class FileTreeIterator extends WorkingTreeIterator {
/**
- * the starting directory. This directory should correspond to the root of
- * the repository.
+ * the starting directory of this Iterator. All entries are located directly
+ * in this directory.
*/
protected final File directory;
@@ -238,8 +238,6 @@ public class FileTreeIterator extends WorkingTreeIterator {
@Override
protected byte[] idSubmodule(final Entry e) {
- if (repository == null)
- return idSubmodule(getDirectory(), e);
- return super.idSubmodule(e);
+ return idSubmodule(getDirectory(), e);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
index 2d6acbd..d2195a8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
@@ -43,6 +43,8 @@
package org.eclipse.jgit.treewalk;
+import java.io.IOException;
+
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.lib.FileMode;
@@ -96,7 +98,7 @@ public class NameConflictTreeWalk extends TreeWalk {
* the repository the walker will obtain data from.
*/
public NameConflictTreeWalk(final Repository repo) {
- this(repo.newObjectReader());
+ super(repo);
}
/**
@@ -338,6 +340,41 @@ public class NameConflictTreeWalk extends TreeWalk {
dfConflict = null;
}
+ void stopWalk() throws IOException {
+ if (!needsStopWalk()) {
+ return;
+ }
+
+ // Name conflicts make aborting early difficult. Multiple paths may
+ // exist between the file and directory versions of a name. To ensure
+ // the directory version is skipped over (as it was previously visited
+ // during the file version step) requires popping up the stack and
+ // finishing out each subtree that the walker dove into. Siblings in
+ // parents do not need to be recursed into, bounding the cost.
+ for (;;) {
+ AbstractTreeIterator t = min();
+ if (t.eof()) {
+ if (depth > 0) {
+ exitSubtree();
+ popEntriesEqual();
+ continue;
+ }
+ return;
+ }
+ currentHead = t;
+ skipEntriesEqual();
+ }
+ }
+
+ private boolean needsStopWalk() {
+ for (AbstractTreeIterator t : trees) {
+ if (t.needsStopWalk()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* True if the current entry is covered by a directory/file conflict.
*
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
index 94281fb..5cd713d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
@@ -45,12 +45,26 @@
package org.eclipse.jgit.treewalk;
import java.io.IOException;
-
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.attributes.Attribute;
+import org.eclipse.jgit.attributes.Attribute.State;
+import org.eclipse.jgit.attributes.Attributes;
+import org.eclipse.jgit.attributes.AttributesNode;
+import org.eclipse.jgit.attributes.AttributesNodeProvider;
+import org.eclipse.jgit.attributes.AttributesProvider;
+import org.eclipse.jgit.dircache.DirCacheBuildIterator;
+import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.StopWalkException;
import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.MutableObjectId;
@@ -60,6 +74,7 @@ import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
+import org.eclipse.jgit.util.QuotedString;
import org.eclipse.jgit.util.RawParseUtils;
/**
@@ -82,10 +97,45 @@ import org.eclipse.jgit.util.RawParseUtils;
* Multiple simultaneous TreeWalk instances per {@link Repository} are
* permitted, even from concurrent threads.
*/
-public class TreeWalk {
+public class TreeWalk implements AutoCloseable, AttributesProvider {
private static final AbstractTreeIterator[] NO_TREES = {};
/**
+ * @since 4.2
+ */
+ public static enum OperationType {
+ /**
+ * Represents a checkout operation (for example a checkout or reset
+ * operation).
+ */
+ CHECKOUT_OP,
+
+ /**
+ * Represents a checkin operation (for example an add operation)
+ */
+ CHECKIN_OP
+ }
+
+ /**
+ * Type of operation you want to retrieve the git attributes for.
+ */
+ private OperationType operationType = OperationType.CHECKOUT_OP;
+
+ /**
+ * The filter command as defined in gitattributes. The keys are
+ * filterName+"."+filterCommandType. E.g. "lfs.clean"
+ */
+ private Map<String, String> filterCommandsByNameDotType = new HashMap<String, String>();
+
+ /**
+ * @param operationType
+ * @since 4.2
+ */
+ public void setOperationType(OperationType operationType) {
+ this.operationType = operationType;
+ }
+
+ /**
* Open a tree walk and filter to exactly one path.
* <p>
* The returned tree walk is already positioned on the requested path, so
@@ -157,11 +207,8 @@ public class TreeWalk {
public static TreeWalk forPath(final Repository db, final String path,
final AnyObjectId... trees) throws MissingObjectException,
IncorrectObjectTypeException, CorruptObjectException, IOException {
- ObjectReader reader = db.newObjectReader();
- try {
+ try (ObjectReader reader = db.newObjectReader()) {
return forPath(reader, path, trees);
- } finally {
- reader.release();
}
}
@@ -198,6 +245,8 @@ public class TreeWalk {
private final ObjectReader reader;
+ private final boolean closeReader;
+
private final MutableObjectId idBuffer = new MutableObjectId();
private TreeFilter filter;
@@ -208,34 +257,51 @@ public class TreeWalk {
private boolean postOrderTraversal;
- private int depth;
+ int depth;
private boolean advance;
private boolean postChildren;
+ private AttributesNodeProvider attributesNodeProvider;
+
AbstractTreeIterator currentHead;
+ /** Cached attribute for the current entry */
+ private Attributes attrs = null;
+
+ private Config config;
+
/**
* Create a new tree walker for a given repository.
*
* @param repo
- * the repository the walker will obtain data from.
+ * the repository the walker will obtain data from. An
+ * ObjectReader will be created by the walker, and will be closed
+ * when the walker is closed.
*/
public TreeWalk(final Repository repo) {
- this(repo.newObjectReader());
+ this(repo.newObjectReader(), true);
+ config = repo.getConfig();
+ attributesNodeProvider = repo.createAttributesNodeProvider();
}
/**
* Create a new tree walker for a given repository.
*
* @param or
- * the reader the walker will obtain tree data from.
+ * the reader the walker will obtain tree data from. The reader
+ * is not closed when the walker is closed.
*/
public TreeWalk(final ObjectReader or) {
+ this(or, false);
+ }
+
+ private TreeWalk(final ObjectReader or, final boolean closeReader) {
reader = or;
filter = TreeFilter.ALL;
trees = NO_TREES;
+ this.closeReader = closeReader;
}
/** @return the reader this walker is using to load objects. */
@@ -248,9 +314,14 @@ public class TreeWalk {
* <p>
* A walker that has been released can be used again, but may need to be
* released after the subsequent usage.
+ *
+ * @since 4.0
*/
- public void release() {
- reader.release();
+ @Override
+ public void close() {
+ if (closeReader) {
+ reader.close();
+ }
}
/**
@@ -344,8 +415,29 @@ public class TreeWalk {
postOrderTraversal = b;
}
+ /**
+ * Sets the {@link AttributesNodeProvider} for this {@link TreeWalk}.
+ * <p>
+ * This is a requirement for a correct computation of the git attributes.
+ * If this {@link TreeWalk} has been built using
+ * {@link #TreeWalk(Repository)} constructor, the
+ * {@link AttributesNodeProvider} has already been set. Indeed,the
+ * {@link Repository} can provide an {@link AttributesNodeProvider} using
+ * {@link Repository#createAttributesNodeProvider()} method. Otherwise you
+ * should provide one.
+ * </p>
+ *
+ * @see Repository#createAttributesNodeProvider()
+ * @param provider
+ * @since 4.2
+ */
+ public void setAttributesNodeProvider(AttributesNodeProvider provider) {
+ attributesNodeProvider = provider;
+ }
+
/** Reset this walker so new tree iterators can be added to it. */
public void reset() {
+ attrs = null;
trees = NO_TREES;
advance = false;
depth = 0;
@@ -389,6 +481,7 @@ public class TreeWalk {
advance = false;
depth = 0;
+ attrs = null;
}
/**
@@ -438,6 +531,7 @@ public class TreeWalk {
trees = r;
advance = false;
depth = 0;
+ attrs = null;
}
/**
@@ -480,18 +574,13 @@ public class TreeWalk {
* @param p
* an iterator to walk over. The iterator should be new, with no
* parent, and should still be positioned before the first entry.
- * The tree which the iterator operates on must have the same root
- * as other trees in the walk.
- *
+ * The tree which the iterator operates on must have the same
+ * root as other trees in the walk.
* @return position of this tree within the walker.
- * @throws CorruptObjectException
- * the iterator was unable to obtain its first entry, due to
- * possible data corruption within the backing data store.
*/
- public int addTree(final AbstractTreeIterator p)
- throws CorruptObjectException {
- final int n = trees.length;
- final AbstractTreeIterator[] newTrees = new AbstractTreeIterator[n + 1];
+ public int addTree(AbstractTreeIterator p) {
+ int n = trees.length;
+ AbstractTreeIterator[] newTrees = new AbstractTreeIterator[n + 1];
System.arraycopy(trees, 0, newTrees, 0, n);
newTrees[n] = p;
@@ -534,6 +623,7 @@ public class TreeWalk {
public boolean next() throws MissingObjectException,
IncorrectObjectTypeException, CorruptObjectException, IOException {
try {
+ attrs = null;
if (advance) {
advance = false;
postChildren = false;
@@ -571,13 +661,30 @@ public class TreeWalk {
return true;
}
} catch (StopWalkException stop) {
- for (final AbstractTreeIterator t : trees)
- t.stopWalk();
+ stopWalk();
return false;
}
}
/**
+ * Notify iterators the walk is aborting.
+ * <p>
+ * Primarily to notify {@link DirCacheBuildIterator} the walk is aborting so
+ * that it can copy any remaining entries.
+ *
+ * @throws IOException
+ * if traversal of remaining entries throws an exception during
+ * object access. This should never occur as remaining trees
+ * should already be in memory, however the methods used to
+ * finish traversal are declared to throw IOException.
+ */
+ void stopWalk() throws IOException {
+ for (AbstractTreeIterator t : trees) {
+ t.stopWalk();
+ }
+ }
+
+ /**
* Obtain the tree iterator for the current entry.
* <p>
* Entering into (or exiting out of) a subtree causes the current tree
@@ -767,10 +874,13 @@ public class TreeWalk {
* Test if the supplied path matches the current entry's path.
* <p>
* This method tests that the supplied path is exactly equal to the current
- * entry, or is one of its parent directories. It is faster to use this
+ * entry or is one of its parent directories. It is faster to use this
* method then to use {@link #getPathString()} to first create a String
* object, then test <code>startsWith</code> or some other type of string
* match function.
+ * <p>
+ * If the current entry is a subtree, then all paths within the subtree
+ * are considered to match it.
*
* @param p
* path buffer to test. Callers should ensure the path does not
@@ -806,7 +916,7 @@ public class TreeWalk {
// If p[ci] == '/' then pattern matches this subtree,
// otherwise we cannot be certain so we return -1.
//
- return p[ci] == '/' ? 0 : -1;
+ return p[ci] == '/' && FileMode.TREE.equals(t.mode) ? 0 : -1;
}
// Both strings are identical.
@@ -903,6 +1013,7 @@ public class TreeWalk {
*/
public void enterSubtree() throws MissingObjectException,
IncorrectObjectTypeException, CorruptObjectException, IOException {
+ attrs = null;
final AbstractTreeIterator ch = currentHead;
final AbstractTreeIterator[] tmp = new AbstractTreeIterator[trees.length];
for (int i = 0; i < trees.length; i++) {
@@ -967,7 +1078,7 @@ public class TreeWalk {
}
}
- private void exitSubtree() {
+ void exitSubtree() {
depth--;
for (int i = 0; i < trees.length; i++)
trees[i] = trees[i].parent;
@@ -996,4 +1107,296 @@ public class TreeWalk {
static String pathOf(final byte[] buf, int pos, int end) {
return RawParseUtils.decode(Constants.CHARSET, buf, pos, end);
}
+
+ /**
+ * Retrieve the git attributes for the current entry.
+ *
+ * <h4>Git attribute computation</h4>
+ *
+ * <ul>
+ * <li>Get the attributes matching the current path entry from the info file
+ * (see {@link AttributesNodeProvider#getInfoAttributesNode()}).</li>
+ * <li>Completes the list of attributes using the .gitattributes files
+ * located on the current path (the further the directory that contains
+ * .gitattributes is from the path in question, the lower its precedence).
+ * For a checkin operation, it will look first on the working tree (if any).
+ * If there is no attributes file, it will fallback on the index. For a
+ * checkout operation, it will first use the index entry and then fallback
+ * on the working tree if none.</li>
+ * <li>In the end, completes the list of matching attributes using the
+ * global attribute file define in the configuration (see
+ * {@link AttributesNodeProvider#getGlobalAttributesNode()})</li>
+ *
+ * </ul>
+ *
+ *
+ * <h4>Iterator constraints</h4>
+ *
+ * <p>
+ * In order to have a correct list of attributes for the current entry, this
+ * {@link TreeWalk} requires to have at least one
+ * {@link AttributesNodeProvider} and a {@link DirCacheIterator} set up. An
+ * {@link AttributesNodeProvider} is used to retrieve the attributes from
+ * the info attributes file and the global attributes file. The
+ * {@link DirCacheIterator} is used to retrieve the .gitattributes files
+ * stored in the index. A {@link WorkingTreeIterator} can also be provided
+ * to access the local version of the .gitattributes files. If none is
+ * provided it will fallback on the {@link DirCacheIterator}.
+ * </p>
+ *
+ * @return a {@link Set} of {@link Attribute}s that match the current entry.
+ * @since 4.2
+ */
+ public Attributes getAttributes() {
+ if (attrs != null)
+ return attrs;
+
+ if (attributesNodeProvider == null) {
+ // The work tree should have a AttributesNodeProvider to be able to
+ // retrieve the info and global attributes node
+ throw new IllegalStateException(
+ "The tree walk should have one AttributesNodeProvider set in order to compute the git attributes."); //$NON-NLS-1$
+ }
+
+ WorkingTreeIterator workingTreeIterator = getTree(WorkingTreeIterator.class);
+ DirCacheIterator dirCacheIterator = getTree(DirCacheIterator.class);
+ CanonicalTreeParser other = getTree(CanonicalTreeParser.class);
+
+ if (workingTreeIterator == null && dirCacheIterator == null
+ && other == null) {
+ // Can not retrieve the attributes without at least one of the above
+ // iterators.
+ return new Attributes();
+ }
+
+ String path = currentHead.getEntryPathString();
+ final boolean isDir = FileMode.TREE.equals(currentHead.mode);
+ Attributes attributes = new Attributes();
+ try {
+ // Gets the global attributes node
+ AttributesNode globalNodeAttr = attributesNodeProvider
+ .getGlobalAttributesNode();
+ // Gets the info attributes node
+ AttributesNode infoNodeAttr = attributesNodeProvider
+ .getInfoAttributesNode();
+
+ // Gets the info attributes
+ if (infoNodeAttr != null) {
+ infoNodeAttr.getAttributes(path, isDir, attributes);
+ }
+
+ // Gets the attributes located on the current entry path
+ getPerDirectoryEntryAttributes(path, isDir, operationType,
+ workingTreeIterator, dirCacheIterator, other, attributes);
+
+ // Gets the attributes located in the global attribute file
+ if (globalNodeAttr != null) {
+ globalNodeAttr.getAttributes(path, isDir, attributes);
+ }
+ } catch (IOException e) {
+ throw new JGitInternalException("Error while parsing attributes", e); //$NON-NLS-1$
+ }
+ // now after all attributes are collected - in the correct hierarchy
+ // order - remove all unspecified entries (the ! marker)
+ for (Attribute a : attributes.getAll()) {
+ if (a.getState() == State.UNSPECIFIED)
+ attributes.remove(a.getKey());
+ }
+ return attributes;
+ }
+
+ /**
+ * Get the attributes located on the current entry path.
+ *
+ * @param path
+ * current entry path
+ * @param isDir
+ * holds true if the current entry is a directory
+ * @param opType
+ * type of operation
+ * @param workingTreeIterator
+ * a {@link WorkingTreeIterator} matching the current entry
+ * @param dirCacheIterator
+ * a {@link DirCacheIterator} matching the current entry
+ * @param other
+ * a {@link CanonicalTreeParser} matching the current entry
+ * @param attributes
+ * Non null map holding the existing attributes. This map will be
+ * augmented with new entry. None entry will be overrided.
+ * @throws IOException
+ * It raises an {@link IOException} if a problem appears while
+ * parsing one on the attributes file.
+ */
+ private void getPerDirectoryEntryAttributes(String path, boolean isDir,
+ OperationType opType, WorkingTreeIterator workingTreeIterator,
+ DirCacheIterator dirCacheIterator, CanonicalTreeParser other,
+ Attributes attributes)
+ throws IOException {
+ // Prevents infinite recurrence
+ if (workingTreeIterator != null || dirCacheIterator != null
+ || other != null) {
+ AttributesNode currentAttributesNode = getCurrentAttributesNode(
+ opType, workingTreeIterator, dirCacheIterator, other);
+ if (currentAttributesNode != null) {
+ currentAttributesNode.getAttributes(path, isDir, attributes);
+ }
+ getPerDirectoryEntryAttributes(path, isDir, opType,
+ getParent(workingTreeIterator, WorkingTreeIterator.class),
+ getParent(dirCacheIterator, DirCacheIterator.class),
+ getParent(other, CanonicalTreeParser.class), attributes);
+ }
+ }
+
+ private static <T extends AbstractTreeIterator> T getParent(T current,
+ Class<T> type) {
+ if (current != null) {
+ AbstractTreeIterator parent = current.parent;
+ if (type.isInstance(parent)) {
+ return type.cast(parent);
+ }
+ }
+ return null;
+ }
+
+ private <T extends AbstractTreeIterator> T getTree(Class<T> type) {
+ for (int i = 0; i < trees.length; i++) {
+ AbstractTreeIterator tree = trees[i];
+ if (type.isInstance(tree)) {
+ return type.cast(tree);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the {@link AttributesNode} for the current entry.
+ * <p>
+ * This method implements the fallback mechanism between the index and the
+ * working tree depending on the operation type
+ * </p>
+ *
+ * @param opType
+ * @param workingTreeIterator
+ * @param dirCacheIterator
+ * @param other
+ * @return a {@link AttributesNode} of the current entry,
+ * {@link NullPointerException} otherwise.
+ * @throws IOException
+ * It raises an {@link IOException} if a problem appears while
+ * parsing one on the attributes file.
+ */
+ private AttributesNode getCurrentAttributesNode(OperationType opType,
+ @Nullable WorkingTreeIterator workingTreeIterator,
+ @Nullable DirCacheIterator dirCacheIterator,
+ @Nullable CanonicalTreeParser other)
+ throws IOException {
+ AttributesNode attributesNode = null;
+ switch (opType) {
+ case CHECKIN_OP:
+ if (workingTreeIterator != null) {
+ attributesNode = workingTreeIterator.getEntryAttributesNode();
+ }
+ if (attributesNode == null && dirCacheIterator != null) {
+ attributesNode = getAttributesNode(dirCacheIterator
+ .getEntryAttributesNode(getObjectReader()),
+ attributesNode);
+ }
+ if (attributesNode == null && other != null) {
+ attributesNode = getAttributesNode(
+ other.getEntryAttributesNode(getObjectReader()),
+ attributesNode);
+ }
+ break;
+ case CHECKOUT_OP:
+ if (other != null) {
+ attributesNode = other
+ .getEntryAttributesNode(getObjectReader());
+ }
+ if (dirCacheIterator != null) {
+ attributesNode = getAttributesNode(dirCacheIterator
+ .getEntryAttributesNode(getObjectReader()),
+ attributesNode);
+ }
+ if (attributesNode == null && workingTreeIterator != null) {
+ attributesNode = getAttributesNode(
+ workingTreeIterator.getEntryAttributesNode(),
+ attributesNode);
+ }
+ break;
+ default:
+ throw new IllegalStateException(
+ "The only supported operation types are:" //$NON-NLS-1$
+ + OperationType.CHECKIN_OP + "," //$NON-NLS-1$
+ + OperationType.CHECKOUT_OP);
+ }
+
+ return attributesNode;
+ }
+
+ private static AttributesNode getAttributesNode(AttributesNode value,
+ AttributesNode defaultValue) {
+ return (value == null) ? defaultValue : value;
+ }
+
+ /**
+ * Inspect config and attributes to return a filtercommand applicable for
+ * the current path
+ *
+ * @param filterCommandType
+ * which type of filterCommand should be executed. E.g. "clean",
+ * "smudge"
+ * @return a filter command
+ * @throws IOException
+ * @since 4.2
+ */
+ public String getFilterCommand(String filterCommandType)
+ throws IOException {
+ Attributes attributes = getAttributes();
+
+ Attribute f = attributes.get(Constants.ATTR_FILTER);
+ if (f == null) {
+ return null;
+ }
+ String filterValue = f.getValue();
+ if (filterValue == null) {
+ return null;
+ }
+
+ String filterCommand = getFilterCommandDefinition(filterValue,
+ filterCommandType);
+ if (filterCommand == null) {
+ return null;
+ }
+ return filterCommand.replaceAll("%f", //$NON-NLS-1$
+ QuotedString.BOURNE.quote((getPathString())));
+ }
+
+ /**
+ * Get the filter command how it is defined in gitconfig. The returned
+ * string may contain "%f" which needs to be replaced by the current path
+ * before executing the filter command. These filter definitions are cached
+ * for better performance.
+ *
+ * @param filterDriverName
+ * The name of the filter driver as it is referenced in the
+ * gitattributes file. E.g. "lfs". For each filter driver there
+ * may be many commands defined in the .gitconfig
+ * @param filterCommandType
+ * The type of the filter command for a specific filter driver.
+ * May be "clean" or "smudge".
+ * @return the definition of the command to be executed for this filter
+ * driver and filter command
+ */
+ private String getFilterCommandDefinition(String filterDriverName,
+ String filterCommandType) {
+ String key = filterDriverName + "." + filterCommandType; //$NON-NLS-1$
+ String filterCommand = filterCommandsByNameDotType.get(key);
+ if (filterCommand != null)
+ return filterCommand;
+ filterCommand = config.getString(Constants.ATTR_FILTER,
+ filterDriverName, filterCommandType);
+ if (filterCommand != null)
+ filterCommandsByNameDotType.put(key, filterCommand);
+ return filterCommand;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
index 3838149..0d617ee 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -62,7 +62,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
-import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.api.errors.FilterFailedException;
import org.eclipse.jgit.attributes.AttributesNode;
import org.eclipse.jgit.attributes.AttributesRule;
import org.eclipse.jgit.diff.RawText;
@@ -77,6 +77,7 @@ import org.eclipse.jgit.ignore.IgnoreNode;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.CoreConfig;
+import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
import org.eclipse.jgit.lib.CoreConfig.CheckStat;
import org.eclipse.jgit.lib.CoreConfig.SymLinks;
import org.eclipse.jgit.lib.FileMode;
@@ -86,7 +87,9 @@ import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.submodule.SubmoduleWalk;
import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.FS.ExecutionResult;
import org.eclipse.jgit.util.IO;
+import org.eclipse.jgit.util.Paths;
import org.eclipse.jgit.util.RawParseUtils;
import org.eclipse.jgit.util.io.EolCanonicalizingInputStream;
@@ -102,6 +105,8 @@ import org.eclipse.jgit.util.io.EolCanonicalizingInputStream;
* @see FileTreeIterator
*/
public abstract class WorkingTreeIterator extends AbstractTreeIterator {
+ private static final int MAX_EXCEPTION_TEXT_SIZE = 10 * 1024;
+
/** An empty entry array, suitable for {@link #init(Entry[])}. */
protected static final Entry[] EOF = {};
@@ -135,8 +140,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
/** If there is a .gitignore file present, the parsed rules from it. */
private IgnoreNode ignoreNode;
- /** If there is a .gitattributes file present, the parsed rules from it. */
- private AttributesNode attributesNode;
+ private String cleanFilterCommand;
/** Repository that is the root level being iterated over */
protected Repository repository;
@@ -148,19 +152,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
private int contentIdOffset;
/**
- * Holds the {@link AttributesNode} that is stored in
- * $GIT_DIR/info/attributes file.
- */
- private AttributesNode infoAttributeNode;
-
- /**
- * Holds the {@link AttributesNode} that is stored in global attribute file.
- *
- * @see CoreConfig#getAttributesFile()
- */
- private AttributesNode globalAttributeNode;
-
- /**
* Create a new iterator with no parent.
*
* @param options
@@ -203,8 +194,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
protected WorkingTreeIterator(final WorkingTreeIterator p) {
super(p);
state = p.state;
- infoAttributeNode = p.infoAttributeNode;
- globalAttributeNode = p.globalAttributeNode;
+ repository = p.repository;
}
/**
@@ -224,10 +214,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
else
entry = null;
ignoreNode = new RootIgnoreNode(entry, repo);
-
- infoAttributeNode = new InfoAttributesNode(repo);
-
- globalAttributeNode = new GlobalAttributesNode(repo);
}
/**
@@ -371,7 +357,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
private InputStream possiblyFilteredInputStream(final Entry e,
final InputStream is, final long len) throws IOException {
- if (!mightNeedCleaning()) {
+ boolean mightNeedCleaning = mightNeedCleaning();
+ if (!mightNeedCleaning) {
canonLen = len;
return is;
}
@@ -389,7 +376,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
return new ByteArrayInputStream(raw, 0, n);
}
- if (isBinary(e)) {
+ // TODO: fix autocrlf causing mightneedcleaning
+ if (!mightNeedCleaning && isBinary(e)) {
canonLen = len;
return is;
}
@@ -413,10 +401,12 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
}
}
- private boolean mightNeedCleaning() {
+ private boolean mightNeedCleaning() throws IOException {
switch (getOptions().getAutoCRLF()) {
case FALSE:
default:
+ if (getCleanFilterCommand() != null)
+ return true;
return false;
case TRUE:
@@ -438,8 +428,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
}
}
- private static ByteBuffer filterClean(byte[] src, int n)
- throws IOException {
+ private ByteBuffer filterClean(byte[] src, int n) throws IOException {
InputStream in = new ByteArrayInputStream(src);
try {
return IO.readWholeStream(filterClean(in), n);
@@ -448,8 +437,42 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
}
}
- private static InputStream filterClean(InputStream in) {
- return new EolCanonicalizingInputStream(in, true);
+ private InputStream filterClean(InputStream in) throws IOException {
+ in = handleAutoCRLF(in);
+ String filterCommand = getCleanFilterCommand();
+ if (filterCommand != null) {
+ FS fs = repository.getFS();
+ ProcessBuilder filterProcessBuilder = fs.runInShell(filterCommand,
+ new String[0]);
+ filterProcessBuilder.directory(repository.getWorkTree());
+ filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY,
+ repository.getDirectory().getAbsolutePath());
+ ExecutionResult result;
+ try {
+ result = fs.execute(filterProcessBuilder, in);
+ } catch (IOException | InterruptedException e) {
+ throw new IOException(new FilterFailedException(e,
+ filterCommand, getEntryPathString()));
+ }
+ int rc = result.getRc();
+ if (rc != 0) {
+ throw new IOException(new FilterFailedException(rc,
+ filterCommand, getEntryPathString(),
+ result.getStdout().toByteArray(MAX_EXCEPTION_TEXT_SIZE),
+ RawParseUtils.decode(result.getStderr()
+ .toByteArray(MAX_EXCEPTION_TEXT_SIZE))));
+ }
+ return result.getStdout().openInputStream();
+ }
+ return in;
+ }
+
+ private InputStream handleAutoCRLF(InputStream in) {
+ AutoCRLF autoCRLF = getOptions().getAutoCRLF();
+ if (autoCRLF == AutoCRLF.TRUE || autoCRLF == AutoCRLF.INPUT) {
+ in = new EolCanonicalizingInputStream(in, true);
+ }
+ return in;
}
/**
@@ -508,6 +531,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
System.arraycopy(e.encodedName, 0, path, pathOffset, nameLen);
pathLen = pathOffset + nameLen;
canonLen = -1;
+ cleanFilterCommand = null;
}
/**
@@ -597,7 +621,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
* a relevant ignore rule file exists but cannot be read.
*/
protected boolean isEntryIgnored(final int pLen) throws IOException {
- return isEntryIgnored(pLen, false);
+ return isEntryIgnored(pLen, mode, false);
}
/**
@@ -606,13 +630,16 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
*
* @param pLen
* the length of the path in the path buffer.
+ * @param fileMode
+ * the original iterator file mode
* @param negatePrevious
* true if the previous matching iterator rule was negation
* @return true if the entry is ignored by an ignore rule.
* @throws IOException
* a relevant ignore rule file exists but cannot be read.
*/
- private boolean isEntryIgnored(final int pLen, boolean negatePrevious)
+ private boolean isEntryIgnored(final int pLen, int fileMode,
+ boolean negatePrevious)
throws IOException {
IgnoreNode rules = getIgnoreNode();
if (rules != null) {
@@ -624,7 +651,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
if (0 < pOff)
pOff--;
String p = TreeWalk.pathOf(path, pOff, pLen);
- switch (rules.isIgnored(p, FileMode.TREE.equals(mode),
+ switch (rules.isIgnored(p, FileMode.TREE.equals(fileMode),
negatePrevious)) {
case IGNORED:
return true;
@@ -639,7 +666,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
}
}
if (parent instanceof WorkingTreeIterator)
- return ((WorkingTreeIterator) parent).isEntryIgnored(pLen,
+ return ((WorkingTreeIterator) parent).isEntryIgnored(pLen, fileMode,
negatePrevious);
return false;
}
@@ -665,67 +692,14 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
return attributesNode;
}
- /**
- * Retrieves the {@link AttributesNode} that holds the information located
- * in $GIT_DIR/info/attributes file.
- *
- * @return the {@link AttributesNode} that holds the information located in
- * $GIT_DIR/info/attributes file.
- * @throws IOException
- * if an error is raised while parsing the attributes file
- * @since 3.7
- */
- public AttributesNode getInfoAttributesNode() throws IOException {
- if (infoAttributeNode instanceof InfoAttributesNode)
- infoAttributeNode = ((InfoAttributesNode) infoAttributeNode).load();
- return infoAttributeNode;
- }
-
- /**
- * Retrieves the {@link AttributesNode} that holds the information located
- * in system-wide file.
- *
- * @return the {@link AttributesNode} that holds the information located in
- * system-wide file.
- * @throws IOException
- * IOException if an error is raised while parsing the
- * attributes file
- * @see CoreConfig#getAttributesFile()
- * @since 3.7
- */
- public AttributesNode getGlobalAttributesNode() throws IOException {
- if (globalAttributeNode instanceof GlobalAttributesNode)
- globalAttributeNode = ((GlobalAttributesNode) globalAttributeNode)
- .load();
- return globalAttributeNode;
- }
-
private static final Comparator<Entry> ENTRY_CMP = new Comparator<Entry>() {
- public int compare(final Entry o1, final Entry o2) {
- final byte[] a = o1.encodedName;
- final byte[] b = o2.encodedName;
- final int aLen = o1.encodedNameLen;
- final int bLen = o2.encodedNameLen;
- int cPos;
-
- for (cPos = 0; cPos < aLen && cPos < bLen; cPos++) {
- final int cmp = (a[cPos] & 0xff) - (b[cPos] & 0xff);
- if (cmp != 0)
- return cmp;
- }
-
- if (cPos < aLen)
- return (a[cPos] & 0xff) - lastPathChar(o2);
- if (cPos < bLen)
- return lastPathChar(o1) - (b[cPos] & 0xff);
- return lastPathChar(o1) - lastPathChar(o2);
+ public int compare(Entry a, Entry b) {
+ return Paths.compare(
+ a.encodedName, 0, a.encodedNameLen, a.getMode().getBits(),
+ b.encodedName, 0, b.encodedNameLen, b.getMode().getBits());
}
};
- static int lastPathChar(final Entry e) {
- return e.getMode() == FileMode.TREE ? '/' : '\0';
- }
-
/**
* Constructor helper.
*
@@ -899,32 +873,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
* @param forceContentCheck
* True if the actual file content should be checked if
* modification time differs.
- * @return true if content is most likely different.
- * @deprecated Use {@link #isModified(DirCacheEntry, boolean, ObjectReader)}
- */
- @Deprecated
- public boolean isModified(DirCacheEntry entry, boolean forceContentCheck) {
- try {
- return isModified(entry, forceContentCheck,
- repository.newObjectReader());
- } catch (IOException e) {
- throw new JGitInternalException(e.getMessage(), e);
- }
- }
-
- /**
- * Checks whether this entry differs from a given entry from the
- * {@link DirCache}.
- *
- * File status information is used and if status is same we consider the
- * file identical to the state in the working directory. Native git uses
- * more stat fields than we have accessible in Java.
- *
- * @param entry
- * the entry from the dircache we want to compare against
- * @param forceContentCheck
- * True if the actual file content should be checked if
- * modification time differs.
* @param reader
* access to repository objects if necessary. Should not be null.
* @return true if content is most likely different.
@@ -1320,68 +1268,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
}
}
- /**
- * Attributes node loaded from global system-wide file.
- */
- private static class GlobalAttributesNode extends AttributesNode {
- final Repository repository;
-
- GlobalAttributesNode(Repository repository) {
- this.repository = repository;
- }
-
- AttributesNode load() throws IOException {
- AttributesNode r = new AttributesNode();
-
- FS fs = repository.getFS();
- String path = repository.getConfig().get(CoreConfig.KEY)
- .getAttributesFile();
- if (path != null) {
- File attributesFile;
- if (path.startsWith("~/")) //$NON-NLS-1$
- attributesFile = fs.resolve(fs.userHome(),
- path.substring(2));
- else
- attributesFile = fs.resolve(null, path);
- loadRulesFromFile(r, attributesFile);
- }
- return r.getRules().isEmpty() ? null : r;
- }
- }
-
- /** Magic type indicating there may be rules for the top level. */
- private static class InfoAttributesNode extends AttributesNode {
- final Repository repository;
-
- InfoAttributesNode(Repository repository) {
- this.repository = repository;
- }
-
- AttributesNode load() throws IOException {
- AttributesNode r = new AttributesNode();
-
- FS fs = repository.getFS();
-
- File attributes = fs.resolve(repository.getDirectory(),
- "info/attributes"); //$NON-NLS-1$
- loadRulesFromFile(r, attributes);
-
- return r.getRules().isEmpty() ? null : r;
- }
-
- }
-
- private static void loadRulesFromFile(AttributesNode r, File attrs)
- throws FileNotFoundException, IOException {
- if (attrs.exists()) {
- FileInputStream in = new FileInputStream(attrs);
- try {
- r.parse(in);
- } finally {
- in.close();
- }
- }
- }
private static final class IteratorState {
/** Options used to process the working tree. */
@@ -1414,4 +1300,18 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
}
}
}
+
+ /**
+ * @return the clean filter command for the current entry or
+ * <code>null</code> if no such command is defined
+ * @throws IOException
+ * @since 4.2
+ */
+ public String getCleanFilterCommand() throws IOException {
+ if (cleanFilterCommand == null && state.walk != null) {
+ cleanFilterCommand = state.walk
+ .getFilterCommand(Constants.ATTR_FILTER_TYPE_CLEAN);
+ }
+ return cleanFilterCommand;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/IndexDiffFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/IndexDiffFilter.java
index 5aab24c..42725bc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/IndexDiffFilter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/IndexDiffFilter.java
@@ -199,7 +199,7 @@ public class IndexDiffFilter extends TreeFilter {
// If i is cnt then the path does not appear in any other tree,
// and this working tree entry can be safely ignored.
- return i == cnt ? false : true;
+ return i != cnt;
} else {
// In working tree and not ignored, and not in DirCache.
return true;
@@ -224,7 +224,8 @@ public class IndexDiffFilter extends TreeFilter {
// Only one chance left to detect a diff: between index and working
// tree. Make use of the WorkingTreeIterator#isModified() method to
// avoid computing SHA1 on filesystem content if not really needed.
- return wi.isModified(di.getDirCacheEntry(), true, tw.getObjectReader());
+ return wi.isModified(di == null ? null : di.getDirCacheEntry(), true,
+ tw.getObjectReader());
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java
index bdfde0b..7601956 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java
@@ -245,9 +245,9 @@ public class PathFilterGroup {
int hash = hasher.nextHash();
if (fullpaths.contains(rp, hasher.length(), hash))
return true;
- if (!hasher.hasNext())
- if (prefixes.contains(rp, hasher.length(), hash))
- return true;
+ if (!hasher.hasNext() && walker.isSubtree()
+ && prefixes.contains(rp, hasher.length(), hash))
+ return true;
}
final int cmp = walker.isPathPrefix(max, max.length);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/BlockList.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/BlockList.java
index 0454e7e..9d0ad73 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/BlockList.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/BlockList.java
@@ -74,9 +74,9 @@ public class BlockList<T> extends AbstractList<T> {
private static final int BLOCK_MASK = BLOCK_SIZE - 1;
- private T[][] directory;
+ T[][] directory;
- private int size;
+ int size;
private int tailDirIdx;
@@ -282,11 +282,11 @@ public class BlockList<T> extends AbstractList<T> {
return new MyIterator();
}
- private static final int toDirectoryIndex(int index) {
+ static final int toDirectoryIndex(int index) {
return index >>> BLOCK_BITS;
}
- private static final int toBlockIndex(int index) {
+ static final int toBlockIndex(int index) {
return index & BLOCK_MASK;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java
index 35850dc..e14096e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java
@@ -42,7 +42,6 @@
*/
package org.eclipse.jgit.util;
-import java.io.IOException;
import java.util.regex.Pattern;
import org.eclipse.jgit.lib.Constants;
@@ -90,12 +89,10 @@ public class ChangeIdUtil {
* The commit message
* @return the change id SHA1 string (without the 'I') or null if the
* message is not complete enough
- * @throws IOException
*/
public static ObjectId computeChangeId(final ObjectId treeId,
final ObjectId firstParentId, final PersonIdent author,
- final PersonIdent committer, final String message)
- throws IOException {
+ final PersonIdent committer, final String message) {
String cleanMessage = clean(message);
if (cleanMessage.length() == 0)
return null;
@@ -115,8 +112,9 @@ public class ChangeIdUtil {
b.append(committer.toExternalString());
b.append("\n\n"); //$NON-NLS-1$
b.append(cleanMessage);
- return new ObjectInserter.Formatter().idFor(Constants.OBJ_COMMIT, //
- b.toString().getBytes(Constants.CHARACTER_ENCODING));
+ try (ObjectInserter f = new ObjectInserter.Formatter()) {
+ return f.idFor(Constants.OBJ_COMMIT, Constants.encode(b.toString()));
+ }
}
private static final Pattern issuePattern = Pattern
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
index 875e12f..253acbb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -44,27 +44,29 @@
package org.eclipse.jgit.util;
import java.io.BufferedReader;
-import java.io.BufferedWriter;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
-import java.io.OutputStreamWriter;
import java.io.PrintStream;
-import java.io.PrintWriter;
+import java.nio.charset.Charset;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.text.MessageFormat;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.errors.JGitInternalException;
-import org.eclipse.jgit.errors.SymlinksNotSupportedException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
@@ -102,10 +104,56 @@ public abstract class FS {
return new FS_Win32_Cygwin();
else
return new FS_Win32();
- } else if (FS_POSIX_Java6.hasExecute())
- return new FS_POSIX_Java6();
- else
- return new FS_POSIX_Java5();
+ } else {
+ return new FS_POSIX();
+ }
+ }
+ }
+
+ /**
+ * Result of an executed process. The caller is responsible to close the
+ * contained {@link TemporaryBuffer}s
+ *
+ * @since 4.2
+ */
+ public static class ExecutionResult {
+ private TemporaryBuffer stdout;
+
+ private TemporaryBuffer stderr;
+
+ private int rc;
+
+ /**
+ * @param stdout
+ * @param stderr
+ * @param rc
+ */
+ public ExecutionResult(TemporaryBuffer stdout, TemporaryBuffer stderr,
+ int rc) {
+ this.stdout = stdout;
+ this.stderr = stderr;
+ this.rc = rc;
+ }
+
+ /**
+ * @return buffered standard output stream
+ */
+ public TemporaryBuffer getStdout() {
+ return stdout;
+ }
+
+ /**
+ * @return buffered standard error stream
+ */
+ public TemporaryBuffer getStderr() {
+ return stderr;
+ }
+
+ /**
+ * @return the return code of the process
+ */
+ public int getRc() {
+ return rc;
}
}
@@ -148,29 +196,14 @@ public abstract class FS {
*/
public static FS detect(Boolean cygwinUsed) {
if (factory == null) {
- try {
- Class<?> activatorClass = Class
- .forName("org.eclipse.jgit.util.Java7FSFactory"); //$NON-NLS-1$
- // found Java7
- factory = (FSFactory) activatorClass.newInstance();
- } catch (ClassNotFoundException e) {
- // Java7 module not found
- // Silently ignore failure to find Java7 FS factory
- factory = new FS.FSFactory();
- } catch (UnsupportedClassVersionError e) {
- factory = new FS.FSFactory();
- } catch (InstantiationException e) {
- factory = new FS.FSFactory();
- } catch (IllegalAccessException e) {
- factory = new FS.FSFactory();
- }
+ factory = new FS.FSFactory();
}
return factory.detect(cygwinUsed);
}
private volatile Holder<File> userHome;
- private volatile Holder<File> gitPrefix;
+ private volatile Holder<File> gitSystemConfig;
/**
* Constructs a file system abstraction.
@@ -187,7 +220,7 @@ public abstract class FS {
*/
protected FS(FS src) {
userHome = src.userHome;
- gitPrefix = src.gitPrefix;
+ gitSystemConfig = src.gitSystemConfig;
}
/** @return a new instance of the same type of FS. */
@@ -261,7 +294,7 @@ public abstract class FS {
* @since 3.0
*/
public long lastModified(File f) throws IOException {
- return f.lastModified();
+ return FileUtils.lastModified(f);
}
/**
@@ -274,7 +307,7 @@ public abstract class FS {
* @since 3.0
*/
public void setLastModified(File f, long time) throws IOException {
- f.setLastModified(time);
+ FileUtils.setLastModified(f, time);
}
/**
@@ -287,7 +320,7 @@ public abstract class FS {
* @since 3.0
*/
public long length(File path) throws IOException {
- return path.length();
+ return FileUtils.getLength(path);
}
/**
@@ -299,9 +332,7 @@ public abstract class FS {
* @since 3.3
*/
public void delete(File f) throws IOException {
- if (!f.delete())
- throw new IOException(MessageFormat.format(
- JGitText.get().deleteFileFailed, f.getAbsolutePath()));
+ FileUtils.delete(f);
}
/**
@@ -419,75 +450,69 @@ public abstract class FS {
* @param command
* as component array
* @param encoding
- * @return the one-line output of the command
+ * to be used to parse the command's output
+ * @return the one-line output of the command or {@code null} if there is
+ * none
*/
+ @Nullable
protected static String readPipe(File dir, String[] command, String encoding) {
+ return readPipe(dir, command, encoding, null);
+ }
+
+ /**
+ * Execute a command and return a single line of output as a String
+ *
+ * @param dir
+ * Working directory for the command
+ * @param command
+ * as component array
+ * @param encoding
+ * to be used to parse the command's output
+ * @param env
+ * Map of environment variables to be merged with those of the
+ * current process
+ * @return the one-line output of the command or {@code null} if there is
+ * none
+ * @since 4.0
+ */
+ @Nullable
+ protected static String readPipe(File dir, String[] command, String encoding, Map<String, String> env) {
final boolean debug = LOG.isDebugEnabled();
try {
if (debug) {
LOG.debug("readpipe " + Arrays.asList(command) + "," //$NON-NLS-1$ //$NON-NLS-2$
+ dir);
}
- final Process p = Runtime.getRuntime().exec(command, null, dir);
- final BufferedReader lineRead = new BufferedReader(
- new InputStreamReader(p.getInputStream(), encoding));
+ ProcessBuilder pb = new ProcessBuilder(command);
+ pb.directory(dir);
+ if (env != null) {
+ pb.environment().putAll(env);
+ }
+ Process p = pb.start();
p.getOutputStream().close();
- final AtomicBoolean gooblerFail = new AtomicBoolean(false);
- Thread gobbler = new Thread() {
- public void run() {
- InputStream is = p.getErrorStream();
- try {
- int ch;
- if (debug)
- while ((ch = is.read()) != -1)
- System.err.print((char) ch);
- else
- while (is.read() != -1) {
- // ignore
- }
- } catch (IOException e) {
- // Just print on stderr for debugging
- if (debug)
- e.printStackTrace(System.err);
- gooblerFail.set(true);
- }
- try {
- is.close();
- } catch (IOException e) {
- // Just print on stderr for debugging
- if (debug) {
- LOG.debug("Caught exception in gobbler thread", e); //$NON-NLS-1$
- }
- gooblerFail.set(true);
- }
- }
- };
+ GobblerThread gobbler = new GobblerThread(p, command, dir);
gobbler.start();
String r = null;
- try {
+ try (BufferedReader lineRead = new BufferedReader(
+ new InputStreamReader(p.getInputStream(), encoding))) {
r = lineRead.readLine();
if (debug) {
LOG.debug("readpipe may return '" + r + "'"); //$NON-NLS-1$ //$NON-NLS-2$
- LOG.debug("(ignoring remaing output:"); //$NON-NLS-1$
- }
- String l;
- while ((l = lineRead.readLine()) != null) {
- if (debug) {
+ LOG.debug("remaining output:\n"); //$NON-NLS-1$
+ String l;
+ while ((l = lineRead.readLine()) != null) {
LOG.debug(l);
}
}
- } finally {
- p.getErrorStream().close();
- lineRead.close();
}
for (;;) {
try {
int rc = p.waitFor();
gobbler.join();
- if (rc == 0 && r != null && r.length() > 0
- && !gooblerFail.get())
+ if (rc == 0 && !gobbler.fail.get()) {
return r;
+ }
if (debug) {
LOG.debug("readpipe rc=" + rc); //$NON-NLS-1$
}
@@ -505,37 +530,128 @@ public abstract class FS {
return null;
}
- /** @return the $prefix directory C Git would use. */
- public File gitPrefix() {
- Holder<File> p = gitPrefix;
- if (p == null) {
- String overrideGitPrefix = SystemReader.getInstance().getProperty(
- "jgit.gitprefix"); //$NON-NLS-1$
- if (overrideGitPrefix != null)
- p = new Holder<File>(new File(overrideGitPrefix));
- else
- p = new Holder<File>(discoverGitPrefix());
- gitPrefix = p;
+ private static class GobblerThread extends Thread {
+ private final Process p;
+ private final String desc;
+ private final String dir;
+ final AtomicBoolean fail = new AtomicBoolean();
+
+ GobblerThread(Process p, String[] command, File dir) {
+ this.p = p;
+ this.desc = Arrays.toString(command);
+ this.dir = Objects.toString(dir);
}
- return p.value;
+
+ public void run() {
+ StringBuilder err = new StringBuilder();
+ try (InputStream is = p.getErrorStream()) {
+ int ch;
+ while ((ch = is.read()) != -1) {
+ err.append((char) ch);
+ }
+ } catch (IOException e) {
+ if (p.exitValue() != 0) {
+ logError(e);
+ fail.set(true);
+ } else {
+ // ignore. git terminated faster and stream was just closed
+ }
+ } finally {
+ if (err.length() > 0) {
+ LOG.error(err.toString());
+ }
+ }
+ }
+
+ private void logError(Throwable t) {
+ String msg = MessageFormat.format(
+ JGitText.get().exceptionCaughtDuringExcecutionOfCommand, desc, dir);
+ LOG.error(msg, t);
+ }
+ }
+
+ /**
+ * @return the path to the Git executable or {@code null} if it cannot be
+ * determined.
+ * @since 4.0
+ */
+ protected abstract File discoverGitExe();
+
+ /**
+ * @return the path to the system-wide Git configuration file or
+ * {@code null} if it cannot be determined.
+ * @since 4.0
+ */
+ protected File discoverGitSystemConfig() {
+ File gitExe = discoverGitExe();
+ if (gitExe == null) {
+ return null;
+ }
+
+ // Bug 480782: Check if the discovered git executable is JGit CLI
+ String v = readPipe(gitExe.getParentFile(),
+ new String[] { "git", "--version" }, //$NON-NLS-1$ //$NON-NLS-2$
+ Charset.defaultCharset().name());
+ if (v != null && v.startsWith("jgit")) { //$NON-NLS-1$
+ return null;
+ }
+
+ // Trick Git into printing the path to the config file by using "echo"
+ // as the editor.
+ Map<String, String> env = new HashMap<>();
+ env.put("GIT_EDITOR", "echo"); //$NON-NLS-1$ //$NON-NLS-2$
+
+ String w = readPipe(gitExe.getParentFile(),
+ new String[] { "git", "config", "--system", "--edit" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ Charset.defaultCharset().name(), env);
+ if (StringUtils.isEmptyOrNull(w)) {
+ return null;
+ }
+
+ return new File(w);
}
- /** @return the $prefix directory C Git would use. */
- protected abstract File discoverGitPrefix();
+ /**
+ * @return the currently used path to the system-wide Git configuration
+ * file or {@code null} if none has been set.
+ * @since 4.0
+ */
+ public File getGitSystemConfig() {
+ if (gitSystemConfig == null) {
+ gitSystemConfig = new Holder<File>(discoverGitSystemConfig());
+ }
+ return gitSystemConfig.value;
+ }
/**
- * Set the $prefix directory C Git uses.
+ * Set the path to the system-wide Git configuration file to use.
*
- * @param path
- * the directory. Null if C Git is not installed.
+ * @param configFile
+ * the path to the config file.
* @return {@code this}
+ * @since 4.0
*/
- public FS setGitPrefix(File path) {
- gitPrefix = new Holder<File>(path);
+ public FS setGitSystemConfig(File configFile) {
+ gitSystemConfig = new Holder<File>(configFile);
return this;
}
/**
+ * @param grandchild
+ * @return the parent directory of this file's parent directory or
+ * {@code null} in case there's no grandparent directory
+ * @since 4.0
+ */
+ protected static File resolveGrandparentFile(File grandchild) {
+ if (grandchild != null) {
+ File parent = grandchild.getParentFile();
+ if (parent != null)
+ return parent.getParentFile();
+ }
+ return null;
+ }
+
+ /**
* Check if a file is a symbolic link and read it
*
* @param path
@@ -544,8 +660,7 @@ public abstract class FS {
* @since 3.0
*/
public String readSymLink(File path) throws IOException {
- throw new SymlinksNotSupportedException(
- JGitText.get().errorSymlinksNotSupported);
+ return FileUtils.readSymLink(path);
}
/**
@@ -555,7 +670,7 @@ public abstract class FS {
* @since 3.0
*/
public boolean isSymLink(File path) throws IOException {
- return false;
+ return FileUtils.isSymlink(path);
}
/**
@@ -567,7 +682,7 @@ public abstract class FS {
* @since 3.0
*/
public boolean exists(File path) {
- return path.exists();
+ return FileUtils.exists(path);
}
/**
@@ -579,7 +694,7 @@ public abstract class FS {
* @since 3.0
*/
public boolean isDirectory(File path) {
- return path.isDirectory();
+ return FileUtils.isDirectory(path);
}
/**
@@ -591,7 +706,7 @@ public abstract class FS {
* @since 3.0
*/
public boolean isFile(File path) {
- return path.isFile();
+ return FileUtils.isFile(path);
}
/**
@@ -602,7 +717,7 @@ public abstract class FS {
* @since 3.0
*/
public boolean isHidden(File path) throws IOException {
- return path.isHidden();
+ return FileUtils.isHidden(path);
}
/**
@@ -614,9 +729,7 @@ public abstract class FS {
* @since 3.0
*/
public void setHidden(File path, boolean hidden) throws IOException {
- if (!path.getName().startsWith(".")) //$NON-NLS-1$
- throw new IllegalArgumentException(
- "Hiding only allowed for names that start with a period");
+ FileUtils.setHidden(path, hidden);
}
/**
@@ -628,8 +741,7 @@ public abstract class FS {
* @since 3.0
*/
public void createSymLink(File path, String target) throws IOException {
- throw new SymlinksNotSupportedException(
- JGitText.get().errorSymlinksNotSupported);
+ FileUtils.createSymLink(path, target);
}
/**
@@ -660,8 +772,8 @@ public abstract class FS {
*
* @param repository
* The repository for which a hook should be run.
- * @param hook
- * The hook to be executed.
+ * @param hookName
+ * The name of the hook to be executed.
* @param args
* Arguments to pass to this hook. Cannot be <code>null</code>,
* but can be an empty array.
@@ -669,11 +781,12 @@ public abstract class FS {
* @throws JGitInternalException
* if we fail to run the hook somehow. Causes may include an
* interrupted process or I/O errors.
- * @since 3.7
+ * @since 4.0
*/
- public ProcessResult runIfPresent(Repository repository, final Hook hook,
+ public ProcessResult runHookIfPresent(Repository repository,
+ final String hookName,
String[] args) throws JGitInternalException {
- return runIfPresent(repository, hook, args, System.out, System.err,
+ return runHookIfPresent(repository, hookName, args, System.out, System.err,
null);
}
@@ -683,8 +796,8 @@ public abstract class FS {
*
* @param repository
* The repository for which a hook should be run.
- * @param hook
- * The hook to be executed.
+ * @param hookName
+ * The name of the hook to be executed.
* @param args
* Arguments to pass to this hook. Cannot be <code>null</code>,
* but can be an empty array.
@@ -703,9 +816,10 @@ public abstract class FS {
* @throws JGitInternalException
* if we fail to run the hook somehow. Causes may include an
* interrupted process or I/O errors.
- * @since 3.7
+ * @since 4.0
*/
- public ProcessResult runIfPresent(Repository repository, final Hook hook,
+ public ProcessResult runHookIfPresent(Repository repository,
+ final String hookName,
String[] args, PrintStream outRedirect, PrintStream errRedirect,
String stdinArgs) throws JGitInternalException {
return new ProcessResult(Status.NOT_SUPPORTED);
@@ -713,13 +827,13 @@ public abstract class FS {
/**
* See
- * {@link #runIfPresent(Repository, Hook, String[], PrintStream, PrintStream, String)}
+ * {@link #runHookIfPresent(Repository, String, String[], PrintStream, PrintStream, String)}
* . Should only be called by FS supporting shell scripts execution.
*
* @param repository
* The repository for which a hook should be run.
- * @param hook
- * The hook to be executed.
+ * @param hookName
+ * The name of the hook to be executed.
* @param args
* Arguments to pass to this hook. Cannot be <code>null</code>,
* but can be an empty array.
@@ -738,13 +852,13 @@ public abstract class FS {
* @throws JGitInternalException
* if we fail to run the hook somehow. Causes may include an
* interrupted process or I/O errors.
- * @since 3.7
+ * @since 4.0
*/
- protected ProcessResult internalRunIfPresent(Repository repository,
- final Hook hook, String[] args, PrintStream outRedirect,
+ protected ProcessResult internalRunHookIfPresent(Repository repository,
+ final String hookName, String[] args, PrintStream outRedirect,
PrintStream errRedirect, String stdinArgs)
throws JGitInternalException {
- final File hookFile = findHook(repository, hook);
+ final File hookFile = findHook(repository, hookName);
if (hookFile == null)
return new ProcessResult(Status.NOT_PRESENT);
@@ -764,11 +878,11 @@ public abstract class FS {
} catch (IOException e) {
throw new JGitInternalException(MessageFormat.format(
JGitText.get().exceptionCaughtDuringExecutionOfHook,
- hook.getName()), e);
+ hookName), e);
} catch (InterruptedException e) {
throw new JGitInternalException(MessageFormat.format(
JGitText.get().exceptionHookExecutionInterrupted,
- hook.getName()), e);
+ hookName), e);
}
}
@@ -778,15 +892,18 @@ public abstract class FS {
*
* @param repository
* The repository within which to find a hook.
- * @param hook
- * The hook we're trying to find.
+ * @param hookName
+ * The name of the hook we're trying to find.
* @return The {@link File} containing this particular hook if it exists in
* the given repository, <code>null</code> otherwise.
- * @since 3.7
+ * @since 4.0
*/
- public File findHook(Repository repository, final Hook hook) {
- final File hookFile = new File(new File(repository.getDirectory(),
- Constants.HOOKS), hook.getName());
+ public File findHook(Repository repository, final String hookName) {
+ File gitDir = repository.getDirectory();
+ if (gitDir == null)
+ return null;
+ final File hookFile = new File(new File(gitDir,
+ Constants.HOOKS), hookName);
return hookFile.isFile() ? hookFile : null;
}
@@ -794,52 +911,86 @@ public abstract class FS {
* Runs the given process until termination, clearing its stdout and stderr
* streams on-the-fly.
*
- * @param hookProcessBuilder
- * The process builder configured for this hook.
+ * @param processBuilder
+ * The process builder configured for this process.
* @param outRedirect
- * A print stream on which to redirect the hook's stdout. Can be
- * <code>null</code>, in which case the hook's standard output
- * will be lost.
+ * A OutputStream on which to redirect the processes stdout. Can
+ * be <code>null</code>, in which case the processes standard
+ * output will be lost.
* @param errRedirect
- * A print stream on which to redirect the hook's stderr. Can be
- * <code>null</code>, in which case the hook's standard error
- * will be lost.
+ * A OutputStream on which to redirect the processes stderr. Can
+ * be <code>null</code>, in which case the processes standard
+ * error will be lost.
* @param stdinArgs
* A string to pass on to the standard input of the hook. Can be
* <code>null</code>.
- * @return the exit value of this hook.
+ * @return the exit value of this process.
* @throws IOException
- * if an I/O error occurs while executing this hook.
+ * if an I/O error occurs while executing this process.
* @throws InterruptedException
* if the current thread is interrupted while waiting for the
* process to end.
- * @since 3.7
+ * @since 4.2
*/
- protected int runProcess(ProcessBuilder hookProcessBuilder,
+ public int runProcess(ProcessBuilder processBuilder,
OutputStream outRedirect, OutputStream errRedirect, String stdinArgs)
throws IOException, InterruptedException {
+ InputStream in = (stdinArgs == null) ? null : new ByteArrayInputStream(
+ stdinArgs.getBytes(Constants.CHARACTER_ENCODING));
+ return runProcess(processBuilder, outRedirect, errRedirect, in);
+ }
+
+ /**
+ * Runs the given process until termination, clearing its stdout and stderr
+ * streams on-the-fly.
+ *
+ * @param processBuilder
+ * The process builder configured for this process.
+ * @param outRedirect
+ * An OutputStream on which to redirect the processes stdout. Can
+ * be <code>null</code>, in which case the processes standard
+ * output will be lost.
+ * @param errRedirect
+ * An OutputStream on which to redirect the processes stderr. Can
+ * be <code>null</code>, in which case the processes standard
+ * error will be lost.
+ * @param inRedirect
+ * An InputStream from which to redirect the processes stdin. Can
+ * be <code>null</code>, in which case the process doesn't get
+ * any data over stdin. It is assumed that the whole InputStream
+ * will be consumed by the process. The method will close the
+ * inputstream after all bytes are read.
+ * @return the return code of this process.
+ * @throws IOException
+ * if an I/O error occurs while executing this process.
+ * @throws InterruptedException
+ * if the current thread is interrupted while waiting for the
+ * process to end.
+ * @since 4.2
+ */
+ public int runProcess(ProcessBuilder processBuilder,
+ OutputStream outRedirect, OutputStream errRedirect,
+ InputStream inRedirect) throws IOException,
+ InterruptedException {
final ExecutorService executor = Executors.newFixedThreadPool(2);
Process process = null;
// We'll record the first I/O exception that occurs, but keep on trying
// to dispose of our open streams and file handles
IOException ioException = null;
try {
- process = hookProcessBuilder.start();
+ process = processBuilder.start();
final Callable<Void> errorGobbler = new StreamGobbler(
process.getErrorStream(), errRedirect);
final Callable<Void> outputGobbler = new StreamGobbler(
process.getInputStream(), outRedirect);
executor.submit(errorGobbler);
executor.submit(outputGobbler);
- if (stdinArgs != null) {
- final PrintWriter stdinWriter = new PrintWriter(
- process.getOutputStream());
- stdinWriter.print(stdinArgs);
- stdinWriter.flush();
- // We are done with this hook's input. Explicitly close its
- // stdin now to kick off any blocking read the hook might have.
- stdinWriter.close();
+ OutputStream outputStream = process.getOutputStream();
+ if (inRedirect != null) {
+ new StreamGobbler(inRedirect, outputStream)
+ .call();
}
+ outputStream.close();
return process.waitFor();
} catch (IOException e) {
ioException = e;
@@ -858,6 +1009,9 @@ public abstract class FS {
// A process doesn't clean its own resources even when destroyed
// Explicitly try and close all three streams, preserving the
// outer I/O exception if any.
+ if (inRedirect != null) {
+ inRedirect.close();
+ }
try {
process.getErrorStream().close();
} catch (IOException e) {
@@ -898,10 +1052,10 @@ public abstract class FS {
pool.shutdown(); // Disable new tasks from being submitted
try {
// Wait a while for existing tasks to terminate
- if (!pool.awaitTermination(5, TimeUnit.SECONDS)) {
+ if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
pool.shutdownNow(); // Cancel currently executing tasks
// Wait a while for tasks to respond to being canceled
- if (!pool.awaitTermination(5, TimeUnit.SECONDS))
+ if (!pool.awaitTermination(60, TimeUnit.SECONDS))
hasShutdown = false;
}
} catch (InterruptedException ie) {
@@ -915,7 +1069,7 @@ public abstract class FS {
}
/**
- * Initialize a ProcesssBuilder to run a command using the system shell.
+ * Initialize a ProcessBuilder to run a command using the system shell.
*
* @param cmd
* command to execute. This string should originate from the
@@ -928,6 +1082,31 @@ public abstract class FS {
*/
public abstract ProcessBuilder runInShell(String cmd, String[] args);
+ /**
+ * Execute a command defined by a {@link ProcessBuilder}.
+ *
+ * @param pb
+ * The command to be executed
+ * @param in
+ * The standard input stream passed to the process
+ * @return The result of the executed command
+ * @throws InterruptedException
+ * @throws IOException
+ * @since 4.2
+ */
+ public ExecutionResult execute(ProcessBuilder pb, InputStream in)
+ throws IOException, InterruptedException {
+ TemporaryBuffer stdout = new TemporaryBuffer.LocalFile(null);
+ TemporaryBuffer stderr = new TemporaryBuffer.Heap(1024, 1024 * 1024);
+ try {
+ int rc = runProcess(pb, stdout, stderr, in);
+ return new ExecutionResult(stdout, stderr, rc);
+ } finally {
+ stdout.close();
+ stderr.close();
+ }
+ }
+
private static class Holder<V> {
final V value;
@@ -986,28 +1165,28 @@ public abstract class FS {
return lastModifiedTime;
}
- private boolean isDirectory;
+ private final boolean isDirectory;
- private boolean isSymbolicLink;
+ private final boolean isSymbolicLink;
- private boolean isRegularFile;
+ private final boolean isRegularFile;
- private long creationTime;
+ private final long creationTime;
- private long lastModifiedTime;
+ private final long lastModifiedTime;
- private boolean isExecutable;
+ private final boolean isExecutable;
- private File file;
+ private final File file;
- private boolean exists;
+ private final boolean exists;
/**
* file length
*/
protected long length = -1;
- FS fs;
+ final FS fs;
Attributes(FS fs, File file, boolean exists, boolean isDirectory,
boolean isExecutable, boolean isSymbolicLink,
@@ -1026,14 +1205,14 @@ public abstract class FS {
}
/**
- * Constructor when there are issues with reading
+ * Constructor when there are issues with reading. All attributes except
+ * given will be set to the default values.
*
* @param fs
* @param path
*/
public Attributes(File path, FS fs) {
- this.file = path;
- this.fs = fs;
+ this(fs, path, false, false, false, false, false, 0L, 0L, 0L);
}
/**
@@ -1117,30 +1296,27 @@ public abstract class FS {
* </p>
*/
private static class StreamGobbler implements Callable<Void> {
- private final BufferedReader reader;
+ private InputStream in;
- private final BufferedWriter writer;
+ private OutputStream out;
public StreamGobbler(InputStream stream, OutputStream output) {
- this.reader = new BufferedReader(new InputStreamReader(stream));
- if (output == null)
- this.writer = null;
- else
- this.writer = new BufferedWriter(new OutputStreamWriter(output));
+ this.in = stream;
+ this.out = output;
}
public Void call() throws IOException {
boolean writeFailure = false;
-
- String line = null;
- while ((line = reader.readLine()) != null) {
- // Do not try to write again after a failure, but keep reading
- // as long as possible to prevent the input stream from choking.
- if (!writeFailure && writer != null) {
+ byte buffer[] = new byte[4096];
+ int readBytes;
+ while ((readBytes = in.read(buffer)) != -1) {
+ // Do not try to write again after a failure, but keep
+ // reading as long as possible to prevent the input stream
+ // from choking.
+ if (!writeFailure && out != null) {
try {
- writer.write(line);
- writer.newLine();
- writer.flush();
+ out.write(buffer, 0, readBytes);
+ out.flush();
} catch (IOException e) {
writeFailure = true;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
index ee29584..779b10e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
@@ -42,15 +42,22 @@
*/
package org.eclipse.jgit.util;
+import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
+import java.io.InputStreamReader;
import java.io.PrintStream;
import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.PosixFilePermission;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Set;
import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
/**
@@ -58,38 +65,12 @@ import org.eclipse.jgit.lib.Repository;
*
* @since 3.0
*/
-public abstract class FS_POSIX extends FS {
- @Override
- protected File discoverGitPrefix() {
- String path = SystemReader.getInstance().getenv("PATH"); //$NON-NLS-1$
- File gitExe = searchPath(path, "git"); //$NON-NLS-1$
- if (gitExe != null)
- return gitExe.getParentFile().getParentFile();
-
- if (SystemReader.getInstance().isMacOS()) {
- // On MacOSX, PATH is shorter when Eclipse is launched from the
- // Finder than from a terminal. Therefore try to launch bash as a
- // login shell and search using that.
- //
- String w = readPipe(userHome(), //
- new String[] { "bash", "--login", "-c", "which git" }, // //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
- Charset.defaultCharset().name());
- if (w == null || w.length() == 0)
- return null;
- File parentFile = new File(w).getParentFile();
- if (parentFile == null)
- return null;
- return parentFile.getParentFile();
- }
-
- return null;
- }
+public class FS_POSIX extends FS {
+ private static final int DEFAULT_UMASK = 0022;
+ private volatile int umask = -1;
- /**
- * Default constructor
- */
+ /** Default constructor. */
protected FS_POSIX() {
- super();
}
/**
@@ -100,6 +81,79 @@ public abstract class FS_POSIX extends FS {
*/
protected FS_POSIX(FS src) {
super(src);
+ if (src instanceof FS_POSIX) {
+ umask = ((FS_POSIX) src).umask;
+ }
+ }
+
+ @Override
+ public FS newInstance() {
+ return new FS_POSIX(this);
+ }
+
+ /**
+ * Set the umask, overriding any value observed from the shell.
+ *
+ * @param umask
+ * mask to apply when creating files.
+ * @since 4.0
+ */
+ public void setUmask(int umask) {
+ this.umask = umask;
+ }
+
+ private int umask() {
+ int u = umask;
+ if (u == -1) {
+ u = readUmask();
+ umask = u;
+ }
+ return u;
+ }
+
+ /** @return mask returned from running {@code umask} command in shell. */
+ private static int readUmask() {
+ try {
+ Process p = Runtime.getRuntime().exec(
+ new String[] { "sh", "-c", "umask" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ null, null);
+ try (BufferedReader lineRead = new BufferedReader(
+ new InputStreamReader(p.getInputStream(), Charset
+ .defaultCharset().name()))) {
+ if (p.waitFor() == 0) {
+ String s = lineRead.readLine();
+ if (s.matches("0?\\d{3}")) { //$NON-NLS-1$
+ return Integer.parseInt(s, 8);
+ }
+ }
+ return DEFAULT_UMASK;
+ }
+ } catch (Exception e) {
+ return DEFAULT_UMASK;
+ }
+ }
+
+ @Override
+ protected File discoverGitExe() {
+ String path = SystemReader.getInstance().getenv("PATH"); //$NON-NLS-1$
+ File gitExe = searchPath(path, "git"); //$NON-NLS-1$
+
+ if (gitExe == null) {
+ if (SystemReader.getInstance().isMacOS()) {
+ if (searchPath(path, "bash") != null) { //$NON-NLS-1$
+ // On MacOSX, PATH is shorter when Eclipse is launched from the
+ // Finder than from a terminal. Therefore try to launch bash as a
+ // login shell and search using that.
+ String w = readPipe(userHome(),
+ new String[]{"bash", "--login", "-c", "which git"}, // //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ Charset.defaultCharset().name());
+ if (!StringUtils.isEmptyOrNull(w))
+ gitExe = new File(w);
+ }
+ }
+ }
+
+ return gitExe;
}
@Override
@@ -108,8 +162,53 @@ public abstract class FS_POSIX extends FS {
}
@Override
- public void setHidden(File path, boolean hidden) throws IOException {
- // Do nothing
+ public boolean supportsExecute() {
+ return true;
+ }
+
+ @Override
+ public boolean canExecute(File f) {
+ return FileUtils.canExecute(f);
+ }
+
+ @Override
+ public boolean setExecute(File f, boolean canExecute) {
+ if (!isFile(f))
+ return false;
+ if (!canExecute)
+ return f.setExecutable(false);
+
+ try {
+ Path path = f.toPath();
+ Set<PosixFilePermission> pset = Files.getPosixFilePermissions(path);
+
+ // owner (user) is always allowed to execute.
+ pset.add(PosixFilePermission.OWNER_EXECUTE);
+
+ int mask = umask();
+ apply(pset, mask, PosixFilePermission.GROUP_EXECUTE, 1 << 3);
+ apply(pset, mask, PosixFilePermission.OTHERS_EXECUTE, 1);
+ Files.setPosixFilePermissions(path, pset);
+ return true;
+ } catch (IOException e) {
+ // The interface doesn't allow to throw IOException
+ final boolean debug = Boolean.parseBoolean(SystemReader
+ .getInstance().getProperty("jgit.fs.debug")); //$NON-NLS-1$
+ if (debug)
+ System.err.println(e);
+ return false;
+ }
+ }
+
+ private static void apply(Set<PosixFilePermission> set,
+ int umask, PosixFilePermission perm, int test) {
+ if ((umask & test) == 0) {
+ // If bit is clear in umask, permission is allowed.
+ set.add(perm);
+ } else {
+ // If bit is set in umask, permission is denied.
+ set.remove(perm);
+ }
}
@Override
@@ -126,13 +225,68 @@ public abstract class FS_POSIX extends FS {
}
/**
- * @since 3.7
+ * @since 4.0
*/
@Override
- public ProcessResult runIfPresent(Repository repository, Hook hook,
+ public ProcessResult runHookIfPresent(Repository repository, String hookName,
String[] args, PrintStream outRedirect, PrintStream errRedirect,
String stdinArgs) throws JGitInternalException {
- return internalRunIfPresent(repository, hook, args, outRedirect,
+ return internalRunHookIfPresent(repository, hookName, args, outRedirect,
errRedirect, stdinArgs);
}
+
+ @Override
+ public boolean retryFailedLockFileCommit() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsSymlinks() {
+ return true;
+ }
+
+ @Override
+ public void setHidden(File path, boolean hidden) throws IOException {
+ // no action on POSIX
+ }
+
+ /**
+ * @since 3.3
+ */
+ @Override
+ public Attributes getAttributes(File path) {
+ return FileUtils.getFileAttributesPosix(this, path);
+ }
+
+ /**
+ * @since 3.3
+ */
+ @Override
+ public File normalize(File file) {
+ return FileUtils.normalize(file);
+ }
+
+ /**
+ * @since 3.3
+ */
+ @Override
+ public String normalize(String name) {
+ return FileUtils.normalize(name);
+ }
+
+ /**
+ * @since 3.7
+ */
+ @Override
+ public File findHook(Repository repository, String hookName) {
+ final File gitdir = repository.getDirectory();
+ if (gitdir == null) {
+ return null;
+ }
+ final Path hookPath = gitdir.toPath().resolve(Constants.HOOKS)
+ .resolve(hookName);
+ if (Files.isExecutable(hookPath))
+ return hookPath.toFile();
+ return null;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java6.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java6.java
deleted file mode 100644
index 47a7a68..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX_Java6.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2007, Robin Rosenberg <me at lathund.dewire.com>
- * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg at dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce at spearce.org>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package org.eclipse.jgit.util;
-
-import java.io.File;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-
-/**
- * FS implementation for POSIX systems using Java6
- *
- * @since 3.0
- */
-public class FS_POSIX_Java6 extends FS_POSIX {
- private static final Method canExecute;
-
- private static final Method setExecute;
-
- static {
- canExecute = needMethod(File.class, "canExecute"); //$NON-NLS-1$
- setExecute = needMethod(File.class, "setExecutable", Boolean.TYPE); //$NON-NLS-1$
- }
-
- /**
- * @return true if Java has the ability to set and get the executable flag
- * on files
- */
- public static boolean hasExecute() {
- return canExecute != null && setExecute != null;
- }
-
- private static Method needMethod(final Class<?> on, final String name,
- final Class<?>... args) {
- try {
- return on.getMethod(name, args);
- } catch (SecurityException e) {
- return null;
- } catch (NoSuchMethodException e) {
- return null;
- }
- }
-
- /**
- * Constructor
- */
- public FS_POSIX_Java6() {
- super();
- }
-
- /**
- * Constructor
- *
- * @param src
- * instance whose attributes to copy
- */
- public FS_POSIX_Java6(FS src) {
- super(src);
- }
-
- @Override
- public FS newInstance() {
- return new FS_POSIX_Java6(this);
- }
-
- public boolean supportsExecute() {
- return true;
- }
-
- public boolean canExecute(final File f) {
- try {
- final Object r = canExecute.invoke(f, (Object[]) null);
- return ((Boolean) r).booleanValue();
- } catch (IllegalArgumentException e) {
- throw new Error(e);
- } catch (IllegalAccessException e) {
- throw new Error(e);
- } catch (InvocationTargetException e) {
- throw new Error(e);
- }
- }
-
- public boolean setExecute(final File f, final boolean canExec) {
- try {
- final Object r;
- r = setExecute.invoke(f, new Object[] { Boolean.valueOf(canExec) });
- return ((Boolean) r).booleanValue();
- } catch (IllegalArgumentException e) {
- throw new Error(e);
- } catch (IllegalAccessException e) {
- throw new Error(e);
- } catch (InvocationTargetException e) {
- throw new Error(e);
- }
- }
-
- @Override
- public boolean retryFailedLockFileCommit() {
- return false;
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
index 5822dcf..defe14f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
@@ -45,6 +45,7 @@
package org.eclipse.jgit.util;
import java.io.File;
+import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
@@ -57,6 +58,9 @@ import java.util.List;
* @since 3.0
*/
public class FS_Win32 extends FS {
+
+ private volatile Boolean supportSymlinks;
+
/**
* Constructor
*/
@@ -101,34 +105,24 @@ public class FS_Win32 extends FS {
}
@Override
- protected File discoverGitPrefix() {
+ protected File discoverGitExe() {
String path = SystemReader.getInstance().getenv("PATH"); //$NON-NLS-1$
File gitExe = searchPath(path, "git.exe", "git.cmd"); //$NON-NLS-1$ //$NON-NLS-2$
- if (gitExe != null)
- return resolveGrandparentFile(gitExe);
-
- // This isn't likely to work, if bash is in $PATH, git should
- // also be in $PATH. But its worth trying.
- //
- String w = readPipe(userHome(), //
- new String[] { "bash", "--login", "-c", "which git" }, // //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
- Charset.defaultCharset().name());
- if (w != null) {
- // The path may be in cygwin/msys notation so resolve it right away
- gitExe = resolve(null, w);
- if (gitExe != null)
- return resolveGrandparentFile(gitExe);
- }
- return null;
- }
- private static File resolveGrandparentFile(File grandchild) {
- if (grandchild != null) {
- File parent = grandchild.getParentFile();
- if (parent != null)
- return parent.getParentFile();
+ if (gitExe == null) {
+ if (searchPath(path, "bash.exe") != null) { //$NON-NLS-1$
+ // This isn't likely to work, but its worth trying:
+ // If bash is in $PATH, git should also be in $PATH.
+ String w = readPipe(userHome(),
+ new String[]{"bash", "--login", "-c", "which git"}, // //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ Charset.defaultCharset().name());
+ if (!StringUtils.isEmptyOrNull(w))
+ // The path may be in cygwin/msys notation so resolve it right away
+ gitExe = resolve(null, w);
+ }
}
- return null;
+
+ return gitExe;
}
@Override
@@ -161,4 +155,40 @@ public class FS_Win32 extends FS {
proc.command(argv);
return proc;
}
+
+ @Override
+ public boolean supportsSymlinks() {
+ if (supportSymlinks == null)
+ detectSymlinkSupport();
+ return Boolean.TRUE.equals(supportSymlinks);
+ }
+
+ private void detectSymlinkSupport() {
+ File tempFile = null;
+ try {
+ tempFile = File.createTempFile("tempsymlinktarget", ""); //$NON-NLS-1$ //$NON-NLS-2$
+ File linkName = new File(tempFile.getParentFile(), "tempsymlink"); //$NON-NLS-1$
+ createSymLink(linkName, tempFile.getPath());
+ supportSymlinks = Boolean.TRUE;
+ linkName.delete();
+ } catch (IOException | UnsupportedOperationException
+ | InternalError e) {
+ supportSymlinks = Boolean.FALSE;
+ } finally {
+ if (tempFile != null)
+ try {
+ FileUtils.delete(tempFile);
+ } catch (IOException e) {
+ throw new RuntimeException(e); // panic
+ }
+ }
+ }
+
+ /**
+ * @since 3.3
+ */
+ @Override
+ public Attributes getAttributes(File path) {
+ return FileUtils.getFileAttributesBasic(this, path);
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
index d0abd33..ec581b3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
@@ -45,6 +45,8 @@ package org.eclipse.jgit.util;
import java.io.File;
import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
@@ -52,6 +54,7 @@ import java.util.Arrays;
import java.util.List;
import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
/**
@@ -149,13 +152,29 @@ public class FS_Win32_Cygwin extends FS_Win32 {
}
/**
- * @since 3.7
+ * @since 4.0
*/
@Override
- public ProcessResult runIfPresent(Repository repository, Hook hook,
+ public ProcessResult runHookIfPresent(Repository repository, String hookName,
String[] args, PrintStream outRedirect, PrintStream errRedirect,
String stdinArgs) throws JGitInternalException {
- return internalRunIfPresent(repository, hook, args, outRedirect,
+ return internalRunHookIfPresent(repository, hookName, args, outRedirect,
errRedirect, stdinArgs);
}
+
+ /**
+ * @since 3.7
+ */
+ @Override
+ public File findHook(Repository repository, String hookName) {
+ final File gitdir = repository.getDirectory();
+ if (gitdir == null) {
+ return null;
+ }
+ final Path hookPath = gitdir.toPath().resolve(Constants.HOOKS)
+ .resolve(hookName);
+ if (Files.isExecutable(hookPath))
+ return hookPath.toFile();
+ return null;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtil.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtil.java
new file mode 100644
index 0000000..b87b9a4
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtil.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2012, Robin Rosenberg <robin.rosenberg at dewire.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+
+import org.eclipse.jgit.util.FS.Attributes;
+
+/**
+ * File utilities using Java 7 NIO2
+ */
+ at Deprecated
+public class FileUtil {
+
+ /**
+ * @param path
+ * @return target path of the symlink
+ * @throws IOException
+ * @deprecated use {@link FileUtils#readSymLink(File)} instead
+ */
+ @Deprecated
+ public static String readSymlink(File path) throws IOException {
+ return FileUtils.readSymLink(path);
+ }
+
+ /**
+ * @param path
+ * path of the symlink to be created
+ * @param target
+ * target of the symlink to be created
+ * @throws IOException
+ * @deprecated use {@link FileUtils#createSymLink(File, String)} instead
+ */
+ @Deprecated
+ public static void createSymLink(File path, String target)
+ throws IOException {
+ FileUtils.createSymLink(path, target);
+ }
+
+ /**
+ * @param path
+ * @return {@code true} if the passed path is a symlink
+ * @deprecated Use {@link Files#isSymbolicLink(java.nio.file.Path)} instead
+ */
+ @Deprecated
+ public static boolean isSymlink(File path) {
+ return FileUtils.isSymlink(path);
+ }
+
+ /**
+ * @param path
+ * @return lastModified attribute for given path
+ * @throws IOException
+ * @deprecated Use
+ * {@link Files#getLastModifiedTime(java.nio.file.Path, java.nio.file.LinkOption...)}
+ * instead
+ */
+ @Deprecated
+ public static long lastModified(File path) throws IOException {
+ return FileUtils.lastModified(path);
+ }
+
+ /**
+ * @param path
+ * @param time
+ * @throws IOException
+ * @deprecated Use
+ * {@link Files#setLastModifiedTime(java.nio.file.Path, java.nio.file.attribute.FileTime)}
+ * instead
+ */
+ @Deprecated
+ public static void setLastModified(File path, long time) throws IOException {
+ FileUtils.setLastModified(path, time);
+ }
+
+ /**
+ * @param path
+ * @return {@code true} if the given path exists
+ * @deprecated Use
+ * {@link Files#exists(java.nio.file.Path, java.nio.file.LinkOption...)}
+ * instead
+ */
+ @Deprecated
+ public static boolean exists(File path) {
+ return FileUtils.exists(path);
+ }
+
+ /**
+ * @param path
+ * @return {@code true} if the given path is hidden
+ * @throws IOException
+ * @deprecated Use {@link Files#isHidden(java.nio.file.Path)} instead
+ */
+ @Deprecated
+ public static boolean isHidden(File path) throws IOException {
+ return FileUtils.isHidden(path);
+ }
+
+ /**
+ * @param path
+ * @param hidden
+ * @throws IOException
+ * @deprecated Use {@link FileUtils#setHidden(File,boolean)} instead
+ */
+ @Deprecated
+ public static void setHidden(File path, boolean hidden) throws IOException {
+ FileUtils.setHidden(path, hidden);
+ }
+
+ /**
+ * @param path
+ * @return length of the given file
+ * @throws IOException
+ * @deprecated Use {@link FileUtils#getLength(File)} instead
+ */
+ @Deprecated
+ public static long getLength(File path) throws IOException {
+ return FileUtils.getLength(path);
+ }
+
+ /**
+ * @param path
+ * @return {@code true} if the given file a directory
+ * @deprecated Use
+ * {@link Files#isDirectory(java.nio.file.Path, java.nio.file.LinkOption...)}
+ * instead
+ */
+ @Deprecated
+ public static boolean isDirectory(File path) {
+ return FileUtils.isDirectory(path);
+ }
+
+ /**
+ * @param path
+ * @return {@code true} if the given file is a file
+ * @deprecated Use
+ * {@link Files#isRegularFile(java.nio.file.Path, java.nio.file.LinkOption...)}
+ * instead
+ */
+ @Deprecated
+ public static boolean isFile(File path) {
+ return FileUtils.isFile(path);
+ }
+
+ /**
+ * @param path
+ * @return {@code true} if the given file can be executed
+ * @deprecated Use {@link FileUtils#canExecute(File)} instead
+ */
+ @Deprecated
+ public static boolean canExecute(File path) {
+ return FileUtils.canExecute(path);
+ }
+
+ /**
+ * @param path
+ * @throws IOException
+ * @deprecated use {@link FileUtils#delete(File)}
+ */
+ @Deprecated
+ public static void delete(File path) throws IOException {
+ FileUtils.delete(path);
+ }
+
+ /**
+ * @param fs
+ * @param path
+ * @return file system attributes for the given file
+ * @deprecated Use {@link FileUtils#getFileAttributesPosix(FS,File)} instead
+ */
+ @Deprecated
+ public static Attributes getFileAttributesPosix(FS fs, File path) {
+ return FileUtils.getFileAttributesPosix(fs, path);
+ }
+
+ /**
+ * @param file
+ * @return on Mac: NFC normalized {@link File}, otherwise the passed file
+ * @deprecated Use {@link FileUtils#normalize(File)} instead
+ */
+ @Deprecated
+ public static File normalize(File file) {
+ return FileUtils.normalize(file);
+ }
+
+ /**
+ * @param name
+ * @return on Mac: NFC normalized form of given name
+ * @deprecated Use {@link FileUtils#normalize(String)} instead
+ */
+ @Deprecated
+ public static String normalize(String name) {
+ return FileUtils.normalize(name);
+ }
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
index 1e58245..aa101f7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
@@ -48,12 +48,28 @@ package org.eclipse.jgit.util;
import java.io.File;
import java.io.IOException;
import java.nio.channels.FileLock;
+import java.nio.file.AtomicMoveNotSupportedException;
+import java.nio.file.CopyOption;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.BasicFileAttributeView;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.PosixFileAttributeView;
+import java.nio.file.attribute.PosixFileAttributes;
+import java.nio.file.attribute.PosixFilePermission;
import java.text.MessageFormat;
+import java.text.Normalizer;
+import java.text.Normalizer.Form;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.util.FS.Attributes;
/**
* File Utilities
@@ -207,30 +223,68 @@ public class FileUtils {
*/
public static void rename(final File src, final File dst)
throws IOException {
+ rename(src, dst, StandardCopyOption.REPLACE_EXISTING);
+ }
+
+ /**
+ * Rename a file or folder using the passed {@link CopyOption}s. If the
+ * rename fails and if we are running on a filesystem where it makes sense
+ * to repeat a failing rename then repeat the rename operation up to 9 times
+ * with 100ms sleep time between two calls. Furthermore if the destination
+ * exists and is a directory hierarchy with only directories in it, the
+ * whole directory hierarchy will be deleted. If the target represents a
+ * non-empty directory structure, empty subdirectories within that structure
+ * may or may not be deleted even if the method fails. Furthermore if the
+ * destination exists and is a file then the file will be replaced if
+ * {@link StandardCopyOption#REPLACE_EXISTING} has been set. If
+ * {@link StandardCopyOption#ATOMIC_MOVE} has been set the rename will be
+ * done atomically or fail with an {@link AtomicMoveNotSupportedException}
+ *
+ * @param src
+ * the old file
+ * @param dst
+ * the new file
+ * @param options
+ * options to pass to
+ * {@link Files#move(java.nio.file.Path, java.nio.file.Path, CopyOption...)}
+ * @throws AtomicMoveNotSupportedException
+ * if file cannot be moved as an atomic file system operation
+ * @throws IOException
+ * @since 4.1
+ */
+ public static void rename(final File src, final File dst,
+ CopyOption... options)
+ throws AtomicMoveNotSupportedException, IOException {
int attempts = FS.DETECTED.retryFailedLockFileCommit() ? 10 : 1;
while (--attempts >= 0) {
- if (src.renameTo(dst))
- return;
try {
- if (!dst.delete())
- delete(dst, EMPTY_DIRECTORIES_ONLY | RECURSIVE);
- // On *nix there is no try, you do or do not
- if (src.renameTo(dst))
- return;
+ Files.move(src.toPath(), dst.toPath(), options);
+ return;
+ } catch (AtomicMoveNotSupportedException e) {
+ throw e;
} catch (IOException e) {
- // ignore and continue retry
+ try {
+ if (!dst.delete()) {
+ delete(dst, EMPTY_DIRECTORIES_ONLY | RECURSIVE);
+ }
+ // On *nix there is no try, you do or do not
+ Files.move(src.toPath(), dst.toPath(), options);
+ return;
+ } catch (IOException e2) {
+ // ignore and continue retry
+ }
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
- throw new IOException(MessageFormat.format(
- JGitText.get().renameFileFailed, src.getAbsolutePath(),
- dst.getAbsolutePath()));
+ throw new IOException(
+ MessageFormat.format(JGitText.get().renameFileFailed,
+ src.getAbsolutePath(), dst.getAbsolutePath()));
}
}
- throw new IOException(MessageFormat.format(
- JGitText.get().renameFileFailed, src.getAbsolutePath(),
- dst.getAbsolutePath()));
+ throw new IOException(
+ MessageFormat.format(JGitText.get().renameFileFailed,
+ src.getAbsolutePath(), dst.getAbsolutePath()));
}
/**
@@ -344,24 +398,48 @@ public class FileUtils {
* Create a symbolic link
*
* @param path
+ * the path of the symbolic link to create
* @param target
+ * the target of the symbolic link
+ * @return the path to the symbolic link
* @throws IOException
- * @since 3.0
+ * @since 4.2
*/
- public static void createSymLink(File path, String target)
+ public static Path createSymLink(File path, String target)
throws IOException {
- FS.DETECTED.createSymLink(path, target);
+ Path nioPath = path.toPath();
+ if (Files.exists(nioPath, LinkOption.NOFOLLOW_LINKS)) {
+ BasicFileAttributes attrs = Files.readAttributes(nioPath,
+ BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
+ if (attrs.isRegularFile() || attrs.isSymbolicLink()) {
+ delete(path);
+ } else {
+ delete(path, EMPTY_DIRECTORIES_ONLY | RECURSIVE);
+ }
+ }
+ if (SystemReader.getInstance().isWindows()) {
+ target = target.replace('/', '\\');
+ }
+ Path nioTarget = new File(target).toPath();
+ return Files.createSymbolicLink(nioPath, nioTarget);
}
/**
* @param path
- * @return the target of the symbolic link, or null if it is not a symbolic
- * link
+ * @return target path of the symlink, or null if it is not a symbolic link
* @throws IOException
* @since 3.0
*/
public static String readSymLink(File path) throws IOException {
- return FS.DETECTED.readSymLink(path);
+ Path nioPath = path.toPath();
+ Path target = Files.readSymbolicLink(nioPath);
+ String targetString = target.toString();
+ if (SystemReader.getInstance().isWindows()) {
+ targetString = targetString.replace('\\', '/');
+ } else if (SystemReader.getInstance().isMacOS()) {
+ targetString = Normalizer.normalize(targetString, Form.NFC);
+ }
+ return targetString;
}
/**
@@ -453,4 +531,237 @@ public class FileUtils {
}
return builder.toString();
}
+
+ /**
+ * Determine if an IOException is a Stale NFS File Handle
+ *
+ * @param ioe
+ * @return a boolean true if the IOException is a Stale NFS FIle Handle
+ * @since 4.1
+ */
+ public static boolean isStaleFileHandle(IOException ioe) {
+ String msg = ioe.getMessage();
+ return msg != null
+ && msg.toLowerCase().matches("stale .*file .*handle"); //$NON-NLS-1$
+ }
+
+ /**
+ * @param file
+ * @return {@code true} if the passed file is a symbolic link
+ */
+ static boolean isSymlink(File file) {
+ return Files.isSymbolicLink(file.toPath());
+ }
+
+ /**
+ * @param file
+ * @return lastModified attribute for given file, not following symbolic
+ * links
+ * @throws IOException
+ */
+ static long lastModified(File file) throws IOException {
+ return Files.getLastModifiedTime(file.toPath(), LinkOption.NOFOLLOW_LINKS)
+ .toMillis();
+ }
+
+ /**
+ * @param file
+ * @param time
+ * @throws IOException
+ */
+ static void setLastModified(File file, long time) throws IOException {
+ Files.setLastModifiedTime(file.toPath(), FileTime.fromMillis(time));
+ }
+
+ /**
+ * @param file
+ * @return {@code true} if the given file exists, not following symbolic
+ * links
+ */
+ static boolean exists(File file) {
+ return Files.exists(file.toPath(), LinkOption.NOFOLLOW_LINKS);
+ }
+
+ /**
+ * @param file
+ * @return {@code true} if the given file is hidden
+ * @throws IOException
+ */
+ static boolean isHidden(File file) throws IOException {
+ return Files.isHidden(file.toPath());
+ }
+
+ /**
+ * @param file
+ * @param hidden
+ * @throws IOException
+ * @since 4.1
+ */
+ public static void setHidden(File file, boolean hidden) throws IOException {
+ Files.setAttribute(file.toPath(), "dos:hidden", Boolean.valueOf(hidden), //$NON-NLS-1$
+ LinkOption.NOFOLLOW_LINKS);
+ }
+
+ /**
+ * @param file
+ * @return length of the given file
+ * @throws IOException
+ * @since 4.1
+ */
+ public static long getLength(File file) throws IOException {
+ Path nioPath = file.toPath();
+ if (Files.isSymbolicLink(nioPath))
+ return Files.readSymbolicLink(nioPath).toString()
+ .getBytes(Constants.CHARSET).length;
+ return Files.size(nioPath);
+ }
+
+ /**
+ * @param file
+ * @return {@code true} if the given file is a directory, not following
+ * symbolic links
+ */
+ static boolean isDirectory(File file) {
+ return Files.isDirectory(file.toPath(), LinkOption.NOFOLLOW_LINKS);
+ }
+
+ /**
+ * @param file
+ * @return {@code true} if the given file is a file, not following symbolic
+ * links
+ */
+ static boolean isFile(File file) {
+ return Files.isRegularFile(file.toPath(), LinkOption.NOFOLLOW_LINKS);
+ }
+
+ /**
+ * @param file
+ * @return {@code true} if the given file can be executed
+ * @since 4.1
+ */
+ public static boolean canExecute(File file) {
+ if (!isFile(file)) {
+ return false;
+ }
+ return Files.isExecutable(file.toPath());
+ }
+
+ /**
+ * @param fs
+ * @param file
+ * @return non null attributes object
+ */
+ static Attributes getFileAttributesBasic(FS fs, File file) {
+ try {
+ Path nioPath = file.toPath();
+ BasicFileAttributes readAttributes = nioPath
+ .getFileSystem()
+ .provider()
+ .getFileAttributeView(nioPath,
+ BasicFileAttributeView.class,
+ LinkOption.NOFOLLOW_LINKS).readAttributes();
+ Attributes attributes = new Attributes(fs, file,
+ true,
+ readAttributes.isDirectory(),
+ fs.supportsExecute() ? file.canExecute() : false,
+ readAttributes.isSymbolicLink(),
+ readAttributes.isRegularFile(), //
+ readAttributes.creationTime().toMillis(), //
+ readAttributes.lastModifiedTime().toMillis(),
+ readAttributes.isSymbolicLink() ? Constants
+ .encode(readSymLink(file)).length
+ : readAttributes.size());
+ return attributes;
+ } catch (IOException e) {
+ return new Attributes(file, fs);
+ }
+ }
+
+ /**
+ * @param fs
+ * @param file
+ * @return file system attributes for the given file
+ * @since 4.1
+ */
+ public static Attributes getFileAttributesPosix(FS fs, File file) {
+ try {
+ Path nioPath = file.toPath();
+ PosixFileAttributes readAttributes = nioPath
+ .getFileSystem()
+ .provider()
+ .getFileAttributeView(nioPath,
+ PosixFileAttributeView.class,
+ LinkOption.NOFOLLOW_LINKS).readAttributes();
+ Attributes attributes = new Attributes(
+ fs,
+ file,
+ true, //
+ readAttributes.isDirectory(), //
+ readAttributes.permissions().contains(
+ PosixFilePermission.OWNER_EXECUTE),
+ readAttributes.isSymbolicLink(),
+ readAttributes.isRegularFile(), //
+ readAttributes.creationTime().toMillis(), //
+ readAttributes.lastModifiedTime().toMillis(),
+ readAttributes.size());
+ return attributes;
+ } catch (IOException e) {
+ return new Attributes(file, fs);
+ }
+ }
+
+ /**
+ * @param file
+ * @return on Mac: NFC normalized {@link File}, otherwise the passed file
+ * @since 4.1
+ */
+ public static File normalize(File file) {
+ if (SystemReader.getInstance().isMacOS()) {
+ // TODO: Would it be faster to check with isNormalized first
+ // assuming normalized paths are much more common
+ String normalized = Normalizer.normalize(file.getPath(),
+ Normalizer.Form.NFC);
+ return new File(normalized);
+ }
+ return file;
+ }
+
+ /**
+ * @param name
+ * @return on Mac: NFC normalized form of given name
+ * @since 4.1
+ */
+ public static String normalize(String name) {
+ if (SystemReader.getInstance().isMacOS()) {
+ if (name == null)
+ return null;
+ return Normalizer.normalize(name, Normalizer.Form.NFC);
+ }
+ return name;
+ }
+
+ /**
+ * Best-effort variation of {@link File#getCanonicalFile()} returning the
+ * input file if the file cannot be canonicalized instead of throwing
+ * {@link IOException}.
+ *
+ * @param file
+ * to be canonicalized; may be {@code null}
+ * @return canonicalized file, or the unchanged input file if
+ * canonicalization failed or if {@code file == null}
+ * @throws SecurityException
+ * if {@link File#getCanonicalFile()} throws one
+ * @since 4.2
+ */
+ public static File canonicalize(File file) {
+ if (file == null) {
+ return null;
+ }
+ try {
+ return file.getCanonicalFile();
+ } catch (IOException e) {
+ return file;
+ }
+ }
+
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/Hook.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/Hook.java
deleted file mode 100644
index c24c9a3..0000000
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/Hook.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2014 Obeo.
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package org.eclipse.jgit.util;
-
-/**
- * An enum describing the different hooks a user can implement to customize his
- * repositories.
- *
- * @since 3.7
- */
-public enum Hook {
- /**
- * Literal for the "pre-commit" git hook.
- * <p>
- * This hook is invoked by git commit, and can be bypassed with the
- * "no-verify" option. It takes no parameter, and is invoked before
- * obtaining the proposed commit log message and making a commit.
- * </p>
- * <p>
- * A non-zero exit code from the called hook means that the commit should be
- * aborted.
- * </p>
- */
- PRE_COMMIT("pre-commit"), //$NON-NLS-1$
-
- /**
- * Literal for the "prepare-commit-msg" git hook.
- * <p>
- * This hook is invoked by git commit right after preparing the default
- * message, and before any editing possibility is displayed to the user.
- * </p>
- * <p>
- * A non-zero exit code from the called hook means that the commit should be
- * aborted.
- * </p>
- */
- PREPARE_COMMIT_MSG("prepare-commit-msg"), //$NON-NLS-1$
-
- /**
- * Literal for the "commit-msg" git hook.
- * <p>
- * This hook is invoked by git commit, and can be bypassed with the
- * "no-verify" option. Its single parameter is the path to the file
- * containing the prepared commit message (typically
- * "<gitdir>/COMMIT-EDITMSG").
- * </p>
- * <p>
- * A non-zero exit code from the called hook means that the commit should be
- * aborted.
- * </p>
- */
- COMMIT_MSG("commit-msg"), //$NON-NLS-1$
-
- /**
- * Literal for the "post-commit" git hook.
- * <p>
- * This hook is invoked by git commit. It takes no parameter and is invoked
- * after a commit has been made.
- * </p>
- * <p>
- * The exit code of this hook has no significance.
- * </p>
- */
- POST_COMMIT("post-commit"), //$NON-NLS-1$
-
- /**
- * Literal for the "post-rewrite" git hook.
- * <p>
- * This hook is invoked after commands that rewrite commits (currently, only
- * "git rebase" and "git commit --amend"). It a single argument denoting the
- * source of the call (one of <code>rebase</code> or <code>amend</code>). It
- * then accepts a list of rewritten commits through stdin, in the form
- * <code><old SHA-1> <new SHA-1>LF</code>.
- * </p>
- * <p>
- * The exit code of this hook has no significance.
- * </p>
- */
- POST_REWRITE("post-rewrite"), //$NON-NLS-1$
-
- /**
- * Literal for the "pre-rebase" git hook.
- * <p>
- * </p>
- * This hook is invoked right before the rebase operation runs. It accepts
- * up to two parameters, the first being the upstream from which the branch
- * to rebase has been forked. If the tip of the series of commits to rebase
- * is HEAD, the other parameter is unset. Otherwise, that tip is passed as
- * the second parameter of the script.
- * <p>
- * A non-zero exit code from the called hook means that the rebase should be
- * aborted.
- * </p>
- */
- PRE_REBASE("pre-rebase"); //$NON-NLS-1$
-
- private final String name;
-
- private Hook(String name) {
- this.name = name;
- }
-
- /**
- * @return The name of this hook.
- */
- public String getName() {
- return name;
- }
-}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
index 37c9f7b..8b4ad0a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
@@ -74,6 +74,12 @@ public class HttpSupport {
/** The {@code User-Agent} header. */
public static final String HDR_USER_AGENT = "User-Agent"; //$NON-NLS-1$
+ /**
+ * The {@code Server} header.
+ * @since 4.0
+ */
+ public static final String HDR_SERVER = "Server"; //$NON-NLS-1$
+
/** The {@code Date} header. */
public static final String HDR_DATE = "Date"; //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java
index c817c47..0d283fd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java
@@ -51,6 +51,7 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.io.Reader;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.text.MessageFormat;
@@ -371,6 +372,75 @@ public class IO {
return l;
}
+ /**
+ * Read the next line from a reader.
+ * <p>
+ * Like {@link java.io.BufferedReader#readLine()}, but only treats
+ * {@code \n} as end-of-line, and includes the trailing newline.
+ *
+ * @param in
+ * the reader to read from.
+ * @param sizeHint
+ * hint for buffer sizing; 0 or negative for default.
+ * @return the next line from the input, always ending in {@code \n} unless
+ * EOF was reached.
+ * @throws IOException
+ * there was an error reading from the stream.
+ * @since 4.1
+ */
+ public static String readLine(Reader in, int sizeHint) throws IOException {
+ if (in.markSupported()) {
+ if (sizeHint <= 0) {
+ sizeHint = 1024;
+ }
+ StringBuilder sb = new StringBuilder(sizeHint);
+ char[] buf = new char[sizeHint];
+ while (true) {
+ in.mark(sizeHint);
+ int n = in.read(buf);
+ if (n < 0) {
+ in.reset();
+ return sb.toString();
+ }
+ for (int i = 0; i < n; i++) {
+ if (buf[i] == '\n') {
+ resetAndSkipFully(in, ++i);
+ sb.append(buf, 0, i);
+ return sb.toString();
+ }
+ }
+ if (n > 0) {
+ sb.append(buf, 0, n);
+ }
+ resetAndSkipFully(in, n);
+ }
+ } else {
+ StringBuilder buf = sizeHint > 0
+ ? new StringBuilder(sizeHint)
+ : new StringBuilder();
+ int i;
+ while ((i = in.read()) != -1) {
+ char c = (char) i;
+ buf.append(c);
+ if (c == '\n') {
+ break;
+ }
+ }
+ return buf.toString();
+ }
+ }
+
+ private static void resetAndSkipFully(Reader fd, long toSkip) throws IOException {
+ fd.reset();
+ while (toSkip > 0) {
+ long r = fd.skip(toSkip);
+ if (r <= 0) {
+ throw new EOFException(JGitText.get().shortSkipOfBlock);
+ }
+ toSkip -= r;
+ }
+ }
+
private IO() {
// Don't create instances of a static only utility.
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/Paths.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/Paths.java
new file mode 100644
index 0000000..6be7ddb
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/Paths.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2016, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.util;
+
+import static org.eclipse.jgit.lib.FileMode.TYPE_MASK;
+import static org.eclipse.jgit.lib.FileMode.TYPE_TREE;
+
+/**
+ * Utility functions for paths inside of a Git repository.
+ *
+ * @since 4.2
+ */
+public class Paths {
+ /**
+ * Remove trailing {@code '/'} if present.
+ *
+ * @param path
+ * input path to potentially remove trailing {@code '/'} from.
+ * @return null if {@code path == null}; {@code path} after removing a
+ * trailing {@code '/'}.
+ */
+ public static String stripTrailingSeparator(String path) {
+ if (path == null || path.isEmpty()) {
+ return path;
+ }
+
+ int i = path.length();
+ if (path.charAt(path.length() - 1) != '/') {
+ return path;
+ }
+ do {
+ i--;
+ } while (path.charAt(i - 1) == '/');
+ return path.substring(0, i);
+ }
+
+ /**
+ * Compare two paths according to Git path sort ordering rules.
+ *
+ * @param aPath
+ * first path buffer. The range {@code [aPos, aEnd)} is used.
+ * @param aPos
+ * index into {@code aPath} where the first path starts.
+ * @param aEnd
+ * 1 past last index of {@code aPath}.
+ * @param aMode
+ * mode of the first file. Trees are sorted as though
+ * {@code aPath[aEnd] == '/'}, even if aEnd does not exist.
+ * @param bPath
+ * second path buffer. The range {@code [bPos, bEnd)} is used.
+ * @param bPos
+ * index into {@code bPath} where the second path starts.
+ * @param bEnd
+ * 1 past last index of {@code bPath}.
+ * @param bMode
+ * mode of the second file. Trees are sorted as though
+ * {@code bPath[bEnd] == '/'}, even if bEnd does not exist.
+ * @return <0 if {@code aPath} sorts before {@code bPath};
+ * 0 if the paths are the same;
+ * >0 if {@code aPath} sorts after {@code bPath}.
+ */
+ public static int compare(byte[] aPath, int aPos, int aEnd, int aMode,
+ byte[] bPath, int bPos, int bEnd, int bMode) {
+ int cmp = coreCompare(
+ aPath, aPos, aEnd, aMode,
+ bPath, bPos, bEnd, bMode);
+ if (cmp == 0) {
+ cmp = lastPathChar(aMode) - lastPathChar(bMode);
+ }
+ return cmp;
+ }
+
+ /**
+ * Compare two paths, checking for identical name.
+ * <p>
+ * Unlike {@code compare} this method returns {@code 0} when the paths have
+ * the same characters in their names, even if the mode differs. It is
+ * intended for use in validation routines detecting duplicate entries.
+ * <p>
+ * Returns {@code 0} if the names are identical and a conflict exists
+ * between {@code aPath} and {@code bPath}, as they share the same name.
+ * <p>
+ * Returns {@code <0} if all possibles occurrences of {@code aPath} sort
+ * before {@code bPath} and no conflict can happen. In a properly sorted
+ * tree there are no other occurrences of {@code aPath} and therefore there
+ * are no duplicate names.
+ * <p>
+ * Returns {@code >0} when it is possible for a duplicate occurrence of
+ * {@code aPath} to appear later, after {@code bPath}. Callers should
+ * continue to examine candidates for {@code bPath} until the method returns
+ * one of the other return values.
+ *
+ * @param aPath
+ * first path buffer. The range {@code [aPos, aEnd)} is used.
+ * @param aPos
+ * index into {@code aPath} where the first path starts.
+ * @param aEnd
+ * 1 past last index of {@code aPath}.
+ * @param bPath
+ * second path buffer. The range {@code [bPos, bEnd)} is used.
+ * @param bPos
+ * index into {@code bPath} where the second path starts.
+ * @param bEnd
+ * 1 past last index of {@code bPath}.
+ * @param bMode
+ * mode of the second file. Trees are sorted as though
+ * {@code bPath[bEnd] == '/'}, even if bEnd does not exist.
+ * @return <0 if no duplicate name could exist;
+ * 0 if the paths have the same name;
+ * >0 other {@code bPath} should still be checked by caller.
+ */
+ public static int compareSameName(
+ byte[] aPath, int aPos, int aEnd,
+ byte[] bPath, int bPos, int bEnd, int bMode) {
+ return coreCompare(
+ aPath, aPos, aEnd, TYPE_TREE,
+ bPath, bPos, bEnd, bMode);
+ }
+
+ private static int coreCompare(
+ byte[] aPath, int aPos, int aEnd, int aMode,
+ byte[] bPath, int bPos, int bEnd, int bMode) {
+ while (aPos < aEnd && bPos < bEnd) {
+ int cmp = (aPath[aPos++] & 0xff) - (bPath[bPos++] & 0xff);
+ if (cmp != 0) {
+ return cmp;
+ }
+ }
+ if (aPos < aEnd) {
+ return (aPath[aPos] & 0xff) - lastPathChar(bMode);
+ }
+ if (bPos < bEnd) {
+ return lastPathChar(aMode) - (bPath[bPos] & 0xff);
+ }
+ return 0;
+ }
+
+ private static int lastPathChar(int mode) {
+ if ((mode & TYPE_MASK) == TYPE_TREE) {
+ return '/';
+ }
+ return 0;
+ }
+
+ private Paths() {
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/ProcessResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/ProcessResult.java
index f56bb15..77c9608 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/ProcessResult.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/ProcessResult.java
@@ -109,4 +109,13 @@ public class ProcessResult {
public Status getStatus() {
return status;
}
+
+ /**
+ * @return <code>true</code> if the execution occurred and resulted in a
+ * return code different from 0, <code>false</code> otherwise.
+ * @since 4.0
+ */
+ public boolean isExecutedWithError() {
+ return getStatus() == ProcessResult.Status.OK && getExitCode() != 0;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java
index 3c2460c..f2955f7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java
@@ -44,6 +44,8 @@
package org.eclipse.jgit.util;
+import static java.nio.charset.StandardCharsets.ISO_8859_1;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.eclipse.jgit.lib.ObjectChecker.author;
import static org.eclipse.jgit.lib.ObjectChecker.committer;
import static org.eclipse.jgit.lib.ObjectChecker.encoding;
@@ -60,6 +62,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.PersonIdent;
@@ -70,7 +73,7 @@ public final class RawParseUtils {
*
* @since 2.2
*/
- public static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); //$NON-NLS-1$
+ public static final Charset UTF8_CHARSET = UTF_8;
private static final byte[] digits10;
@@ -81,8 +84,9 @@ public final class RawParseUtils {
private static final Map<String, Charset> encodingAliases;
static {
- encodingAliases = new HashMap<String, Charset>();
- encodingAliases.put("latin-1", Charset.forName("ISO-8859-1")); //$NON-NLS-1$ //$NON-NLS-2$
+ encodingAliases = new HashMap<>();
+ encodingAliases.put("latin-1", ISO_8859_1); //$NON-NLS-1$
+ encodingAliases.put("iso-latin-1", ISO_8859_1); //$NON-NLS-1$
digits10 = new byte['9' + 1];
Arrays.fill(digits10, (byte) -1);
@@ -390,7 +394,28 @@ public final class RawParseUtils {
* @return the timezone at this location, expressed in minutes.
*/
public static final int parseTimeZoneOffset(final byte[] b, int ptr) {
- final int v = parseBase10(b, ptr, null);
+ return parseTimeZoneOffset(b, ptr, null);
+ }
+
+ /**
+ * Parse a Git style timezone string.
+ * <p>
+ * The sequence "-0315" will be parsed as the numeric value -195, as the
+ * lower two positions count minutes, not 100ths of an hour.
+ *
+ * @param b
+ * buffer to scan.
+ * @param ptr
+ * position within buffer to start parsing digits at.
+ * @param ptrResult
+ * optional location to return the new ptr value through. If null
+ * the ptr value will be discarded.
+ * @return the timezone at this location, expressed in minutes.
+ * @since 4.1
+ */
+ public static final int parseTimeZoneOffset(final byte[] b, int ptr,
+ MutableInteger ptrResult) {
+ final int v = parseBase10(b, ptr, ptrResult);
final int tzMins = v % 100;
final int tzHours = v / 100;
return tzHours * 60 + tzMins;
@@ -650,35 +675,60 @@ public final class RawParseUtils {
}
/**
+ * Parse the "encoding " header as a string.
+ * <p>
+ * Locates the "encoding " header (if present) and returns its value.
+ *
+ * @param b
+ * buffer to scan.
+ * @return the encoding header as specified in the commit; null if the
+ * header was not present and should be assumed.
+ * @since 4.2
+ */
+ @Nullable
+ public static String parseEncodingName(final byte[] b) {
+ int enc = encoding(b, 0);
+ if (enc < 0) {
+ return null;
+ }
+ int lf = nextLF(b, enc);
+ return decode(UTF_8, b, enc, lf - 1);
+ }
+
+ /**
* Parse the "encoding " header into a character set reference.
* <p>
* Locates the "encoding " header (if present) by first calling
* {@link #encoding(byte[], int)} and then returns the proper character set
* to apply to this buffer to evaluate its contents as character data.
* <p>
- * If no encoding header is present, {@link Constants#CHARSET} is assumed.
+ * If no encoding header is present {@code UTF-8} is assumed.
*
* @param b
* buffer to scan.
* @return the Java character set representation. Never null.
+ * @throws IllegalCharsetNameException
+ * if the character set requested by the encoding header is
+ * malformed and unsupportable.
+ * @throws UnsupportedCharsetException
+ * if the JRE does not support the character set requested by
+ * the encoding header.
*/
public static Charset parseEncoding(final byte[] b) {
- final int enc = encoding(b, 0);
- if (enc < 0)
- return Constants.CHARSET;
- final int lf = nextLF(b, enc);
- String decoded = decode(Constants.CHARSET, b, enc, lf - 1);
+ String enc = parseEncodingName(b);
+ if (enc == null) {
+ return UTF_8;
+ }
+
+ String name = enc.trim();
try {
- return Charset.forName(decoded);
- } catch (IllegalCharsetNameException badName) {
- Charset aliased = charsetForAlias(decoded);
- if (aliased != null)
- return aliased;
- throw badName;
- } catch (UnsupportedCharsetException badName) {
- Charset aliased = charsetForAlias(decoded);
- if (aliased != null)
+ return Charset.forName(name);
+ } catch (IllegalCharsetNameException
+ | UnsupportedCharsetException badName) {
+ Charset aliased = charsetForAlias(name);
+ if (aliased != null) {
return aliased;
+ }
throw badName;
}
}
@@ -717,7 +767,15 @@ public final class RawParseUtils {
* parsed.
*/
public static PersonIdent parsePersonIdent(final byte[] raw, final int nameB) {
- final Charset cs = parseEncoding(raw);
+ Charset cs;
+ try {
+ cs = parseEncoding(raw);
+ } catch (IllegalCharsetNameException | UnsupportedCharsetException e) {
+ // Assume UTF-8 for person identities, usually this is correct.
+ // If not decode() will fall back to the ISO-8859-1 encoding.
+ cs = UTF_8;
+ }
+
final int emailB = nextLF(raw, nameB, '<');
final int emailE = nextLF(raw, emailB, '>');
if (emailB >= raw.length || raw[emailB] == '\n' ||
@@ -865,7 +923,7 @@ public final class RawParseUtils {
*/
public static String decode(final byte[] buffer, final int start,
final int end) {
- return decode(Constants.CHARSET, buffer, start, end);
+ return decode(UTF_8, buffer, start, end);
}
/**
@@ -939,23 +997,21 @@ public final class RawParseUtils {
public static String decodeNoFallback(final Charset cs,
final byte[] buffer, final int start, final int end)
throws CharacterCodingException {
- final ByteBuffer b = ByteBuffer.wrap(buffer, start, end - start);
+ ByteBuffer b = ByteBuffer.wrap(buffer, start, end - start);
b.mark();
// Try our built-in favorite. The assumption here is that
// decoding will fail if the data is not actually encoded
// using that encoder.
- //
try {
- return decode(b, Constants.CHARSET);
+ return decode(b, UTF_8);
} catch (CharacterCodingException e) {
b.reset();
}
- if (!cs.equals(Constants.CHARSET)) {
+ if (!cs.equals(UTF_8)) {
// Try the suggested encoding, it might be right since it was
// provided by the caller.
- //
try {
return decode(b, cs);
} catch (CharacterCodingException e) {
@@ -965,9 +1021,8 @@ public final class RawParseUtils {
// Try the default character set. A small group of people
// might actually use the same (or very similar) locale.
- //
- final Charset defcs = Charset.defaultCharset();
- if (!defcs.equals(cs) && !defcs.equals(Constants.CHARSET)) {
+ Charset defcs = Charset.defaultCharset();
+ if (!defcs.equals(cs) && !defcs.equals(UTF_8)) {
try {
return decode(b, defcs);
} catch (CharacterCodingException e) {
@@ -1081,7 +1136,17 @@ public final class RawParseUtils {
return ptr;
}
- private static int lastIndexOfTrim(byte[] raw, char ch, int pos) {
+ /**
+ * @param raw
+ * buffer to scan.
+ * @param ch
+ * character to find.
+ * @param pos
+ * starting position.
+ * @return last index of ch in raw, trimming spaces.
+ * @since 4.1
+ */
+ public static int lastIndexOfTrim(byte[] raw, char ch, int pos) {
while (pos >= 0 && raw[pos] == ' ')
pos--;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RefList.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RefList.java
index 4695111..0853e95 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RefList.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RefList.java
@@ -80,9 +80,9 @@ public class RefList<T extends Ref> implements Iterable<Ref> {
return (RefList<T>) EMPTY;
}
- private final Ref[] list;
+ final Ref[] list;
- private final int cnt;
+ final int cnt;
RefList(Ref[] list, int cnt) {
this.list = list;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RefMap.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RefMap.java
index 5cc7e92..c72727b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RefMap.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RefMap.java
@@ -78,10 +78,10 @@ public class RefMap extends AbstractMap<String, Ref> {
* All reference names in this map must start with this prefix. If the
* prefix is not the empty string, it must end with a '/'.
*/
- private final String prefix;
+ final String prefix;
/** Immutable collection of the packed references at construction time. */
- private RefList<Ref> packed;
+ RefList<Ref> packed;
/**
* Immutable collection of the loose references at construction time.
@@ -91,7 +91,7 @@ public class RefMap extends AbstractMap<String, Ref> {
* are typically unresolved, so they only tell us who their target is, but
* not the current value of the target.
*/
- private RefList<Ref> loose;
+ RefList<Ref> loose;
/**
* Immutable collection of resolved symbolic references.
@@ -101,11 +101,11 @@ public class RefMap extends AbstractMap<String, Ref> {
* from {@link #loose}. Every entry in this list must be matched by an entry
* in {@code loose}, otherwise it might be omitted by the map.
*/
- private RefList<Ref> resolved;
+ RefList<Ref> resolved;
- private int size;
+ int size;
- private boolean sizeIsValid;
+ boolean sizeIsValid;
private Set<Entry<String, Ref>> entrySet;
@@ -280,7 +280,7 @@ public class RefMap extends AbstractMap<String, Ref> {
return name;
}
- private String toMapKey(Ref ref) {
+ String toMapKey(Ref ref) {
String name = ref.getName();
if (0 < prefix.length())
name = name.substring(prefix.length());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java
index 40b89fb..2bfeaf1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java
@@ -96,6 +96,32 @@ public final class StringUtils {
return r.toString();
}
+
+ /**
+ * Borrowed from commons-lang <code>StringUtils.capitalize()</code> method.
+ *
+ * <p>
+ * Capitalizes a String changing the first letter to title case as per
+ * {@link Character#toTitleCase(char)}. No other letters are changed.
+ * </p>
+ *
+ * A <code>null</code> input String returns <code>null</code>.</p>
+ *
+ * @param str
+ * the String to capitalize, may be null
+ * @return the capitalized String, <code>null</code> if null String input
+ * @since 4.0
+ */
+ public static String capitalize(String str) {
+ int strLen;
+ if (str == null || (strLen = str.length()) == 0) {
+ return str;
+ }
+ return new StringBuffer(strLen)
+ .append(Character.toTitleCase(str.charAt(0)))
+ .append(str.substring(1)).toString();
+ }
+
/**
* Test if two strings are equal, ignoring case.
* <p>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
index f6f415e..9860ef0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
@@ -71,6 +71,11 @@ import org.eclipse.jgit.lib.ObjectChecker;
*/
public abstract class SystemReader {
private static final SystemReader DEFAULT;
+
+ private static Boolean isMacOS;
+
+ private static Boolean isWindows;
+
static {
SystemReader r = new Default();
r.init();
@@ -89,8 +94,8 @@ public abstract class SystemReader {
}
public FileBasedConfig openSystemConfig(Config parent, FS fs) {
- File prefix = fs.gitPrefix();
- if (prefix == null) {
+ File configFile = fs.getGitSystemConfig();
+ if (configFile == null) {
return new FileBasedConfig(null, fs) {
public void load() {
// empty, do not load
@@ -102,9 +107,7 @@ public abstract class SystemReader {
}
};
}
- File etc = fs.resolve(prefix, "etc"); //$NON-NLS-1$
- File config = fs.resolve(etc, "gitconfig"); //$NON-NLS-1$
- return new FileBasedConfig(parent, config, fs);
+ return new FileBasedConfig(parent, configFile, fs);
}
public FileBasedConfig openUserConfig(Config parent, FS fs) {
@@ -150,6 +153,8 @@ public abstract class SystemReader {
* the default instance.
*/
public static void setInstance(SystemReader newReader) {
+ isMacOS = null;
+ isWindows = null;
if (newReader == null)
INSTANCE = DEFAULT;
else {
@@ -295,26 +300,31 @@ public abstract class SystemReader {
* @return true if we are running on a Windows.
*/
public boolean isWindows() {
- String osDotName = AccessController
- .doPrivileged(new PrivilegedAction<String>() {
- public String run() {
- return getProperty("os.name"); //$NON-NLS-1$
- }
- });
- return osDotName.startsWith("Windows"); //$NON-NLS-1$
+ if (isWindows == null) {
+ String osDotName = getOsName();
+ isWindows = Boolean.valueOf(osDotName.startsWith("Windows")); //$NON-NLS-1$
+ }
+ return isWindows.booleanValue();
}
/**
* @return true if we are running on Mac OS X
*/
public boolean isMacOS() {
- String osDotName = AccessController
- .doPrivileged(new PrivilegedAction<String>() {
- public String run() {
- return getProperty("os.name"); //$NON-NLS-1$
- }
- });
- return "Mac OS X".equals(osDotName) || "Darwin".equals(osDotName); //$NON-NLS-1$ //$NON-NLS-2$
+ if (isMacOS == null) {
+ String osDotName = getOsName();
+ isMacOS = Boolean.valueOf(
+ "Mac OS X".equals(osDotName) || "Darwin".equals(osDotName)); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ return isMacOS.booleanValue();
+ }
+
+ private String getOsName() {
+ return AccessController.doPrivileged(new PrivilegedAction<String>() {
+ public String run() {
+ return getProperty("os.name"); //$NON-NLS-1$
+ }
+ });
}
/**
@@ -329,4 +339,19 @@ public abstract class SystemReader {
public void checkPath(String path) throws CorruptObjectException {
platformChecker.checkPath(path);
}
+
+ /**
+ * Check tree path entry for validity.
+ * <p>
+ * Scans a multi-directory path string such as {@code "src/main.c"}.
+ *
+ * @param path
+ * path string to scan.
+ * @throws CorruptObjectException
+ * path is invalid.
+ * @since 4.2
+ */
+ public void checkPath(byte[] path) throws CorruptObjectException {
+ platformChecker.checkPath(path, 0, path.length);
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
index 10aade4..3cd5929 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
@@ -44,6 +44,7 @@
package org.eclipse.jgit.util;
+import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -68,7 +69,7 @@ public abstract class TemporaryBuffer extends OutputStream {
protected static final int DEFAULT_IN_CORE_LIMIT = 1024 * 1024;
/** Chain of data, if we are still completely in-core; otherwise null. */
- private ArrayList<Block> blocks;
+ ArrayList<Block> blocks;
/**
* Maximum number of bytes we will permit storing in memory.
@@ -78,6 +79,9 @@ public abstract class TemporaryBuffer extends OutputStream {
*/
private int inCoreLimit;
+ /** Initial size of block list. */
+ private int initialBlocks;
+
/** If {@link #inCoreLimit} has been reached, remainder goes here. */
private OutputStream overflow;
@@ -86,10 +90,28 @@ public abstract class TemporaryBuffer extends OutputStream {
*
* @param limit
* maximum number of bytes to store in memory before entering the
- * overflow output path.
+ * overflow output path; also used as the estimated size.
*/
protected TemporaryBuffer(final int limit) {
- inCoreLimit = limit;
+ this(limit, limit);
+ }
+
+ /**
+ * Create a new empty temporary buffer.
+ *
+ * @param estimatedSize
+ * estimated size of storage used, to size the initial list of
+ * block pointers.
+ * @param limit
+ * maximum number of bytes to store in memory before entering the
+ * overflow output path.
+ * @since 4.0
+ */
+ protected TemporaryBuffer(final int estimatedSize, final int limit) {
+ if (estimatedSize > limit)
+ throw new IllegalArgumentException();
+ this.inCoreLimit = limit;
+ this.initialBlocks = (estimatedSize - 1) / Block.SZ + 1;
reset();
}
@@ -225,6 +247,37 @@ public abstract class TemporaryBuffer extends OutputStream {
}
/**
+ * Convert this buffer's contents into a contiguous byte array. If this size
+ * of the buffer exceeds the limit only return the first {@code limit} bytes
+ * <p>
+ * The buffer is only complete after {@link #close()} has been invoked.
+ *
+ * @param limit
+ * the maximum number of bytes to be returned
+ *
+ * @return the byte array limited to {@code limit} bytes.
+ * @throws IOException
+ * an error occurred reading from a local temporary file
+ * @throws OutOfMemoryError
+ * the buffer cannot fit in memory
+ *
+ * @since 4.2
+ */
+ public byte[] toByteArray(int limit) throws IOException {
+ final long len = Math.min(length(), limit);
+ if (Integer.MAX_VALUE < len)
+ throw new OutOfMemoryError(
+ JGitText.get().lengthExceedsMaximumArraySize);
+ final byte[] out = new byte[(int) len];
+ int outPtr = 0;
+ for (final Block b : blocks) {
+ System.arraycopy(b.buffer, 0, out, outPtr, b.count);
+ outPtr += b.count;
+ }
+ return out;
+ }
+
+ /**
* Send this buffer to an output stream.
* <p>
* This method may only be invoked after {@link #close()} has completed
@@ -270,13 +323,11 @@ public abstract class TemporaryBuffer extends OutputStream {
if (overflow != null) {
destroy();
}
- if (inCoreLimit < Block.SZ) {
- blocks = new ArrayList<Block>(1);
- blocks.add(new Block(inCoreLimit));
- } else {
- blocks = new ArrayList<Block>(inCoreLimit / Block.SZ);
- blocks.add(new Block());
- }
+ if (blocks != null)
+ blocks.clear();
+ else
+ blocks = new ArrayList<Block>(initialBlocks);
+ blocks.add(new Block(Math.min(inCoreLimit, Block.SZ)));
}
/**
@@ -362,29 +413,6 @@ public abstract class TemporaryBuffer extends OutputStream {
private File onDiskFile;
/**
- * Create a new temporary buffer.
- *
- * @deprecated Use the {@code File} overload to supply a directory.
- */
- @Deprecated
- public LocalFile() {
- this(null, DEFAULT_IN_CORE_LIMIT);
- }
-
- /**
- * Create a new temporary buffer, limiting memory usage.
- *
- * @param inCoreLimit
- * maximum number of bytes to store in memory. Storage beyond
- * this limit will use the local file.
- * @deprecated Use the {@code File,int} overload to supply a directory.
- */
- @Deprecated
- public LocalFile(final int inCoreLimit) {
- this(null, inCoreLimit);
- }
-
- /**
* Create a new temporary buffer, limiting memory usage.
*
* @param directory
@@ -416,7 +444,7 @@ public abstract class TemporaryBuffer extends OutputStream {
protected OutputStream overflow() throws IOException {
onDiskFile = File.createTempFile("jgit_", ".buf", directory); //$NON-NLS-1$ //$NON-NLS-2$
- return new FileOutputStream(onDiskFile);
+ return new BufferedOutputStream(new FileOutputStream(onDiskFile));
}
public long length() {
@@ -498,12 +526,28 @@ public abstract class TemporaryBuffer extends OutputStream {
* Create a new heap buffer with a maximum storage limit.
*
* @param limit
+ * maximum number of bytes that can be stored in this buffer;
+ * also used as the estimated size. Storing beyond this many
+ * will cause an IOException to be thrown during write.
+ */
+ public Heap(final int limit) {
+ super(limit);
+ }
+
+ /**
+ * Create a new heap buffer with a maximum storage limit.
+ *
+ * @param estimatedSize
+ * estimated size of storage used, to size the initial list of
+ * block pointers.
+ * @param limit
* maximum number of bytes that can be stored in this buffer.
* Storing beyond this many will cause an IOException to be
* thrown during write.
+ * @since 4.0
*/
- public Heap(final int limit) {
- super(limit);
+ public Heap(final int estimatedSize, final int limit) {
+ super(estimatedSize, limit);
}
@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/LimitedInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/LimitedInputStream.java
index 85c8172..d74532f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/LimitedInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/LimitedInputStream.java
@@ -48,6 +48,8 @@ import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
+import org.eclipse.jgit.internal.JGitText;
+
/**
* Wraps a {@link InputStream}, limiting the number of bytes which can be
* read.
@@ -124,10 +126,10 @@ public abstract class LimitedInputStream extends FilterInputStream {
@Override
public synchronized void reset() throws IOException {
if (!in.markSupported())
- throw new IOException("Mark not supported");
+ throw new IOException(JGitText.get().unsupportedMark);
if (mark == -1)
- throw new IOException("Mark not set");
+ throw new IOException(JGitText.get().unsetMark);
in.reset();
left = mark;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/StreamCopyThread.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/StreamCopyThread.java
index 24b8b53..8d39a22 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/StreamCopyThread.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/StreamCopyThread.java
@@ -47,6 +47,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
+import java.util.concurrent.atomic.AtomicInteger;
/** Thread to copy from an input stream to an output stream. */
public class StreamCopyThread extends Thread {
@@ -58,6 +59,8 @@ public class StreamCopyThread extends Thread {
private volatile boolean done;
+ private final AtomicInteger flushCount = new AtomicInteger(0);
+
/**
* Create a thread to copy data from an input stream to an output stream.
*
@@ -82,6 +85,7 @@ public class StreamCopyThread extends Thread {
* the request.
*/
public void flush() {
+ flushCount.incrementAndGet();
interrupt();
}
@@ -109,22 +113,30 @@ public class StreamCopyThread extends Thread {
public void run() {
try {
final byte[] buf = new byte[BUFFER_SIZE];
- int interruptCounter = 0;
+ int flushCountBeforeRead = 0;
+ boolean readInterrupted = false;
for (;;) {
try {
- if (interruptCounter > 0) {
+ if (readInterrupted) {
dst.flush();
- interruptCounter--;
+ readInterrupted = false;
+ if (!flushCount.compareAndSet(flushCountBeforeRead, 0)) {
+ // There was a flush() call since last blocked read.
+ // Set interrupt status, so next blocked read will throw
+ // an InterruptedIOException and we will flush again.
+ interrupt();
+ }
}
if (done)
break;
+ flushCountBeforeRead = flushCount.get();
final int n;
try {
n = src.read(buf);
} catch (InterruptedIOException wakey) {
- interruptCounter++;
+ readInterrupted = true;
continue;
}
if (n < 0)
@@ -141,7 +153,7 @@ public class StreamCopyThread extends Thread {
// set interrupt status, which will be checked
// when we block in src.read
- if (writeInterrupted)
+ if (writeInterrupted || flushCount.get() > 0)
interrupt();
break;
}
diff --git a/pom.xml b/pom.xml
index 41fc910..a65622d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -51,20 +51,30 @@
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
<packaging>pom</packaging>
- <version>3.7.1.201504261725-r</version>
+ <version>4.2.0.201601211800-r</version>
<name>JGit - Parent</name>
<url>${jgit-url}</url>
+ <organization>
+ <name>Eclipse JGit Project</name>
+ <url>http://www.eclipse.org/jgit</url>
+ </organization>
+
<description>
Pure Java implementation of Git
</description>
<scm>
- <url>http://egit.eclipse.org/w/?p=jgit.git</url>
- <connection>scm:git:git://egit.eclipse.org/jgit.git</connection>
+ <url>http://git.eclipse.org/c/jgit/jgit.git/</url>
+ <connection>scm:git:https://git.eclipse.org/r/jgit/jgit</connection>
</scm>
+ <ciManagement>
+ <system>hudson</system>
+ <url>https://hudson.eclipse.org/jgit/</url>
+ </ciManagement>
+
<developers>
<developer>
<name>Chris Aniszczyk</name>
@@ -82,6 +92,9 @@
<name>Gunnar Wagenknecht</name>
</developer>
<developer>
+ <name>Jonathan Nieder</name>
+ </developer>
+ <developer>
<name>Kevin Sawicki</name>
</developer>
<developer>
@@ -94,6 +107,9 @@
<name>Robin Rosenberg</name>
</developer>
<developer>
+ <name>Robin Stocker</name>
+ </developer>
+ <developer>
<name>Sasa Zivkov</name>
</developer>
<developer>
@@ -176,22 +192,22 @@
<maven.build.timestamp.format>yyyyMMddHHmm</maven.build.timestamp.format>
<bundle-manifest>${project.build.directory}/META-INF/MANIFEST.MF</bundle-manifest>
- <jgit-last-release-version>3.6.0.201412230720-r</jgit-last-release-version>
- <jsch-version>0.1.50</jsch-version>
+ <jgit-last-release-version>4.1.0.201509280440-r</jgit-last-release-version>
+ <jsch-version>0.1.53</jsch-version>
<javaewah-version>0.7.9</javaewah-version>
<junit-version>4.11</junit-version>
<test-fork-count>1C</test-fork-count>
- <!-- TODO: update Maven dependency for args4j to 2.0.21 as soon as available on Maven Central -->
- <args4j-version>2.0.12</args4j-version>
+ <args4j-version>2.0.15</args4j-version>
<commons-compress-version>1.6</commons-compress-version>
<osgi-core-version>4.3.1</osgi-core-version>
- <servlet-api-version>2.5</servlet-api-version>
- <jetty-version>7.6.14.v20131031</jetty-version>
- <clirr-version>2.6.1</clirr-version>
- <httpclient-version>4.1.3</httpclient-version>
+ <servlet-api-version>3.1.0</servlet-api-version>
+ <jetty-version>9.2.13.v20150730</jetty-version>
+ <japicmp-version>0.5.3</japicmp-version>
+ <httpclient-version>4.3.6</httpclient-version>
<slf4j-version>1.7.2</slf4j-version>
<log4j-version>1.2.15</log4j-version>
- <maven-javadoc-plugin-version>2.9.1</maven-javadoc-plugin-version>
+ <maven-javadoc-plugin-version>2.10.1</maven-javadoc-plugin-version>
+ <tycho-extras-version>0.23.0</tycho-extras-version>
<!-- Properties to enable jacoco code coverage analysis -->
<sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin>
@@ -211,6 +227,10 @@
<id>repo.eclipse.org.cbi-releases</id>
<url>https://repo.eclipse.org/content/repositories/cbi-releases/</url>
</pluginRepository>
+ <pluginRepository>
+ <id>repo.eclipse.org.cbi-snapshots</id>
+ <url>https://repo.eclipse.org/content/repositories/cbi-snapshots/</url>
+ </pluginRepository>
</pluginRepositories>
<build>
@@ -219,7 +239,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
- <version>2.5</version>
+ <version>2.6</version>
<configuration>
<archive>
<manifestEntries>
@@ -238,12 +258,17 @@
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
- <version>3.1</version>
+ <version>3.2</version>
+ <configuration>
+ <encoding>UTF-8</encoding>
+ <source>1.7</source>
+ <target>1.7</target>
+ </configuration>
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
- <version>2.5</version>
+ <version>2.6.1</version>
</plugin>
<plugin>
@@ -255,19 +280,19 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
- <version>1.7</version>
+ <version>1.8</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
- <version>2.8</version>
+ <version>2.10</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
- <version>2.3</version>
+ <version>2.4</version>
</plugin>
<plugin>
@@ -279,7 +304,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
- <version>2.17</version>
+ <version>2.18.1</version>
<configuration>
<forkCount>${test-fork-count}</forkCount>
<reuseForks>true</reuseForks>
@@ -312,11 +337,11 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
- <version>3.2</version>
+ <version>3.4</version>
<configuration>
<sourceEncoding>utf-8</sourceEncoding>
<minimumTokens>100</minimumTokens>
- <targetJdk>1.5</targetJdk>
+ <targetJdk>1.7</targetJdk>
<format>xml</format>
<failOnViolation>false</failOnViolation>
</configuration>
@@ -330,29 +355,19 @@
</plugin>
<plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>clirr-maven-plugin</artifactId>
- <version>${clirr-version}</version>
- <configuration>
- <comparisonVersion>${jgit-last-release-version}</comparisonVersion>
- <minSeverity>info</minSeverity>
- </configuration>
- </plugin>
-
- <plugin>
<groupId>org.eclipse.cbi.maven.plugins</groupId>
<artifactId>eclipse-jarsigner-plugin</artifactId>
- <version>1.1.1</version>
+ <version>1.1.2</version>
</plugin>
<plugin>
<groupId>org.eclipse.tycho.extras</groupId>
<artifactId>tycho-pack200a-plugin</artifactId>
- <version>0.22.0</version>
+ <version>${tycho-extras-version}</version>
</plugin>
<plugin>
<groupId>org.eclipse.tycho.extras</groupId>
<artifactId>tycho-pack200b-plugin</artifactId>
- <version>0.22.0</version>
+ <version>${tycho-extras-version}</version>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
@@ -371,17 +386,27 @@
</dependency>
</dependencies>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-report-plugin</artifactId>
+ <version>2.18.1</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jxr-plugin</artifactId>
+ <version>2.5</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-project-info-reports-plugin</artifactId>
+ <version>2.8</version>
+ </plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
- <configuration>
- <source>1.5</source>
- <target>1.5</target>
- <encoding>UTF-8</encoding>
- </configuration>
</plugin>
<plugin>
@@ -428,7 +453,7 @@
<encoding>${project.build.sourceEncoding}</encoding>
<quiet>true</quiet>
<links>
- <link>http://java.sun.com/j2se/1.5.0/docs/api</link>
+ <link>http://docs.oracle.com/javase/7/docs/api</link>
</links>
</configuration>
<executions>
@@ -475,6 +500,10 @@
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-report-plugin</artifactId>
+ </plugin>
</plugins>
</build>
@@ -487,6 +516,28 @@
<configuration>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jxr-plugin</artifactId>
+ <version>2.5</version>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>findbugs-maven-plugin</artifactId>
+ <version>3.0.0</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-report-plugin</artifactId>
+ <version>2.18.1</version>
+ <configuration>
+ <aggregate>true</aggregate>
+ <alwaysGenerateSurefireReport>false</alwaysGenerateSurefireReport>
+ <reportsDirectories>
+ <reportsDirectories>${project.build.directory}/surefire-reports</reportsDirectories>
+ </reportsDirectories>
+ </configuration>
+ </plugin>
</plugins>
</reporting>
@@ -518,7 +569,7 @@
<dependency>
<groupId>javax.servlet</groupId>
- <artifactId>servlet-api</artifactId>
+ <artifactId>javax.servlet-api</artifactId>
<version>${servlet-api-version}</version>
</dependency>
@@ -562,6 +613,24 @@
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j-version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>javax.mail</groupId>
+ <artifactId>mail</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>javax.jms</groupId>
+ <artifactId>jms</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>com.sun.jdmk</groupId>
+ <artifactId>jmxtools</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>com.sun.jmx</groupId>
+ <artifactId>jmxri</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
</dependencies>
</dependencyManagement>
@@ -587,23 +656,10 @@
<profiles>
<profile>
- <id>jgit.java6</id>
- <activation>
- <jdk>[1.6,)</jdk>
- </activation>
- <modules>
- <module>org.eclipse.jgit.console</module>
- </modules>
- </profile>
- <profile>
<id>jgit.java7</id>
<activation>
<jdk>[1.7,)</jdk>
</activation>
- <modules>
- <module>org.eclipse.jgit.java7</module>
- <module>org.eclipse.jgit.java7.test</module>
- </modules>
</profile>
<profile>
<id>jgit.java8</id>
diff --git a/tools/default.defs b/tools/default.defs
new file mode 100644
index 0000000..3481fa1
--- /dev/null
+++ b/tools/default.defs
@@ -0,0 +1,42 @@
+def java_sources(
+ name,
+ srcs,
+ visibility = ['PUBLIC']
+ ):
+ java_library(
+ name = name,
+ resources = srcs,
+ visibility = visibility,
+ )
+
+def maven_jar(
+ name,
+ group,
+ artifact,
+ version,
+ bin_sha1,
+ src_sha1,
+ visibility = ['PUBLIC']):
+ jar_name = '%s__jar' % name
+ src_name = '%s__src' % name
+
+ remote_file(
+ name = jar_name,
+ sha1 = bin_sha1,
+ url = 'mvn:%s:%s:jar:%s' % (group, artifact, version),
+ out = '%s.jar' % jar_name,
+ )
+
+ remote_file(
+ name = src_name,
+ sha1 = src_sha1,
+ url = 'mvn:%s:%s:src:%s' % (group, artifact, version),
+ out = '%s.jar' % src_name,
+ )
+
+ prebuilt_jar(
+ name = name,
+ binary_jar = ':' + jar_name,
+ source_jar = ':' + src_name,
+ visibility = visibility)
+
diff --git a/tools/eclipse-JGit-Format.xml b/tools/eclipse-JGit-Format.xml
index 52845ca..278b449 100644
--- a/tools/eclipse-JGit-Format.xml
+++ b/tools/eclipse-JGit-Format.xml
@@ -45,7 +45,7 @@
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
-<setting id="org.eclipse.jdt.core.compiler.source" value="1.5"/>
+<setting id="org.eclipse.jdt.core.compiler.source" value="1.7"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
@@ -156,7 +156,7 @@
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
-<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.5"/>
+<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.7"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
@@ -227,7 +227,7 @@
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
-<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.5"/>
+<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.7"/>
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member" value="insert"/>
diff --git a/tools/git.defs b/tools/git.defs
new file mode 100644
index 0000000..557dff2
--- /dev/null
+++ b/tools/git.defs
@@ -0,0 +1,9 @@
+def git_version():
+ import subprocess
+ cmd = ['git', 'describe', '--always', '--match', 'v[0-9].*', '--dirty']
+ p = subprocess.Popen(cmd, stdout = subprocess.PIPE)
+ v = p.communicate()[0].strip()
+ r = p.returncode
+ if r != 0:
+ raise subprocess.CalledProcessError(r, ' '.join(cmd))
+ return v
diff --git a/tools/maven-central/deploy.rb b/tools/maven-central/deploy.rb
index 929af9c..d620a88 100755
--- a/tools/maven-central/deploy.rb
+++ b/tools/maven-central/deploy.rb
@@ -51,8 +51,7 @@ group = 'org.eclipse.jgit'
artifacts = [group,
group + '.ant',
group + '.archive',
- group + '.console',
- group + '.http.apache',
+ group + '.http.apache',
group + '.http.server',
group + '.java7',
group + '.junit',
diff --git a/tools/maven-central/download.rb b/tools/maven-central/download.rb
index fc06f7d..de4ecd4 100755
--- a/tools/maven-central/download.rb
+++ b/tools/maven-central/download.rb
@@ -11,8 +11,7 @@ group = 'org.eclipse.jgit'
artifacts = [group,
group + '.ant',
group + '.archive',
- group + '.console',
- group + '.http.apache',
+ group + '.http.apache',
group + '.http.server',
group + '.java7',
group + '.junit',
diff --git a/tools/version.sh b/tools/version.sh
index a2f8129..81ffe06 100755
--- a/tools/version.sh
+++ b/tools/version.sh
@@ -149,17 +149,6 @@ perl -pi~ -e '
$seen_version = 0;
$old_argv = $ARGV;
}
- if ($seen_version < 5) {
- $seen_version++ if
- s{<(version)>.*</\1>}{<${1}>'"$POM_V"'</${1}>};
- }
- ' org.eclipse.jgit.packaging/org.eclipse.jgit.updatesite/pom.xml
-
-perl -pi~ -e '
- if ($ARGV ne $old_argv) {
- $seen_version = 0;
- $old_argv = $ARGV;
- }
if (!$seen_version) {
$seen_version = 1 if
s{<(version)>.*</\1>}{<${1}>'"$POM_V"'</${1}>};
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/eclipse-jgit.git
More information about the pkg-java-commits
mailing list