diff --git a/org.eclipse.jgit.java7/META-INF/MANIFEST.MF b/org.eclipse.jgit.java7/META-INF/MANIFEST.MF index f90cdfab1..911b00873 100644 --- a/org.eclipse.jgit.java7/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.java7/META-INF/MANIFEST.MF @@ -4,6 +4,7 @@ Fragment-Host: org.eclipse.jgit;bundle-version="3.0.0" Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit.java7 Bundle-Version: 3.0.0.qualifier +Bundle-Localization: plugin Bundle-Vendor: %provider_name Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Export-Package: org.eclipse.jgit.util;version="3.0.0" diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.properties index 442a76e55..44112e878 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.properties +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.feature/feature.properties @@ -8,7 +8,7 @@ # ############################################################################### -featureName=Eclipse JGit +featureName=Java implementation of Git providerName=Eclipse JGit updateSiteName=Eclipse JGit Update Site 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 index 438a5af5f..5d5c78597 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/feature.properties +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.java7.feature/feature.properties @@ -8,7 +8,7 @@ # ############################################################################### -featureName=Eclipse JGit - Java7 fragments +featureName=Java implementation of Git - optional Java 7 libraries providerName=Eclipse JGit updateSiteName=Eclipse JGit Update Site diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.properties index 324c653f9..7853e5578 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.properties +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.junit.feature/feature.properties @@ -9,7 +9,7 @@ # ############################################################################### -featureName=Eclipse JGit JUnit Test Support +featureName=JUnit test support for Java implementation of Git providerName=Eclipse JGit updateSiteName=Eclipse JGit Update Site diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.properties index 692123476..7b479f305 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.properties +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.feature/feature.properties @@ -8,7 +8,7 @@ # ############################################################################### -featureName=Eclipse JGit Command Line Interface +featureName=Command Line Interface for Java implementation of Git providerName=Eclipse JGit updateSiteName=Eclipse JGit Update Site diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.properties index 51c5f325e..8d4df4648 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.properties +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.pgm.source.feature/feature.properties @@ -8,7 +8,7 @@ # ############################################################################### -featureName=Eclipse JGit Command Line Interface - Source +featureName=Command Line Interface for Java implementation of Git - Source Code providerName=Eclipse JGit updateSiteName=Eclipse JGit Update Site diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.properties b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.properties index 8617d405d..dd12bc4b9 100644 --- a/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.properties +++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.source.feature/feature.properties @@ -8,7 +8,7 @@ # ############################################################################### -featureName=Eclipse JGit - Source +featureName=Java implementation of Git - Source Code providerName=Eclipse JGit updateSiteName=Eclipse JGit Update Site diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml index 40abdb835..81afb9f4d 100644 --- a/org.eclipse.jgit.packaging/pom.xml +++ b/org.eclipse.jgit.packaging/pom.xml @@ -64,7 +64,7 @@ 2.0.21.v201301150030 0.1.46.v201205102330 http://download.eclipse.org/releases/juno - http://download.eclipse.org/tools/orbit/downloads/drops/R20130118183705/repository/ + http://download.eclipse.org/tools/orbit/downloads/drops/R20130517111416/repository/ 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 742295192..31f909a5f 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 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, Chris Aniszczyk + * Copyright (C) 2011, 2013 Chris Aniszczyk * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -64,7 +64,6 @@ import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Ref; -import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevBlob; import org.eclipse.jgit.revwalk.RevCommit; @@ -92,11 +91,10 @@ public void setUp() throws Exception { writeTrashFile("Test.txt", "Hello world"); git.add().addFilepattern("Test.txt").call(); git.commit().setMessage("Initial commit").call(); + git.tag().setName("tag-initial").setMessage("Tag initial").call(); - // create a master branch and switch to it - git.branchCreate().setName("test").call(); - RefUpdate rup = db.updateRef(Constants.HEAD); - rup.link("refs/heads/test"); + // create a test branch and switch to it + git.checkout().setCreateBranch(true).setName("test").call(); // commit something on the test branch writeTrashFile("Test.txt", "Some change"); @@ -205,6 +203,36 @@ public void testCloneRepositoryWithBranch() throws IOException, .branchList().setListMode(ListMode.ALL).call())); } + @Test + public void testCloneRepositoryWithBranchShortName() throws Exception { + File directory = createTempDirectory("testCloneRepositoryWithBranch"); + CloneCommand command = Git.cloneRepository(); + command.setBranch("test"); + command.setDirectory(directory); + command.setURI("file://" + git.getRepository().getWorkTree().getPath()); + Git git2 = command.call(); + addRepoToClose(git2.getRepository()); + + assertNotNull(git2); + assertEquals("refs/heads/test", git2.getRepository().getFullBranch()); + } + + @Test + public void testCloneRepositoryWithTagName() throws Exception { + File directory = createTempDirectory("testCloneRepositoryWithBranch"); + CloneCommand command = Git.cloneRepository(); + command.setBranch("tag-initial"); + command.setDirectory(directory); + command.setURI("file://" + git.getRepository().getWorkTree().getPath()); + Git git2 = command.call(); + addRepoToClose(git2.getRepository()); + + assertNotNull(git2); + ObjectId taggedCommit = db.resolve("tag-initial^{commit}"); + assertEquals(taggedCommit.name(), git2 + .getRepository().getFullBranch()); + } + @Test public void testCloneRepositoryOnlyOneBranch() throws IOException, JGitInternalException, GitAPIException { 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 ba0847bd8..67e1879d3 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 @@ -326,6 +326,33 @@ public void testContentMerge() throws Exception { assertEquals(RepositoryState.MERGING, db.getRepositoryState()); } + @Test + public void testMergeTag() throws Exception { + Git git = new Git(db); + + writeTrashFile("a", "a"); + git.add().addFilepattern("a").call(); + RevCommit initialCommit = git.commit().setMessage("initial").call(); + + createBranch(initialCommit, "refs/heads/side"); + checkoutBranch("refs/heads/side"); + + writeTrashFile("b", "b"); + git.add().addFilepattern("b").call(); + RevCommit secondCommit = git.commit().setMessage("side").call(); + Ref tag = git.tag().setAnnotated(true).setMessage("my tag 01") + .setName("tag01").setObjectId(secondCommit).call(); + + checkoutBranch("refs/heads/master"); + + writeTrashFile("a", "a2"); + git.add().addFilepattern("a").call(); + git.commit().setMessage("main").call(); + + MergeResult result = git.merge().include(tag).setStrategy(MergeStrategy.RESOLVE).call(); + assertEquals(MergeStatus.MERGED, result.getMergeStatus()); + } + @Test public void testMergeMessage() throws Exception { Git git = new Git(db); 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 820c0c6d7..00e5f0674 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 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010, Google Inc. + * Copyright (C) 2010, 2013 Google Inc. * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -256,6 +256,35 @@ public void testCreateFileHeader_GitLink() throws Exception { assertEquals(0, hh.toEditList().size()); } + @Test + public void testCreateFileHeaderWithoutIndexLine() throws Exception { + DiffEntry m = DiffEntry.modify(PATH_A); + m.oldMode = FileMode.REGULAR_FILE; + m.newMode = FileMode.EXECUTABLE_FILE; + + FileHeader fh = df.toFileHeader(m); + String expected = DIFF + "a/src/a b/src/a\n" + // + "old mode 100644\n" + // + "new mode 100755\n"; + assertEquals(expected, fh.getScriptText()); + } + + @Test + public void testCreateFileHeaderForRenameWithoutContentChange() throws Exception { + DiffEntry a = DiffEntry.delete(PATH_A, ObjectId.zeroId()); + DiffEntry b = DiffEntry.add(PATH_B, ObjectId.zeroId()); + DiffEntry m = DiffEntry.pair(ChangeType.RENAME, a, b, 100); + m.oldId = null; + m.newId = null; + + FileHeader fh = df.toFileHeader(m); + String expected = DIFF + "a/src/a b/src/b\n" + // + "similarity index 100%\n" + // + "rename from src/a\n" + // + "rename to src/b\n"; + assertEquals(expected, fh.getScriptText()); + } + @Test public void testDiff() throws Exception { write(new File(db.getDirectory().getParent(), "test.txt"), "test"); 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 9bf3b94c4..a821e948e 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 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010, Google Inc. + * Copyright (C) 2010, 2013 Google Inc. * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -73,6 +73,7 @@ import org.eclipse.jgit.junit.TestRepository; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.BatchRefUpdate; +import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref.Storage; @@ -1191,8 +1192,7 @@ public void testBatchRefUpdateSimpleNoForce() throws IOException { ReceiveCommand.Type.UPDATE_NONFASTFORWARD)); BatchRefUpdate batchUpdate = refdir.newBatchUpdate(); batchUpdate.addCommand(commands); - batchUpdate - .execute(new RevWalk(diskRepo), NullProgressMonitor.INSTANCE); + batchUpdate.execute(new RevWalk(diskRepo), new StrictWorkMonitor()); Map refs = refdir.getRefs(RefDatabase.ALL); assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult()); assertEquals(ReceiveCommand.Result.REJECTED_NONFASTFORWARD, commands @@ -1215,8 +1215,7 @@ public void testBatchRefUpdateSimpleForce() throws IOException { BatchRefUpdate batchUpdate = refdir.newBatchUpdate(); batchUpdate.setAllowNonFastForwards(true); batchUpdate.addCommand(commands); - batchUpdate - .execute(new RevWalk(diskRepo), NullProgressMonitor.INSTANCE); + batchUpdate.execute(new RevWalk(diskRepo), new StrictWorkMonitor()); Map refs = refdir.getRefs(RefDatabase.ALL); assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult()); assertEquals(ReceiveCommand.Result.OK, commands.get(1).getResult()); @@ -1267,8 +1266,7 @@ public void testBatchRefUpdateConflictThanksToDelete() throws IOException { BatchRefUpdate batchUpdate = refdir.newBatchUpdate(); batchUpdate.setAllowNonFastForwards(true); batchUpdate.addCommand(commands); - batchUpdate - .execute(new RevWalk(diskRepo), NullProgressMonitor.INSTANCE); + batchUpdate.execute(new RevWalk(diskRepo), new StrictWorkMonitor()); Map refs = refdir.getRefs(RefDatabase.ALL); assertEquals(ReceiveCommand.Result.OK, commands.get(0).getResult()); assertEquals(ReceiveCommand.Result.OK, commands.get(1).getResult()); @@ -1311,4 +1309,29 @@ private void deleteLooseRef(String name) { File path = new File(diskRepo.getDirectory(), name); assertTrue("deleted " + name, path.delete()); } + + private static final class StrictWorkMonitor implements ProgressMonitor { + private int lastWork, totalWork; + + public void start(int totalTasks) { + // empty + } + + public void beginTask(String title, int totalWork) { + this.totalWork = totalWork; + lastWork = 0; + } + + public void update(int completed) { + lastWork += completed; + } + + public void endTask() { + assertEquals("Units of work recorded", totalWork, lastWork); + } + + public boolean isCancelled() { + return false; + } + } } 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 871741f69..3f5fcbbf0 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 @@ -1,6 +1,7 @@ /* * Copyright (C) 2008, Robin Rosenberg * Copyright (C) 2008, Shawn O. Pearce + * Copyright (C) 2013, Robin Stocker * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -46,6 +47,7 @@ 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.assertNull; import static org.junit.Assert.assertSame; @@ -294,4 +296,152 @@ public void testExpandFromDestination_Wildcard() { assertEquals(src, r.getSource()); assertEquals(dst, r.getDestination()); } + + @Test + public void isWildcardShouldWorkForWildcardSuffixAndComponent() { + assertTrue(RefSpec.isWildcard("refs/heads/*")); + assertTrue(RefSpec.isWildcard("refs/pull/*/head")); + assertFalse(RefSpec.isWildcard("refs/heads/a")); + } + + @Test + public void testWildcardInMiddleOfSource() { + RefSpec a = new RefSpec("+refs/pull/*/head:refs/remotes/origin/pr/*"); + assertTrue(a.isWildcard()); + assertTrue(a.matchSource("refs/pull/a/head")); + assertTrue(a.matchSource("refs/pull/foo/head")); + assertTrue(a.matchSource("refs/pull/foo/bar/head")); + assertFalse(a.matchSource("refs/pull/foo")); + assertFalse(a.matchSource("refs/pull/head")); + assertFalse(a.matchSource("refs/pull/foo/head/more")); + assertFalse(a.matchSource("refs/pullx/head")); + + RefSpec b = a.expandFromSource("refs/pull/foo/head"); + assertEquals("refs/remotes/origin/pr/foo", b.getDestination()); + RefSpec c = a.expandFromDestination("refs/remotes/origin/pr/foo"); + assertEquals("refs/pull/foo/head", c.getSource()); + } + + @Test + public void testWildcardInMiddleOfDestionation() { + RefSpec a = new RefSpec("+refs/heads/*:refs/remotes/origin/*/head"); + assertTrue(a.isWildcard()); + assertTrue(a.matchDestination("refs/remotes/origin/a/head")); + assertTrue(a.matchDestination("refs/remotes/origin/foo/head")); + assertTrue(a.matchDestination("refs/remotes/origin/foo/bar/head")); + assertFalse(a.matchDestination("refs/remotes/origin/foo")); + assertFalse(a.matchDestination("refs/remotes/origin/head")); + assertFalse(a.matchDestination("refs/remotes/origin/foo/head/more")); + assertFalse(a.matchDestination("refs/remotes/originx/head")); + + RefSpec b = a.expandFromSource("refs/heads/foo"); + assertEquals("refs/remotes/origin/foo/head", b.getDestination()); + RefSpec c = a.expandFromDestination("refs/remotes/origin/foo/head"); + assertEquals("refs/heads/foo", c.getSource()); + } + + @Test + public void testWildcardMirror() { + RefSpec a = new RefSpec("*:*"); + assertTrue(a.isWildcard()); + assertTrue(a.matchSource("a")); + assertTrue(a.matchSource("foo")); + assertTrue(a.matchSource("foo/bar")); + assertTrue(a.matchDestination("a")); + assertTrue(a.matchDestination("foo")); + assertTrue(a.matchDestination("foo/bar")); + + RefSpec b = a.expandFromSource("refs/heads/foo"); + assertEquals("refs/heads/foo", b.getDestination()); + RefSpec c = a.expandFromDestination("refs/heads/foo"); + assertEquals("refs/heads/foo", c.getSource()); + } + + @Test + public void testWildcardAtStart() { + RefSpec a = new RefSpec("*/head:refs/heads/*"); + assertTrue(a.isWildcard()); + assertTrue(a.matchSource("a/head")); + assertTrue(a.matchSource("foo/head")); + assertTrue(a.matchSource("foo/bar/head")); + assertFalse(a.matchSource("/head")); + assertFalse(a.matchSource("a/head/extra")); + + RefSpec b = a.expandFromSource("foo/head"); + assertEquals("refs/heads/foo", b.getDestination()); + RefSpec c = a.expandFromDestination("refs/heads/foo"); + assertEquals("foo/head", c.getSource()); + } + + @Test(expected = IllegalArgumentException.class) + public void invalidWhenSourceOnlyAndWildcard() { + assertNotNull(new RefSpec("refs/heads/*")); + } + + @Test(expected = IllegalArgumentException.class) + public void invalidWhenDestinationOnlyAndWildcard() { + assertNotNull(new RefSpec(":refs/heads/*")); + } + + @Test(expected = IllegalArgumentException.class) + public void invalidWhenOnlySourceWildcard() { + assertNotNull(new RefSpec("refs/heads/*:refs/heads/foo")); + } + + @Test(expected = IllegalArgumentException.class) + public void invalidWhenOnlyDestinationWildcard() { + assertNotNull(new RefSpec("refs/heads/foo:refs/heads/*")); + } + + @Test(expected = IllegalArgumentException.class) + public void invalidWhenMoreThanOneWildcardInSource() { + assertNotNull(new RefSpec("refs/heads/*/*:refs/heads/*")); + } + + @Test(expected = IllegalArgumentException.class) + public void invalidWhenMoreThanOneWildcardInDestination() { + assertNotNull(new RefSpec("refs/heads/*:refs/heads/*/*")); + } + + @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")); + } + + @Test(expected = IllegalArgumentException.class) + public void invalidSlashAtStart() { + assertNotNull(new RefSpec("/foo:/foo")); + } + + @Test(expected = IllegalArgumentException.class) + public void invalidDestinationDoubleSlashes() { + assertNotNull(new RefSpec(":refs/heads//wrong")); + } + + @Test(expected = IllegalArgumentException.class) + public void invalidSetSource() { + RefSpec a = new RefSpec("refs/heads/*:refs/remotes/origin/*"); + a.setSource("refs/heads/*/*"); + } + + @Test(expected = IllegalArgumentException.class) + public void invalidSetDestination() { + RefSpec a = new RefSpec("refs/heads/*:refs/remotes/origin/*"); + a.setDestination("refs/remotes/origin/*/*"); + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawSubStringPatternTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawSubStringPatternTest.java new file mode 100644 index 000000000..194fab47f --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawSubStringPatternTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2013, Robin Stocker + * 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 static org.junit.Assert.assertNotNull; + +import java.io.UnsupportedEncodingException; + +import org.eclipse.jgit.junit.RepositoryTestCase; +import org.junit.Test; + +public class RawSubStringPatternTest extends RepositoryTestCase { + + @Test + public void testBoundary() { + assertMatchResult("a", "a", 0); + assertMatchResult("a", "abcd", 0); + assertMatchResult("ab", "abcd", 0); + assertMatchResult("abcd", "abcd", 0); + assertMatchResult("bc", "abcd", 1); + assertMatchResult("bcd", "abcd", 1); + assertMatchResult("cd", "abcd", 2); + assertMatchResult("d", "abcd", 3); + assertMatchResult("abab", "abaabab", 3); + } + + @Test + public void testNoMatches() { + assertMatchResult("a", "", -1); + assertMatchResult("a", "b", -1); + assertMatchResult("abab", "abaaba", -1); + assertMatchResult("ab", "ddda", -1); + } + + @Test + public void testCaseInsensitive() { + assertMatchResult("a", "A", 0); + assertMatchResult("A", "a", 0); + assertMatchResult("Ab", "aB", 0); + assertMatchResult("aB", "Ab", 0); + } + + @Test(expected = IllegalArgumentException.class) + public void testEmptyPattern() { + assertNotNull(new RawSubStringPattern("")); + } + + private static void assertMatchResult(String pattern, String input, int position) { + RawSubStringPattern p = new RawSubStringPattern(pattern); + assertEquals("Expected match result " + position + " with input " + + input + " for pattern " + pattern, + position, p.match(raw(input))); + } + + private static RawCharSequence raw(String text) { + try { + byte[] bytes = text.getBytes("UTF-8"); + return new RawCharSequence(bytes, 0, bytes.length); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } +} 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 0920c43e8..706dce7ce 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -274,6 +274,7 @@ invalidTagOption=Invalid tag option: {0} invalidTimeout=Invalid timeout: {0} invalidURL=Invalid URL {0} invalidWildcards=Invalid wildcards {0} +invalidRefSpec=Invalid refspec {0} invalidWindowSize=Invalid window size isAStaticFlagAndHasNorevWalkInstance={0} is a static flag and has no RevWalk instance JRELacksMD5Implementation=JRE lacks MD5 implementation 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 65af216ae..645d3e781 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, Chris Aniszczyk + * Copyright (C) 2011, 2013 Chris Aniszczyk * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -198,12 +198,19 @@ private void checkout(Repository clonedRepo, FetchResult result) throws MissingObjectException, IncorrectObjectTypeException, IOException, GitAPIException { - Ref head = result.getAdvertisedRef(branch); + Ref head = null; if (branch.equals(Constants.HEAD)) { Ref foundBranch = findBranchToCheckout(result); if (foundBranch != null) head = foundBranch; } + if (head == null) { + head = result.getAdvertisedRef(branch); + if (head == null) + head = result.getAdvertisedRef(Constants.R_HEADS + branch); + if (head == null) + head = result.getAdvertisedRef(Constants.R_TAGS + branch); + } if (head == null || head.getObjectId() == null) return; // throw exception? @@ -362,7 +369,9 @@ public CloneCommand setRemote(String remote) { /** * @param branch - * the initial branch to check out when cloning the repository + * the initial branch to check out when cloning the repository. + * Can be specified as ref name (refs/heads/master), + * branch name (master) or tag name (v1.2.3). * @return this instance */ public CloneCommand setBranch(String branch) { @@ -409,7 +418,8 @@ public CloneCommand setCloneSubmodules(boolean cloneSubmodules) { /** * @param branchesToClone * collection of branches to clone. Ignored when allSelected is - * true. + * true. Must be specified as full ref names (e.g. + * refs/heads/master). * @return {@code this} */ public CloneCommand setBranchesToClone(Collection branchesToClone) { 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 1f68a7df2..162105b78 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java @@ -77,6 +77,8 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryState; import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevObject; +import org.eclipse.jgit.revwalk.RevTag; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.treewalk.CanonicalTreeParser; import org.eclipse.jgit.treewalk.FileTreeIterator; @@ -150,13 +152,16 @@ public RevCommit call() throws GitAPIException, NoHeadException, checkCallable(); Collections.sort(only); - RepositoryState state = repo.getRepositoryState(); - if (!state.canCommit()) - throw new WrongRepositoryStateException(MessageFormat.format( - JGitText.get().cannotCommitOnARepoWithState, state.name())); - processOptions(state); + RevWalk rw = new RevWalk(repo); try { + RepositoryState state = repo.getRepositoryState(); + if (!state.canCommit()) + throw new WrongRepositoryStateException(MessageFormat.format( + JGitText.get().cannotCommitOnARepoWithState, + state.name())); + processOptions(state, rw); + if (all && !repo.isBare() && repo.getWorkTree() != null) { Git git = new Git(repo); try { @@ -182,8 +187,7 @@ public RevCommit call() throws GitAPIException, NoHeadException, if (headId != null) if (amend) { - RevCommit previousCommit = new RevWalk(repo) - .parseCommit(headId); + RevCommit previousCommit = rw.parseCommit(headId); for (RevCommit p : previousCommit.getParents()) parents.add(p.getId()); if (author == null) @@ -196,7 +200,7 @@ public RevCommit call() throws GitAPIException, NoHeadException, DirCache index = repo.lockDirCache(); try { if (!only.isEmpty()) - index = createTemporaryIndex(headId, index); + index = createTemporaryIndex(headId, index, rw); ObjectInserter odi = repo.newObjectInserter(); try { @@ -219,56 +223,51 @@ public RevCommit call() throws GitAPIException, NoHeadException, ObjectId commitId = odi.insert(commit); odi.flush(); - RevWalk revWalk = new RevWalk(repo); - try { - RevCommit revCommit = revWalk.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): " - : "commit: "; - ru.setRefLogMessage( - prefix + revCommit.getShortMessage(), false); + 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): " + : "commit: "; + 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) { + // 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); } - 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) { - // 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; - } - 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 { - revWalk.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 { odi.release(); @@ -281,6 +280,8 @@ public RevCommit call() throws GitAPIException, NoHeadException, } catch (IOException e) { throw new JGitInternalException( JGitText.get().exceptionCaughtDuringExecutionOfCommitCommand, e); + } finally { + rw.dispose(); } } @@ -297,7 +298,8 @@ private void insertChangeId(ObjectId treeId) throws IOException { + changeId.getName() + "\n"); //$NON-NLS-1$ } - private DirCache createTemporaryIndex(ObjectId headId, DirCache index) + private DirCache createTemporaryIndex(ObjectId headId, DirCache index, + RevWalk rw) throws IOException { ObjectInserter inserter = null; @@ -317,7 +319,7 @@ private DirCache createTemporaryIndex(ObjectId headId, DirCache index) int fIdx = treeWalk.addTree(new FileTreeIterator(repo)); int hIdx = -1; if (headId != null) - hIdx = treeWalk.addTree(new RevWalk(repo).parseTree(headId)); + hIdx = treeWalk.addTree(rw.parseTree(headId)); treeWalk.setRecursive(true); String lastAddedFile = null; @@ -473,11 +475,14 @@ private int lookupOnly(String pathString) { * * @param state * the state of the repository we are working on + * @param rw + * the RevWalk to use * * @throws NoMessageException * if the commit message has not been specified */ - private void processOptions(RepositoryState state) throws NoMessageException { + private void processOptions(RepositoryState state, RevWalk rw) + throws NoMessageException { if (committer == null) committer = new PersonIdent(repo); if (author == null && !amend) @@ -487,6 +492,12 @@ private void processOptions(RepositoryState state) throws NoMessageException { if (state == RepositoryState.MERGING_RESOLVED) { try { parents = repo.readMergeHeads(); + if (parents != null) + for (int i = 0; i < parents.size(); i++) { + RevObject ro = rw.parseAny(parents.get(i)); + if (ro instanceof RevTag) + parents.set(i, rw.peel(ro)); + } } catch (IOException e) { throw new JGitInternalException(MessageFormat.format( JGitText.get().exceptionOccurredDuringReadingOfGIT_DIR, diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java index a3d4e09d7..06b2aec06 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java @@ -308,6 +308,8 @@ static DiffEntry pair(ChangeType changeType, DiffEntry src, DiffEntry dst, r.changeType = changeType; r.score = score; + r.treeFilterMarks = src.treeFilterMarks | dst.treeFilterMarks; + return r; } 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 11848e2c9..f660d6bbd 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java @@ -912,6 +912,11 @@ private FormatResult createFormatResult(DiffEntry ent) throws IOException, editList = new EditList(); type = PatchType.UNIFIED; + } else if (ent.getOldId() == null || ent.getNewId() == null) { + // Content not changed (e.g. only mode, pure rename) + editList = new EditList(); + type = PatchType.UNIFIED; + } else { assertHaveRepository(); @@ -1106,7 +1111,7 @@ private void formatHeader(ByteArrayOutputStream o, DiffEntry ent) o.write('\n'); } - if (!ent.getOldId().equals(ent.getNewId())) { + if (ent.getOldId() != null && !ent.getOldId().equals(ent.getNewId())) { formatIndexLine(o, ent); } } 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 7280a3851..8ac971ab6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -336,6 +336,7 @@ public static JGitText get() { /***/ public String invalidTimeout; /***/ public String invalidURL; /***/ public String invalidWildcards; + /***/ public String invalidRefSpec; /***/ public String invalidWindowSize; /***/ public String isAStaticFlagAndHasNorevWalkInstance; /***/ public String JRELacksMD5Implementation; 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 ccf35e896..75307decb 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 @@ -615,26 +615,25 @@ private Set listNonHEADIndexObjects() while (treeWalk.next()) { ObjectId objectId = treeWalk.getObjectId(0); - switch (treeWalk.getRawMode(0) & FileMode.TYPE_MASK) { - case FileMode.TYPE_MISSING: - case FileMode.TYPE_GITLINK: - continue; - case FileMode.TYPE_TREE: - case FileMode.TYPE_FILE: - case FileMode.TYPE_SYMLINK: - ret.add(objectId); - continue; - default: + switch (treeWalk.getRawMode(0) & FileMode.TYPE_MASK) { + case FileMode.TYPE_MISSING: + case FileMode.TYPE_GITLINK: + continue; + case FileMode.TYPE_TREE: + case FileMode.TYPE_FILE: + case FileMode.TYPE_SYMLINK: + ret.add(objectId); + continue; + default: throw new IOException(MessageFormat.format( - JGitText.get().corruptObjectInvalidMode3, String - .format("%o", Integer.valueOf(treeWalk //$NON-NLS-1$ - .getRawMode(0)), - (objectId == null) ? "null" //$NON-NLS-1$ - : objectId.name(), treeWalk - .getPathString(), repo - .getIndexFile()))); - } - } + JGitText.get().corruptObjectInvalidMode3, + String.format("%o", //$NON-NLS-1$ + Integer.valueOf(treeWalk.getRawMode(0))), + (objectId == null) ? "null" : objectId.name(), //$NON-NLS-1$ + treeWalk.getPathString(), // + repo.getIndexFile())); + } + } return ret; } finally { if (revWalk != null) @@ -703,7 +702,6 @@ public int compare(PackExt o1, PackExt o2) { } // write the packindex - @SuppressWarnings("resource") FileChannel idxChannel = new FileOutputStream(tmpIdx).getChannel(); OutputStream idxStream = Channels.newOutputStream(idxChannel); try { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java index e408c79a3..9db4a61c6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java @@ -57,7 +57,7 @@ * with this instance can alter at any time, if this instance is modified to * represent a different object name. */ -public abstract class AnyObjectId implements Comparable { +public abstract class AnyObjectId implements Comparable { /** * Compare to object identifier byte sequences for equality. @@ -184,10 +184,6 @@ public final int compareTo(final AnyObjectId other) { return NB.compareUInt32(w5, other.w5); } - public final int compareTo(final Object other) { - return compareTo(((AnyObjectId) other)); - } - /** * Compare this ObjectId to a network-byte-order ObjectId. * 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 b86d6fad8..b369d0d1f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchRefUpdate.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchRefUpdate.java @@ -294,8 +294,6 @@ public void execute(RevWalk walk, ProgressMonitor monitor) // Now to the update that may require more room in the name space for (ReceiveCommand cmd : commands2) { try { - monitor.update(1); - if (cmd.getResult() == NOT_ATTEMPTED) { cmd.updateType(walk); RefUpdate ru = newUpdate(cmd); @@ -305,7 +303,6 @@ public void execute(RevWalk walk, ProgressMonitor monitor) break; case UPDATE: case UPDATE_NONFASTFORWARD: - monitor.update(1); RefUpdate ruu = newUpdate(cmd); cmd.setResult(ruu.update(walk)); break; @@ -329,6 +326,8 @@ public void execute(RevWalk walk, ProgressMonitor monitor) } catch (IOException err) { cmd.setResult(REJECTED_OTHER_REASON, MessageFormat.format( JGitText.get().lockError, err.getMessage())); + } finally { + monitor.update(1); } } } 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 9b38b2933..66ffc3abe 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java @@ -43,8 +43,8 @@ package org.eclipse.jgit.transport; -import java.text.MessageFormat; import java.io.Serializable; +import java.text.MessageFormat; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.Constants; @@ -59,7 +59,7 @@ public class RefSpec implements Serializable { private static final long serialVersionUID = 1L; - /** + /** * Suffix for wildcard ref spec component, that indicate matching all refs * with specified prefix. */ @@ -73,7 +73,7 @@ public class RefSpec implements Serializable { * @return true if provided string is a wildcard ref spec component. */ public static boolean isWildcard(final String s) { - return s != null && s.endsWith(WILDCARD_SUFFIX); + return s != null && s.contains("*"); //$NON-NLS-1$ } /** Does this specification ask for forced updated (rewind/reset)? */ @@ -112,6 +112,7 @@ public RefSpec() { *
  • +refs/heads/master
  • *
  • +refs/heads/master:refs/remotes/origin/master
  • *
  • +refs/heads/*:refs/remotes/origin/*
  • + *
  • +refs/pull/*/head:refs/remotes/origin/pr/*
  • *
  • :refs/heads/master
  • * * @@ -132,18 +133,24 @@ public RefSpec(final String spec) { s = s.substring(1); if (isWildcard(s)) throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidWildcards, spec)); - dstName = s; + dstName = checkValid(s); } else if (c > 0) { - srcName = s.substring(0, c); - dstName = s.substring(c + 1); - if (isWildcard(srcName) && isWildcard(dstName)) + String src = s.substring(0, c); + String dst = s.substring(c + 1); + if (isWildcard(src) && isWildcard(dst)) { + // Both contain wildcard wildcard = true; - else if (isWildcard(srcName) || isWildcard(dstName)) + } else if (isWildcard(src) || isWildcard(dst)) { + // If either source or destination has wildcard, the other one + // must have as well. throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidWildcards, spec)); + } + srcName = checkValid(src); + dstName = checkValid(dst); } else { if (isWildcard(s)) throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidWildcards, spec)); - srcName = s; + srcName = checkValid(s); } } @@ -215,7 +222,7 @@ public String getSource() { */ public RefSpec setSource(final String source) { final RefSpec r = new RefSpec(this); - r.srcName = source; + r.srcName = checkValid(source); if (isWildcard(r.srcName) && r.dstName == null) throw new IllegalStateException(JGitText.get().destinationIsNotAWildcard); if (isWildcard(r.srcName) != isWildcard(r.dstName)) @@ -254,7 +261,7 @@ public String getDestination() { */ public RefSpec setDestination(final String destination) { final RefSpec r = new RefSpec(this); - r.dstName = destination; + r.dstName = checkValid(destination); if (isWildcard(r.dstName) && r.srcName == null) throw new IllegalStateException(JGitText.get().sourceIsNotAWildcard); if (isWildcard(r.srcName) != isWildcard(r.dstName)) @@ -350,8 +357,7 @@ private RefSpec expandFromSourceImp(final String name) { final String psrc = srcName, pdst = dstName; wildcard = false; srcName = name; - dstName = pdst.substring(0, pdst.length() - 1) - + name.substring(psrc.length() - 1); + dstName = expandWildcard(name, psrc, pdst); return this; } @@ -392,8 +398,7 @@ public RefSpec expandFromDestination(final String r) { private RefSpec expandFromDstImp(final String name) { final String psrc = srcName, pdst = dstName; wildcard = false; - srcName = psrc.substring(0, psrc.length() - 1) - + name.substring(pdst.length() - 1); + srcName = expandWildcard(name, pdst, psrc); dstName = name; return this; } @@ -414,12 +419,50 @@ public RefSpec expandFromDestination(final Ref r) { return expandFromDestination(r.getName()); } - private boolean match(final String refName, final String s) { + private boolean match(final String name, final String s) { if (s == null) return false; - if (isWildcard()) - return refName.startsWith(s.substring(0, s.length() - 1)); - return refName.equals(s); + if (isWildcard()) { + int wildcardIndex = s.indexOf('*'); + String prefix = s.substring(0, wildcardIndex); + String suffix = s.substring(wildcardIndex + 1); + return name.length() > prefix.length() + suffix.length() + && name.startsWith(prefix) && name.endsWith(suffix); + } + return name.equals(s); + } + + private static String expandWildcard(String name, String patternA, + String patternB) { + int a = patternA.indexOf('*'); + int trailingA = patternA.length() - (a + 1); + int b = patternB.indexOf('*'); + String match = name.substring(a, name.length() - trailingA); + return patternB.substring(0, b) + match + patternB.substring(b + 1); + } + + private static String checkValid(String spec) { + if (spec != null && !isValid(spec)) + throw new IllegalArgumentException(MessageFormat.format( + JGitText.get().invalidRefSpec, spec)); + return spec; + } + + private static boolean isValid(final String s) { + if (s.startsWith("/")) //$NON-NLS-1$ + return false; + if (s.contains("//")) //$NON-NLS-1$ + return false; + int i = s.indexOf('*'); + 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; } public int hashCode() { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawSubStringPattern.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawSubStringPattern.java index 2f7f486e9..bc101bd7d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawSubStringPattern.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawSubStringPattern.java @@ -95,21 +95,21 @@ public int match(final RawCharSequence rcs) { int matchPos = rcs.startPtr; final int maxPos = rcs.endPtr - needleLen; - OUTER: for (; matchPos < maxPos; matchPos++) { + OUTER: for (; matchPos <= maxPos; matchPos++) { if (neq(first, text[matchPos])) { - while (++matchPos < maxPos && neq(first, text[matchPos])) { + while (++matchPos <= maxPos && neq(first, text[matchPos])) { /* skip */ } - if (matchPos == maxPos) + if (matchPos > maxPos) return -1; } - int si = ++matchPos; + int si = matchPos + 1; for (int j = 1; j < needleLen; j++, si++) { if (neq(needle[j], text[si])) continue OUTER; } - return matchPos - 1; + return matchPos; } return -1; }