From d35838624892778f5ac94ef1659b9d74863978c9 Mon Sep 17 00:00:00 2001 From: Marc Strapetz Date: Fri, 11 Mar 2016 11:00:52 +0100 Subject: [PATCH 1/4] Fix: Mode.LINK support --- src/main/java/org/libgit2/jagged/Mode.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/libgit2/jagged/Mode.java b/src/main/java/org/libgit2/jagged/Mode.java index fe032c2..da1a12c 100644 --- a/src/main/java/org/libgit2/jagged/Mode.java +++ b/src/main/java/org/libgit2/jagged/Mode.java @@ -10,7 +10,10 @@ public enum Mode FILE(0100644), /** A file with the executable bit set */ - EXECUTABLE_FILE(0100755); + EXECUTABLE_FILE(0100755), + + /** A symlink */ + LINK(0120000); private final int value; From 74b9f51567ffe645938acdef760cd2374fa54aee Mon Sep 17 00:00:00 2001 From: Marc Strapetz Date: Fri, 11 Mar 2016 10:59:57 +0100 Subject: [PATCH 2/4] Reuse libgit2 test repositories --- .../java/org/libgit2/jagged/BlobTest.java | 20 ++++--- .../java/org/libgit2/jagged/CommitTest.java | 22 +++---- src/test/java/org/libgit2/jagged/GitTest.java | 35 +++++++++-- .../org/libgit2/jagged/ReferenceTest.java | 16 ++++- .../java/org/libgit2/jagged/TreeTest.java | 55 ++++++++---------- src/test/resources/testrepo/.gitattributes | 1 - src/test/resources/testrepo/.gitted/HEAD | 1 - src/test/resources/testrepo/.gitted/config | 6 -- .../resources/testrepo/.gitted/description | 1 - src/test/resources/testrepo/.gitted/index | Bin 361 -> 0 bytes .../resources/testrepo/.gitted/info/exclude | 6 -- src/test/resources/testrepo/.gitted/logs/HEAD | 3 - .../testrepo/.gitted/logs/refs/heads/master | 3 - .../05/5fe18dd1aef07991ebd08b4d54fc761dd022fb | Bin 138 -> 0 bytes .../17/6a458f94e0ea5272ce67c36bf30b6be9caf623 | Bin 28 -> 0 bytes .../2d/08ab4853a55488a4e3323ebf804c3a732a2f6d | Bin 33 -> 0 bytes .../57/cf500670b83b1ad4d86db6436bb91531cf3e27 | 1 - .../5e/ab02d63a3676df528bcd878ac935ec0c4d5bdc | Bin 162 -> 0 bytes .../5f/a5363e9ca45dd8fbf2be90a6898f40332f72b9 | 2 - .../8f/be49af0d14c65f881b57709acae2ea3414089a | Bin 34 -> 0 bytes .../96/a82bbe56b718c4c452491014c91d9b63ab8a79 | Bin 33 -> 0 bytes .../c1/a317a9997907e9245580b6bba9ede7874a5967 | Bin 147 -> 0 bytes .../d1/796967d47949153bb852c07304d9e5f2f0040c | Bin 32 -> 0 bytes .../d4/bcc68acd4410bf836a39f20afb2c2ece09584e | Bin 35 -> 0 bytes .../dc/48b6c38e967e57965e36c6f7a1c3ec5c3e1ff4 | Bin 32 -> 0 bytes .../e7/7ab1c63f3fbde9c5ef9972939aa0717012d7c0 | Bin 109 -> 0 bytes .../testrepo/.gitted/refs/heads/master | 1 - src/test/resources/testrepo/one.txt | 1 - src/test/resources/testrepo/three.txt | 1 - src/test/resources/testrepo/two.txt | 1 - 30 files changed, 91 insertions(+), 85 deletions(-) delete mode 100644 src/test/resources/testrepo/.gitattributes delete mode 100644 src/test/resources/testrepo/.gitted/HEAD delete mode 100644 src/test/resources/testrepo/.gitted/config delete mode 100644 src/test/resources/testrepo/.gitted/description delete mode 100644 src/test/resources/testrepo/.gitted/index delete mode 100644 src/test/resources/testrepo/.gitted/info/exclude delete mode 100644 src/test/resources/testrepo/.gitted/logs/HEAD delete mode 100644 src/test/resources/testrepo/.gitted/logs/refs/heads/master delete mode 100644 src/test/resources/testrepo/.gitted/objects/05/5fe18dd1aef07991ebd08b4d54fc761dd022fb delete mode 100644 src/test/resources/testrepo/.gitted/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623 delete mode 100644 src/test/resources/testrepo/.gitted/objects/2d/08ab4853a55488a4e3323ebf804c3a732a2f6d delete mode 100644 src/test/resources/testrepo/.gitted/objects/57/cf500670b83b1ad4d86db6436bb91531cf3e27 delete mode 100644 src/test/resources/testrepo/.gitted/objects/5e/ab02d63a3676df528bcd878ac935ec0c4d5bdc delete mode 100644 src/test/resources/testrepo/.gitted/objects/5f/a5363e9ca45dd8fbf2be90a6898f40332f72b9 delete mode 100644 src/test/resources/testrepo/.gitted/objects/8f/be49af0d14c65f881b57709acae2ea3414089a delete mode 100644 src/test/resources/testrepo/.gitted/objects/96/a82bbe56b718c4c452491014c91d9b63ab8a79 delete mode 100644 src/test/resources/testrepo/.gitted/objects/c1/a317a9997907e9245580b6bba9ede7874a5967 delete mode 100644 src/test/resources/testrepo/.gitted/objects/d1/796967d47949153bb852c07304d9e5f2f0040c delete mode 100644 src/test/resources/testrepo/.gitted/objects/d4/bcc68acd4410bf836a39f20afb2c2ece09584e delete mode 100644 src/test/resources/testrepo/.gitted/objects/dc/48b6c38e967e57965e36c6f7a1c3ec5c3e1ff4 delete mode 100644 src/test/resources/testrepo/.gitted/objects/e7/7ab1c63f3fbde9c5ef9972939aa0717012d7c0 delete mode 100644 src/test/resources/testrepo/.gitted/refs/heads/master delete mode 100644 src/test/resources/testrepo/one.txt delete mode 100644 src/test/resources/testrepo/three.txt delete mode 100644 src/test/resources/testrepo/two.txt diff --git a/src/test/java/org/libgit2/jagged/BlobTest.java b/src/test/java/org/libgit2/jagged/BlobTest.java index 1de8744..5b833fb 100644 --- a/src/test/java/org/libgit2/jagged/BlobTest.java +++ b/src/test/java/org/libgit2/jagged/BlobTest.java @@ -18,7 +18,7 @@ public void testLookupBlob() final File repoPath = setupRepository("testrepo"); Repository repository = new Repository(repoPath.getAbsolutePath()); - ObjectId oid = new ObjectId("dc48b6c38e967e57965e36c6f7a1c3ec5c3e1ff4"); + ObjectId oid = new ObjectId(C1_README); Blob blob = repository.lookup(oid); Assert.assertEquals(oid, blob.getId()); @@ -32,10 +32,10 @@ public void testGetBlobMetadata() final File repoPath = setupRepository("testrepo"); Repository repository = new Repository(repoPath.getAbsolutePath()); - ObjectId oid = new ObjectId("dc48b6c38e967e57965e36c6f7a1c3ec5c3e1ff4"); + ObjectId oid = new ObjectId(C1_README); Blob blob = repository.lookup(oid); - Assert.assertEquals(18, blob.getSize()); + Assert.assertEquals(4, blob.getSize()); Assert.assertEquals(false, blob.isBinary()); repository.close(); @@ -45,12 +45,14 @@ public void testGetBlobMetadata() public void testGetRawContent() throws IOException { - byte[] expected = "This is file two!\n".getBytes(); + byte[] expected = "hey\n".getBytes(); final File repoPath = setupRepository("testrepo"); + write(new File(repoPath, ".gitattributes"), "* text"); + Repository repository = new Repository(repoPath.getAbsolutePath()); - ObjectId oid = new ObjectId("dc48b6c38e967e57965e36c6f7a1c3ec5c3e1ff4"); + ObjectId oid = new ObjectId(C1_README); Blob blob = repository.lookup(oid); InputStream contentStream = blob.getContentStream(); @@ -79,17 +81,19 @@ public void testGetFilteredContent() if (Platform.getCurrentPlatform().getOperatingSystem().equals(OperatingSystem.WINDOWS)) { - expected = "This is file two!\r\n".getBytes(); + expected = "hey\r\n".getBytes(); } else { - expected = "This is file two!\n".getBytes(); + expected = "hey\n".getBytes(); } final File repoPath = setupRepository("testrepo"); + write(new File(repoPath, ".gitattributes"), "* text"); + Repository repository = new Repository(repoPath.getAbsolutePath()); - ObjectId oid = new ObjectId("dc48b6c38e967e57965e36c6f7a1c3ec5c3e1ff4"); + ObjectId oid = new ObjectId(C1_README); Blob blob = repository.lookup(oid); InputStream contentStream = blob.getContentStream(new FilteringOptions("two.txt")); diff --git a/src/test/java/org/libgit2/jagged/CommitTest.java b/src/test/java/org/libgit2/jagged/CommitTest.java index c703ad3..12c39c6 100644 --- a/src/test/java/org/libgit2/jagged/CommitTest.java +++ b/src/test/java/org/libgit2/jagged/CommitTest.java @@ -16,7 +16,7 @@ public void testLookupCommit() final File repoPath = setupRepository("testrepo"); Repository repository = new Repository(repoPath.getAbsolutePath()); - ObjectId oid = new ObjectId("055fe18dd1aef07991ebd08b4d54fc761dd022fb"); + ObjectId oid = new ObjectId(C1); Commit commit = repository.lookup(oid); Assert.assertEquals(oid, commit.getId()); @@ -30,11 +30,11 @@ public void testGetCommitter() final File repoPath = setupRepository("testrepo"); Repository repository = new Repository(repoPath.getAbsolutePath()); - ObjectId oid = new ObjectId("055fe18dd1aef07991ebd08b4d54fc761dd022fb"); + ObjectId oid = new ObjectId(C1); Commit commit = repository.lookup(oid); - Assert.assertEquals("Edward Thomson", commit.getCommitter().getName()); - Assert.assertEquals("ethomson@microsoft.com", commit.getCommitter().getEmail()); + Assert.assertEquals("Scott Chacon", commit.getCommitter().getName()); + Assert.assertEquals("schacon@gmail.com", commit.getCommitter().getEmail()); repository.close(); } @@ -45,11 +45,11 @@ public void testGetAuthor() final File repoPath = setupRepository("testrepo"); Repository repository = new Repository(repoPath.getAbsolutePath()); - ObjectId oid = new ObjectId("5eab02d63a3676df528bcd878ac935ec0c4d5bdc"); + ObjectId oid = new ObjectId(C2); Commit commit = repository.lookup(oid); - Assert.assertEquals("Edward Thomson", commit.getAuthor().getName()); - Assert.assertEquals("ethomson@microsoft.com", commit.getAuthor().getEmail()); + Assert.assertEquals("Scott Chacon", commit.getAuthor().getName()); + Assert.assertEquals("schacon@gmail.com", commit.getAuthor().getEmail()); repository.close(); } @@ -60,12 +60,12 @@ public void testGetParents() final File repoPath = setupRepository("testrepo"); Repository repository = new Repository(repoPath.getAbsolutePath()); - ObjectId parentOid = new ObjectId("055fe18dd1aef07991ebd08b4d54fc761dd022fb"); + ObjectId parentOid = new ObjectId(C1); Commit parent = repository.lookup(parentOid); Assert.assertEquals(false, parent.getParents().iterator().hasNext()); - ObjectId childOid = new ObjectId("5eab02d63a3676df528bcd878ac935ec0c4d5bdc"); + ObjectId childOid = new ObjectId(C2); Commit child = repository.lookup(childOid); Iterator parents = child.getParents().iterator(); @@ -82,10 +82,10 @@ public void testGetTree() final File repoPath = setupRepository("testrepo"); Repository repository = new Repository(repoPath.getAbsolutePath()); - ObjectId oid = new ObjectId("055fe18dd1aef07991ebd08b4d54fc761dd022fb"); + ObjectId oid = new ObjectId(C1); Commit commit = repository.lookup(oid); - Tree tree = repository.lookup(new ObjectId("e77ab1c63f3fbde9c5ef9972939aa0717012d7c0")); + Tree tree = repository.lookup(new ObjectId(C1_ROOT)); Assert.assertEquals(tree, commit.getTree()); diff --git a/src/test/java/org/libgit2/jagged/GitTest.java b/src/test/java/org/libgit2/jagged/GitTest.java index 816ebb8..fc9880d 100644 --- a/src/test/java/org/libgit2/jagged/GitTest.java +++ b/src/test/java/org/libgit2/jagged/GitTest.java @@ -3,6 +3,8 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; import java.nio.channels.FileChannel; import java.text.MessageFormat; @@ -13,7 +15,17 @@ public abstract class GitTest { - private static File resourcesRoot; + public static String README = "README"; + public static String C1 = "8496071c1b46c854b31185ea97743be6a8774479"; + public static String C1_ROOT = "181037049a54a1eb5fab404658a3a250b44335d7"; + public static String C1_README = "1385f264afb75a56a5bec74243be9b367ba4ca08"; + + public static String C2 = "5b5b025afb0b4c913b4c338a42934a3863bf3644"; + + public static String C8_ROOT = "45dd856fdd4d89b884c340ba0e047752d9b085d6"; + public static String C8_README = "a8233120f6ad708f843d861ce2b7228ec4e3dec6"; + + private static File testRepositoriesRoot; private static File tempRoot; private static File tempDir; private static File tempConfigurationDir; @@ -24,11 +36,10 @@ public abstract class GitTest { File systemTempDir; - resourcesRoot = new File("src/test/resources"); - - if (!resourcesRoot.exists()) + testRepositoriesRoot = new File("src/main/native/libgit2/tests/resources"); + if (!testRepositoriesRoot.exists()) { - resourcesRoot = new File(GitTest.class.getResource("/testrepo").getFile()).getParentFile(); + throw new RuntimeException("Can't find test resources at " + testRepositoriesRoot.getAbsolutePath()); } if (System.getenv("TMPDIR") != null) @@ -112,6 +123,18 @@ public File getTempDir() return tempDir; } + public void write(File file, String content) throws IOException + { + FileWriter writer = new FileWriter(file); + try + { + writer.write(content); + } finally + { + writer.close(); + } + } + private static void cleanupDirectory(final File file) { if (file.isDirectory()) @@ -170,6 +193,6 @@ private static File copyRecursive(final File source, final File target, final St public File setupRepository(final String name) { - return copyRecursive(resourcesRoot, tempDir, name); + return copyRecursive(testRepositoriesRoot, tempDir, name); } } diff --git a/src/test/java/org/libgit2/jagged/ReferenceTest.java b/src/test/java/org/libgit2/jagged/ReferenceTest.java index 88e2342..22f1e49 100644 --- a/src/test/java/org/libgit2/jagged/ReferenceTest.java +++ b/src/test/java/org/libgit2/jagged/ReferenceTest.java @@ -45,7 +45,21 @@ public void testCanIterateReferences() { final String[] expected = new String[] { - "refs/heads/master" + "refs/heads/br2", + "refs/heads/dir", + "refs/heads/ident", + "refs/heads/long-file-name", + "refs/heads/master", + "refs/heads/packed-test", + "refs/heads/subtrees", + "refs/heads/test", + "refs/tags/e90810b", + "refs/tags/foo/bar", + "refs/tags/foo/foo/bar", + "refs/tags/point_to_blob", + "refs/tags/test", + "refs/heads/packed", + "refs/tags/packed-tag" }; Repository repository = new Repository(setupRepository("testrepo").getAbsolutePath()); diff --git a/src/test/java/org/libgit2/jagged/TreeTest.java b/src/test/java/org/libgit2/jagged/TreeTest.java index 2b86370..ca8b5a2 100644 --- a/src/test/java/org/libgit2/jagged/TreeTest.java +++ b/src/test/java/org/libgit2/jagged/TreeTest.java @@ -16,7 +16,7 @@ public void testLookupTree() final File repoPath = setupRepository("testrepo"); Repository repository = new Repository(repoPath.getAbsolutePath()); - ObjectId oid = new ObjectId("e77ab1c63f3fbde9c5ef9972939aa0717012d7c0"); + ObjectId oid = new ObjectId(C8_ROOT); Tree tree = repository.lookup(oid); Assert.assertEquals(oid, tree.getId()); @@ -30,10 +30,10 @@ public void testGetEntryCount() final File repoPath = setupRepository("testrepo"); Repository repository = new Repository(repoPath.getAbsolutePath()); - ObjectId oid = new ObjectId("e77ab1c63f3fbde9c5ef9972939aa0717012d7c0"); + ObjectId oid = new ObjectId(C8_ROOT); Tree tree = repository.lookup(oid); - Assert.assertEquals(3, tree.getEntryCount()); + Assert.assertEquals(4, tree.getEntryCount()); repository.close(); } @@ -44,29 +44,36 @@ public void testGetEntries() final File repoPath = setupRepository("testrepo"); Repository repository = new Repository(repoPath.getAbsolutePath()); - ObjectId oid = new ObjectId("e77ab1c63f3fbde9c5ef9972939aa0717012d7c0"); + ObjectId oid = new ObjectId(C8_ROOT); Tree tree = repository.lookup(oid); Iterator iterator = tree.getEntries().iterator(); TreeEntry entry = iterator.next(); - Assert.assertEquals("one.txt", entry.getName()); - Assert.assertEquals(new ObjectId("d1796967d47949153bb852c07304d9e5f2f0040c"), entry.getId()); + Assert.assertEquals("README", entry.getName()); + Assert.assertEquals(new ObjectId("a8233120f6ad708f843d861ce2b7228ec4e3dec6"), entry.getId()); Assert.assertEquals(Mode.FILE, entry.getMode()); Assert.assertEquals(ObjectType.BLOB, entry.getType()); entry = iterator.next(); - Assert.assertEquals("three.txt", entry.getName()); - Assert.assertEquals(new ObjectId("8fbe49af0d14c65f881b57709acae2ea3414089a"), entry.getId()); + Assert.assertEquals("branch_file.txt", entry.getName()); + Assert.assertEquals(new ObjectId("3697d64be941a53d4ae8f6a271e4e3fa56b022cc"), entry.getId()); Assert.assertEquals(Mode.FILE, entry.getMode()); Assert.assertEquals(ObjectType.BLOB, entry.getType()); entry = iterator.next(); - Assert.assertEquals("two.txt", entry.getName()); - Assert.assertEquals(new ObjectId("dc48b6c38e967e57965e36c6f7a1c3ec5c3e1ff4"), entry.getId()); + Assert.assertEquals("link_to_new.txt", entry.getName()); + Assert.assertEquals(new ObjectId("c0528fd6cc988c0a40ce0be11bc192fc8dc5346e"), entry.getId()); + Assert.assertEquals(Mode.LINK, entry.getMode()); + Assert.assertEquals(ObjectType.BLOB, entry.getType()); + + entry = iterator.next(); + + Assert.assertEquals("new.txt", entry.getName()); + Assert.assertEquals(new ObjectId("a71586c1dfe8a71c6cbf6c129f404c5642ff31bd"), entry.getId()); Assert.assertEquals(Mode.FILE, entry.getMode()); Assert.assertEquals(ObjectType.BLOB, entry.getType()); @@ -81,27 +88,13 @@ public void testGetEntryByName() final File repoPath = setupRepository("testrepo"); Repository repository = new Repository(repoPath.getAbsolutePath()); - ObjectId oid = new ObjectId("e77ab1c63f3fbde9c5ef9972939aa0717012d7c0"); + ObjectId oid = new ObjectId(C8_ROOT); Tree tree = repository.lookup(oid); - TreeEntry entry = tree.getEntry("one.txt"); - - Assert.assertEquals("one.txt", entry.getName()); - Assert.assertEquals(new ObjectId("d1796967d47949153bb852c07304d9e5f2f0040c"), entry.getId()); - Assert.assertEquals(Mode.FILE, entry.getMode()); - Assert.assertEquals(ObjectType.BLOB, entry.getType()); - - entry = tree.getEntry("two.txt"); - - Assert.assertEquals("two.txt", entry.getName()); - Assert.assertEquals(new ObjectId("dc48b6c38e967e57965e36c6f7a1c3ec5c3e1ff4"), entry.getId()); - Assert.assertEquals(Mode.FILE, entry.getMode()); - Assert.assertEquals(ObjectType.BLOB, entry.getType()); - - entry = tree.getEntry("three.txt"); + TreeEntry entry = tree.getEntry(README); - Assert.assertEquals("three.txt", entry.getName()); - Assert.assertEquals(new ObjectId("8fbe49af0d14c65f881b57709acae2ea3414089a"), entry.getId()); + Assert.assertEquals(README, entry.getName()); + Assert.assertEquals(new ObjectId(C8_README), entry.getId()); Assert.assertEquals(Mode.FILE, entry.getMode()); Assert.assertEquals(ObjectType.BLOB, entry.getType()); @@ -114,12 +107,12 @@ public void testCanRealizeTreeEntry() final File repoPath = setupRepository("testrepo"); Repository repository = new Repository(repoPath.getAbsolutePath()); - ObjectId oid = new ObjectId("e77ab1c63f3fbde9c5ef9972939aa0717012d7c0"); + ObjectId oid = new ObjectId(C8_ROOT); Tree tree = repository.lookup(oid); - TreeEntry entry = tree.getEntry("one.txt"); + TreeEntry entry = tree.getEntry(README); Blob blob = entry.realize(); - Assert.assertEquals(new ObjectId("d1796967d47949153bb852c07304d9e5f2f0040c"), blob.getId()); + Assert.assertEquals(new ObjectId(C8_README), blob.getId()); } } diff --git a/src/test/resources/testrepo/.gitattributes b/src/test/resources/testrepo/.gitattributes deleted file mode 100644 index 176a458..0000000 --- a/src/test/resources/testrepo/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -* text=auto diff --git a/src/test/resources/testrepo/.gitted/HEAD b/src/test/resources/testrepo/.gitted/HEAD deleted file mode 100644 index cb089cd..0000000 --- a/src/test/resources/testrepo/.gitted/HEAD +++ /dev/null @@ -1 +0,0 @@ -ref: refs/heads/master diff --git a/src/test/resources/testrepo/.gitted/config b/src/test/resources/testrepo/.gitted/config deleted file mode 100644 index af10792..0000000 --- a/src/test/resources/testrepo/.gitted/config +++ /dev/null @@ -1,6 +0,0 @@ -[core] - repositoryformatversion = 0 - filemode = true - bare = false - logallrefupdates = true - ignorecase = true diff --git a/src/test/resources/testrepo/.gitted/description b/src/test/resources/testrepo/.gitted/description deleted file mode 100644 index 498b267..0000000 --- a/src/test/resources/testrepo/.gitted/description +++ /dev/null @@ -1 +0,0 @@ -Unnamed repository; edit this file 'description' to name the repository. diff --git a/src/test/resources/testrepo/.gitted/index b/src/test/resources/testrepo/.gitted/index deleted file mode 100644 index deda49e860a825bceef3cd9a71a27b73810ef2a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 361 zcmZ?q402{*U|<4bmavPWXMr>WjAjIiG0VycGcYtRVPIhV3X~E7Vjl4<*ZwIFUIi7M zOFx|bnLGRCsc*^*e0u4bC5a^^MVU#ZC8@msnfIm_}B*KZxYbDR-=44fqy zMX3;zaG1l2W{&W*725m4wo4p2667f$a#D77^6IWi2KJKje5iXuf?QpJ&XQy>Q83^- uxLADU%u4o`DxnSAcCUQ=u^*#H11KxAqF diff --git a/src/test/resources/testrepo/.gitted/info/exclude b/src/test/resources/testrepo/.gitted/info/exclude deleted file mode 100644 index a5196d1..0000000 --- a/src/test/resources/testrepo/.gitted/info/exclude +++ /dev/null @@ -1,6 +0,0 @@ -# git ls-files --others --exclude-from=.git/info/exclude -# Lines that start with '#' are comments. -# For a project mostly in C, the following would be a good set of -# exclude patterns (uncomment them if you want to use them): -# *.[oa] -# *~ diff --git a/src/test/resources/testrepo/.gitted/logs/HEAD b/src/test/resources/testrepo/.gitted/logs/HEAD deleted file mode 100644 index bfdc00d..0000000 --- a/src/test/resources/testrepo/.gitted/logs/HEAD +++ /dev/null @@ -1,3 +0,0 @@ -0000000000000000000000000000000000000000 055fe18dd1aef07991ebd08b4d54fc761dd022fb Edward Thomson 1367986388 -0500 commit (initial): Initial revision -055fe18dd1aef07991ebd08b4d54fc761dd022fb 5eab02d63a3676df528bcd878ac935ec0c4d5bdc Edward Thomson 1387241114 -0500 commit: Updated! -5eab02d63a3676df528bcd878ac935ec0c4d5bdc 5fa5363e9ca45dd8fbf2be90a6898f40332f72b9 Edward Thomson 1456543187 -0500 commit: Add gitattributes diff --git a/src/test/resources/testrepo/.gitted/logs/refs/heads/master b/src/test/resources/testrepo/.gitted/logs/refs/heads/master deleted file mode 100644 index bfdc00d..0000000 --- a/src/test/resources/testrepo/.gitted/logs/refs/heads/master +++ /dev/null @@ -1,3 +0,0 @@ -0000000000000000000000000000000000000000 055fe18dd1aef07991ebd08b4d54fc761dd022fb Edward Thomson 1367986388 -0500 commit (initial): Initial revision -055fe18dd1aef07991ebd08b4d54fc761dd022fb 5eab02d63a3676df528bcd878ac935ec0c4d5bdc Edward Thomson 1387241114 -0500 commit: Updated! -5eab02d63a3676df528bcd878ac935ec0c4d5bdc 5fa5363e9ca45dd8fbf2be90a6898f40332f72b9 Edward Thomson 1456543187 -0500 commit: Add gitattributes diff --git a/src/test/resources/testrepo/.gitted/objects/05/5fe18dd1aef07991ebd08b4d54fc761dd022fb b/src/test/resources/testrepo/.gitted/objects/05/5fe18dd1aef07991ebd08b4d54fc761dd022fb deleted file mode 100644 index 4077e3bd23c713fb37a852ef7d0033522b7e65f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 138 zcmV;50CoR(0i}&W4#FT106p`H{eT8a=ynrhym;{L2PiBliF6Y{{l54HZ<9I91SutV zFys!sG6N&})r8q5o7Tt_948{2$ddOO4Qew*3|jeD-=xAVeteDa*rYVs;mpJS=vs29 s(j@g$ka7WIXQXAe%K}I3v{u88apyl0>b~cm{Szv`bIY=;FM)AE;v2t1t^fc4 diff --git a/src/test/resources/testrepo/.gitted/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623 b/src/test/resources/testrepo/.gitted/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623 deleted file mode 100644 index ef83166706c43aabd3c68d155be9c92a23960f0f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28 kcmbVZP%%@=`EW:"9os~0E`%Vh .qHIɫ**~ \ No newline at end of file diff --git a/src/test/resources/testrepo/.gitted/objects/5e/ab02d63a3676df528bcd878ac935ec0c4d5bdc b/src/test/resources/testrepo/.gitted/objects/5e/ab02d63a3676df528bcd878ac935ec0c4d5bdc deleted file mode 100644 index a2e3ca14fa2452c657eb9ad0ee4364f22be05f58..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 162 zcmV;T0A2rh0i}+~4Z<)G0C~F#Qvhh!cO-=P&;b-+d)EpN$B|=1`-B?!jpi|$+-iLd zskA+e&Md9Z#R51FDd>ce3>Uz;QYDG_9&_vRPM_FGKIT@pw#WmYIRF3v diff --git a/src/test/resources/testrepo/.gitted/objects/5f/a5363e9ca45dd8fbf2be90a6898f40332f72b9 b/src/test/resources/testrepo/.gitted/objects/5f/a5363e9ca45dd8fbf2be90a6898f40332f72b9 deleted file mode 100644 index 01d2b42..0000000 --- a/src/test/resources/testrepo/.gitted/objects/5f/a5363e9ca45dd8fbf2be90a6898f40332f72b9 +++ /dev/null @@ -1,2 +0,0 @@ -xm0 E{\ lEs]GB#]<s6TM&R -OZ战}MrQQK"wT~8$Jr9&2PoQJs|ˋҷgEG_-{=}1a S&8yxi]EX` M5 \ No newline at end of file diff --git a/src/test/resources/testrepo/.gitted/objects/8f/be49af0d14c65f881b57709acae2ea3414089a b/src/test/resources/testrepo/.gitted/objects/8f/be49af0d14c65f881b57709acae2ea3414089a deleted file mode 100644 index ee567496ec5661cbef6e8f09c5cc7b01e96d9c08..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34 qcmb7v1BkbFfcPQQP4}zEJ-XWDauSLElDkA5YKY$pYq^UP|>;c z!`Yv?vtOS2rVLe?pO>mvQc=R7%dy%ccxgz-lE+4N`x|_$ina7}p-M_JfTn{LUD#U2w{^l&pPh7us^v-cc_`wvF=Yy3@TcN!#Y`esfBSD@5A}3{MC$H|R1OQ4CJe$CW BM&AGc diff --git a/src/test/resources/testrepo/.gitted/objects/d1/796967d47949153bb852c07304d9e5f2f0040c b/src/test/resources/testrepo/.gitted/objects/d1/796967d47949153bb852c07304d9e5f2f0040c deleted file mode 100644 index 9f0d3d47151c32e6420767c4c9fc44470d2ed3b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32 ocmb{Y{I$;cpww#$0()wplh^rn$-c1fJ0O_m^#{d8T diff --git a/src/test/resources/testrepo/.gitted/objects/d4/bcc68acd4410bf836a39f20afb2c2ece09584e b/src/test/resources/testrepo/.gitted/objects/d4/bcc68acd4410bf836a39f20afb2c2ece09584e deleted file mode 100644 index 739f5c2d0eb15dbf23711f98e4a732c414a4694d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35 rcmb{Y{I$;cpww#$0(mLr=jjJ9*z6pmC0Os2bF8}}l diff --git a/src/test/resources/testrepo/.gitted/objects/e7/7ab1c63f3fbde9c5ef9972939aa0717012d7c0 b/src/test/resources/testrepo/.gitted/objects/e7/7ab1c63f3fbde9c5ef9972939aa0717012d7c0 deleted file mode 100644 index 34bdb6526c63af04434617d29ef3a48408b702fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 109 zcmV-z0FwWB0V^p=O;s>7FlR6{FfcPQQOM6r)hnqeVYpbCnSQ0xQ`CA#(1Bu>n@>M| zVBvu(Daiop0W0d?=eeF& Date: Mon, 7 Mar 2016 17:03:35 +0100 Subject: [PATCH 3/4] jniutils introduced --- src/main/native/libjagged/jniutil.c | 105 ++++++++++++++++++++++++++++ src/main/native/libjagged/jniutil.h | 22 ++++++ src/main/native/libjagged/util.h | 2 + 3 files changed, 129 insertions(+) create mode 100644 src/main/native/libjagged/jniutil.c create mode 100644 src/main/native/libjagged/jniutil.h diff --git a/src/main/native/libjagged/jniutil.c b/src/main/native/libjagged/jniutil.c new file mode 100644 index 0000000..b9e3d36 --- /dev/null +++ b/src/main/native/libjagged/jniutil.c @@ -0,0 +1,105 @@ +#include + +#include + +#include "jniutil.h" + +int jni_get_class(jclass *class, char *class_name, JNIEnv *env) { + *class = (*env)->FindClass(env, class_name); + return *class != NULL ? 1 : 0; +} + +int jni_get_method_id_by_class(jmethodID *methodId, jclass clazz, char *method_name, char *method_signature, + JNIEnv *env) { + *methodId = (*env)->GetMethodID(env, clazz, method_name, method_signature); + return *methodId != NULL ? 1 : 0; +} + +int jni_get_method_id_by_object(jmethodID *methodId, jobject object, char *method_name, char *method_signature, + JNIEnv *env) { + jclass class = (*env)->GetObjectClass(env, object); + *methodId = (*env)->GetMethodID(env, class, method_name, method_signature); + return *methodId != NULL ? 1 : 0; +} + +int jni_get_static_object_field(jobject *field, jclass class, char *field_name, char *field_type, JNIEnv *env) { + jfieldID fieldId = (*env)->GetStaticFieldID(env, class, field_name, field_type); + if (fieldId == NULL) { + return 0; + } + + *field = (*env)->GetStaticObjectField(env, class, fieldId); + return 1; +} + +int jni_get_object_field(jobject *field, jobject object, char *field_name, char *field_type, JNIEnv *env) { + jclass class = (*env)->GetObjectClass(env, object); + jfieldID fieldId = (*env)->GetFieldID(env, class, field_name, field_type); + if (fieldId == NULL) { + return 0; + } + + *field = (*env)->GetObjectField(env, object, fieldId); + return 1; +} + +int jni_get_int_field(jint *field, jobject object, char *field_name, JNIEnv *env) { + jclass class = (*env)->GetObjectClass(env, object); + jfieldID fieldId = (*env)->GetFieldID(env, class, field_name, "I"); + if (fieldId == NULL) { + return 0; + } + + *field = (*env)->GetIntField(env, object, fieldId); + return 1; +} + +int jni_constructor_init(jni_constructor *constructor, char *class_name, char *signature, JNIEnv *env) { + if (!jni_get_class(&(constructor->class), class_name, env) || + !jni_get_method_id_by_class(&(constructor->init), constructor->class, "", signature, env)) + return 0; + + return 1; +} + +jobject jni_constructor_invoke(jni_constructor *constructor, JNIEnv *env, ...) { + assert(constructor->class && constructor->init); + + va_list args; + va_start(args, env); + jobject object = (*env)->NewObjectV(env, constructor->class, constructor->init, args); + va_end(args); + return object; +} + +int jni_call_object_method(jobject *result, jobject object, char *class_name, char *method_name, char *method_signature, JNIEnv *env) { + jclass java_class; + if (class_name != NULL) { + java_class = (*env)->FindClass(env, class_name); + } + else { + java_class = (*env)->GetObjectClass(env, object); + } + if (java_class == NULL) { + return 0; + } + + jmethodID methodId; + if (!jni_get_method_id_by_class(&methodId, java_class, method_name, method_signature, env)) { + return 0; + } + + *result = (*env)->CallObjectMethod(env, object, methodId, object); + return 1; +} + +int jni_create_object_array(jobjectArray *result, char *class_name, jint size, JNIEnv *env) { + jclass class = (*env)->FindClass(env, class_name); + if (class == NULL) { + return 0; + } + + *result = (*env)->NewObjectArray(env, size, class, NULL); + return *result != NULL ? 1 : 0; +} + diff --git a/src/main/native/libjagged/jniutil.h b/src/main/native/libjagged/jniutil.h new file mode 100644 index 0000000..0c8cf9a --- /dev/null +++ b/src/main/native/libjagged/jniutil.h @@ -0,0 +1,22 @@ +#ifndef GIT_JAVA_JNI_UTIL_H +#define GIT_JAVA_JNI_UTIL_H + +#include + +typedef struct { + jclass class; + jmethodID init; +} jni_constructor; + +int jni_get_class(jclass *class, char* class_name, JNIEnv *env); +int jni_get_method_id_by_class(jmethodID *methodId, jclass class, char* method_name, char* method_signature, JNIEnv* env); +int jni_get_method_id_by_object(jmethodID *methodId, jobject object, char* method_name, char* method_signature, JNIEnv* env); +int jni_get_object_field(jobject *field, jobject object, char* field_name, char* field_type, JNIEnv *env); +int jni_get_static_object_field(jobject *field, jclass class, char* field_name, char* field_type, JNIEnv *env); +int jni_get_int_field(jint *field, jobject object, char *field_name, JNIEnv *env); +int jni_call_object_method(jobject *result, jobject object, char *class_name, char *method_name, char *method_signature, JNIEnv *env); +int jni_create_object_array(jobjectArray *result, char *class_name, jint size, JNIEnv *env); +int jni_constructor_init(jni_constructor *constructor, char *class_name, char *signature, JNIEnv *env); +jobject jni_constructor_invoke(jni_constructor *constructor, JNIEnv *env, ...); + +#endif /* GIT_JAVA_JNI_UTIL_H */ diff --git a/src/main/native/libjagged/util.h b/src/main/native/libjagged/util.h index c894a5a..e6ec47f 100644 --- a/src/main/native/libjagged/util.h +++ b/src/main/native/libjagged/util.h @@ -14,6 +14,8 @@ #include +#include "jniutil.h" + #define GIT_UNUSED(x) ((void)(x)) #ifdef _MSC_VER From 4c2c194cb99d61aee1631eaafdc6eb9c283897ca Mon Sep 17 00:00:00 2001 From: Marc Strapetz Date: Fri, 11 Mar 2016 11:29:43 +0100 Subject: [PATCH 4/4] status API --- .../java/org/libgit2/jagged/ObjectId.java | 2 + .../java/org/libgit2/jagged/Repository.java | 7 + .../libgit2/jagged/core/NativeMethods.java | 10 + .../org/libgit2/jagged/status/Status.java | 209 ++++++++++++++ .../libgit2/jagged/status/StatusOptions.java | 78 +++++ .../jagged/status/StatusShowOptions.java | 6 + src/main/native/libjagged/oid.h | 26 +- src/main/native/libjagged/status.c | 270 ++++++++++++++++++ .../libgit2/jagged/GitLocalCommandTest.java | 119 ++++++++ .../java/org/libgit2/jagged/StatusTest.java | 118 ++++++++ 10 files changed, 838 insertions(+), 7 deletions(-) create mode 100644 src/main/java/org/libgit2/jagged/status/Status.java create mode 100644 src/main/java/org/libgit2/jagged/status/StatusOptions.java create mode 100644 src/main/java/org/libgit2/jagged/status/StatusShowOptions.java create mode 100644 src/main/native/libjagged/status.c create mode 100644 src/test/java/org/libgit2/jagged/GitLocalCommandTest.java create mode 100644 src/test/java/org/libgit2/jagged/StatusTest.java diff --git a/src/main/java/org/libgit2/jagged/ObjectId.java b/src/main/java/org/libgit2/jagged/ObjectId.java index 5ec975a..8628c3b 100644 --- a/src/main/java/org/libgit2/jagged/ObjectId.java +++ b/src/main/java/org/libgit2/jagged/ObjectId.java @@ -12,6 +12,8 @@ public final class ObjectId public final static int SIZE = 20; public final static int HEX_SIZE = 40; + public static final ObjectId NULL = new ObjectId("0000000000000000000000000000000000000000"); + private final byte[] oid; private final String hex; diff --git a/src/main/java/org/libgit2/jagged/Repository.java b/src/main/java/org/libgit2/jagged/Repository.java index dbd9c15..d474d61 100644 --- a/src/main/java/org/libgit2/jagged/Repository.java +++ b/src/main/java/org/libgit2/jagged/Repository.java @@ -7,6 +7,8 @@ import org.libgit2.jagged.core.GitException; import org.libgit2.jagged.core.NativeHandle; import org.libgit2.jagged.core.NativeMethods; +import org.libgit2.jagged.status.Status; +import org.libgit2.jagged.status.StatusOptions; /** * A Repository is the primary interface to a git repository. @@ -180,6 +182,11 @@ public Branch getHead() return new DetachedHead(this, reference); } + public Status[] statusListNew(StatusOptions options, boolean populateOids) + { + return NativeMethods.statusListNew(this, options, populateOids); + } + /** * Disposes the underlying Repository object. */ diff --git a/src/main/java/org/libgit2/jagged/core/NativeMethods.java b/src/main/java/org/libgit2/jagged/core/NativeMethods.java index 4e9f0c0..38d9ec3 100644 --- a/src/main/java/org/libgit2/jagged/core/NativeMethods.java +++ b/src/main/java/org/libgit2/jagged/core/NativeMethods.java @@ -10,9 +10,11 @@ import org.libgit2.jagged.Reference; import org.libgit2.jagged.Reference.DirectReference; import org.libgit2.jagged.Repository; +import org.libgit2.jagged.status.Status; import org.libgit2.jagged.Tree; import org.libgit2.jagged.TreeEntry; import org.libgit2.jagged.Version; +import org.libgit2.jagged.status.StatusOptions; public class NativeMethods { @@ -128,4 +130,12 @@ public void finalize() public static native void repositoryOpen(Repository repository, String path); public static native boolean repositoryIsBare(Repository repository); + + /* + * Status operations + */ + + public static native StatusOptions statusOptionsInit(); + + public static native Status[] statusListNew(Repository repository, StatusOptions options, boolean populateOids); } diff --git a/src/main/java/org/libgit2/jagged/status/Status.java b/src/main/java/org/libgit2/jagged/status/Status.java new file mode 100644 index 0000000..43c535a --- /dev/null +++ b/src/main/java/org/libgit2/jagged/status/Status.java @@ -0,0 +1,209 @@ +package org.libgit2.jagged.status; + +import org.libgit2.jagged.ObjectId; + +public class Status +{ + + public static final int GIT_STATUS_INDEX_NEW = (1 << 0); + public static final int GIT_STATUS_INDEX_MODIFIED = (1 << 1); + public static final int GIT_STATUS_INDEX_DELETED = (1 << 2); + public static final int GIT_STATUS_INDEX_RENAMED = (1 << 3); + public static final int GIT_STATUS_INDEX_TYPECHANGE = (1 << 4); + public static final int GIT_STATUS_WT_NEW = (1 << 7); + public static final int GIT_STATUS_WT_MODIFIED = (1 << 8); + public static final int GIT_STATUS_WT_DELETED = (1 << 9); + public static final int GIT_STATUS_WT_TYPECHANGE = (1 << 10); + public static final int GIT_STATUS_WT_RENAMED = (1 << 11); + public static final int GIT_STATUS_WT_UNREADABLE = (1 << 12); + public static final int GIT_STATUS_IGNORED = (1 << 14); + public static final int GIT_STATUS_CONFLICTED = (1 << 15); + + public static final int GIT_DELTA_UNMODIFIED = 0; + public static final int GIT_DELTA_ADDED = 1; + public static final int GIT_DELTA_DELETED = 2; + public static final int GIT_DELTA_MODIFIED = 3; + public static final int GIT_DELTA_RENAMED = 4; + public static final int GIT_DELTA_COPIED = 5; + public static final int GIT_DELTA_IGNORED = 6; + public static final int GIT_DELTA_UNTRACKED = 7; + public static final int GIT_DELTA_TYPECHANGE = 8; + public static final int GIT_DELTA_UNREADABLE = 9; + public static final int GIT_DELTA_CONFLICTED = 10; + + public static final int GIT_DIFF_FLAG_BINARY = (1 << 0); + public static final int GIT_DIFF_FLAG_NOT_BINARY = (1 << 1); + public static final int GIT_DIFF_FLAG_VALID_ID = (1 << 2); + public static final int GIT_DIFF_FLAG_EXISTS = (1 << 3); + + public static final int GIT_FILEMODE_UNREADABLE = 0000000; + public static final int GIT_FILEMODE_TREE = 0040000; + public static final int GIT_FILEMODE_BLOB = 0100644; + public static final int GIT_FILEMODE_BLOB_EXECUTABLE = 0100755; + public static final int GIT_FILEMODE_LINK = 0120000; + public static final int GIT_FILEMODE_COMMIT = 0160000; + + private final int status; + private final String headPath; + private final long headSize; + private final int headFlags; + private final int headMode; + private final ObjectId headId; + private final String indexPath; + private final long indexSize; + private final int indexFlags; + private final int indexMode; + private final ObjectId indexId; + private final String wtPath; + private final long wtSize; + private final int wtFlags; + private final int wtMode; + private final ObjectId wtId; + private final int headIndexFlags; + private final int headIndexSimilarity; + private final int headIndexNFiles; + private final int indexWtFlags; + private final int indexWtSimilarity; + private final int indexWtNFiles; + + private Status(int status, + String headPath, long headSize, int headFlags, int headMode, ObjectId headId, + String indexPath, long indexSize, int indexFlags, int indexMode, ObjectId indexId, + String wtPath, long wtSize, int wtFlags, int wtMode, ObjectId wtId, + int headIndexFlags, int headIndexSimilarity, int headIndexNFiles, + int indexWtFlags, int indexWtSimilarity, int indexWtNFiles) + { + this.status = status; + this.headPath = headPath; + this.headSize = headSize; + this.headFlags = headFlags; + this.headMode = headMode; + this.headId = headId; + this.indexPath = indexPath; + this.indexSize = indexSize; + this.indexFlags = indexFlags; + this.indexMode = indexMode; + this.indexId = indexId; + this.wtPath = wtPath; + this.wtSize = wtSize; + this.wtFlags = wtFlags; + this.wtMode = wtMode; + this.wtId = wtId; + this.headIndexFlags = headIndexFlags; + this.headIndexSimilarity = headIndexSimilarity; + this.headIndexNFiles = headIndexNFiles; + this.indexWtFlags = indexWtFlags; + this.indexWtSimilarity = indexWtSimilarity; + this.indexWtNFiles = indexWtNFiles; + } + + public int getStatus() + { + return status; + } + + public String getHeadPath() + { + return headPath; + } + + public long getHeadSize() + { + return headSize; + } + + public int getHeadFlags() + { + return headFlags; + } + + public int getHeadMode() + { + return headMode; + } + + public ObjectId getHeadId() + { + return headId; + } + + public String getIndexPath() + { + return indexPath; + } + + public long getIndexSize() + { + return indexSize; + } + + public int getIndexFlags() + { + return indexFlags; + } + + public int getIndexMode() + { + return indexMode; + } + + public ObjectId getIndexId() + { + return indexId; + } + + public String getWtPath() + { + return wtPath; + } + + public long getWtSize() + { + return wtSize; + } + + public int getWtFlags() + { + return wtFlags; + } + + public int getWtMode() + { + return wtMode; + } + + public ObjectId getWtId() + { + return wtId; + } + + public int getHeadIndexFlags() + { + return headIndexFlags; + } + + public int getHeadIndexSimilarity() + { + return headIndexSimilarity; + } + + public int getHeadIndexNFiles() + { + return headIndexNFiles; + } + + public int getIndexWtFlags() + { + return indexWtFlags; + } + + public int getIndexWtSimilarity() + { + return indexWtSimilarity; + } + + public int getIndexWtNFiles() + { + return indexWtNFiles; + } +} \ No newline at end of file diff --git a/src/main/java/org/libgit2/jagged/status/StatusOptions.java b/src/main/java/org/libgit2/jagged/status/StatusOptions.java new file mode 100644 index 0000000..34c7044 --- /dev/null +++ b/src/main/java/org/libgit2/jagged/status/StatusOptions.java @@ -0,0 +1,78 @@ +package org.libgit2.jagged.status; + +import java.util.Arrays; + +import org.libgit2.jagged.core.NativeMethods; + +public class StatusOptions +{ + + public static final int GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1 << 0); + public static final int GIT_STATUS_OPT_INCLUDE_IGNORED = (1 << 1); + public static final int GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1 << 2); + public static final int GIT_STATUS_OPT_EXCLUDE_SUBMODULES = (1 << 3); + public static final int GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1 << 4); + public static final int GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1 << 5); + public static final int GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = (1 << 6); + public static final int GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX = (1 << 7); + public static final int GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = (1 << 8); + public static final int GIT_STATUS_OPT_SORT_CASE_SENSITIVELY = (1 << 9); + public static final int GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY = (1 << 10); + public static final int GIT_STATUS_OPT_RENAMES_FROM_REWRITES = (1 << 11); + public static final int GIT_STATUS_OPT_NO_REFRESH = (1 << 12); + public static final int GIT_STATUS_OPT_UPDATE_INDEX = (1 << 13); + public static final int GIT_STATUS_OPT_INCLUDE_UNREADABLE = (1 << 14); + public static final int GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED = (1 << 15); + + private StatusShowOptions show; + private int flags; + private String[] pathSpecs; + + private StatusOptions(StatusShowOptions show, int flags, String[] pathSpecs) + { + this.show = show; + this.flags = flags; + this.pathSpecs = pathSpecs; + } + + public static StatusOptions createInitializedInstance() + { + return NativeMethods.statusOptionsInit(); + } + + public StatusShowOptions getShow() + { + return show; + } + + public void setShow(StatusShowOptions show) + { + this.show = show; + } + + public int getFlags() + { + return flags; + } + + public void setFlags(int flags) + { + this.flags = flags; + } + + public String[] getPathSpecs() + { + return pathSpecs; + } + + public void setPathSpecs(String[] pathSpecs) + { + this.pathSpecs = pathSpecs; + } + + @Override + public String toString() + { + return "show=" + show + ";flags=" + flags + ";pathSpecs=" + (pathSpecs != null ? Arrays.asList(pathSpecs) : null); + } +} \ No newline at end of file diff --git a/src/main/java/org/libgit2/jagged/status/StatusShowOptions.java b/src/main/java/org/libgit2/jagged/status/StatusShowOptions.java new file mode 100644 index 0000000..3a25076 --- /dev/null +++ b/src/main/java/org/libgit2/jagged/status/StatusShowOptions.java @@ -0,0 +1,6 @@ +package org.libgit2.jagged.status; + +public enum StatusShowOptions +{ + INDEX_AND_WORKDIR, INDEX_ONLY, WORKDIR_ONLY; +} \ No newline at end of file diff --git a/src/main/native/libjagged/oid.h b/src/main/native/libjagged/oid.h index 9aa28c3..d5c7c25 100644 --- a/src/main/native/libjagged/oid.h +++ b/src/main/native/libjagged/oid.h @@ -5,24 +5,36 @@ #include +#include "jniutil.h" #include "util.h" #define GIT_JAVA_CLASS_OID "org/libgit2/jagged/ObjectId" -GIT_INLINE(jobject) git_java_objectid_init(JNIEnv *env, const git_oid *oid) +GIT_INLINE(int) git_java_objectid_constructor(JNIEnv *env, jni_constructor *constructor) +{ + return jni_constructor_init(constructor, GIT_JAVA_CLASS_OID, "([B)V", env); +} + +GIT_INLINE(jobject) git_java_objectid_init2(JNIEnv *env, const git_oid *oid, jni_constructor* constructor) { - jclass oid_class; - jmethodID oid_initmethod; jbyteArray oid_bytearray; - if ((oid_class = (*env)->FindClass(env, GIT_JAVA_CLASS_OID)) == NULL || - (oid_initmethod = (*env)->GetMethodID(env, oid_class, "", "([B)V")) == NULL || - (oid_bytearray = (*env)->NewByteArray(env, 20)) == NULL) + if ((oid_bytearray = (*env)->NewByteArray(env, 20)) == NULL) return NULL; (*env)->SetByteArrayRegion(env, oid_bytearray, 0, 20, (const jbyte *)oid->id); + return (*env)->NewObject(env, constructor->class, constructor->init, oid_bytearray); + +// return jni_constructor_invoke(constructor, env, oid_bytearray); +} + +GIT_INLINE(jobject) git_java_objectid_init(JNIEnv *env, const git_oid *oid) +{ + jni_constructor constructor; + if (!git_java_objectid_constructor(env, &constructor)) + return NULL; - return (*env)->NewObject(env, oid_class, oid_initmethod, oid_bytearray); + return git_java_objectid_init2(env, oid, &constructor); } GIT_INLINE(int) git_java_objectid_tonative(git_oid *out, JNIEnv *env, jobject oid_java) diff --git a/src/main/native/libjagged/status.c b/src/main/native/libjagged/status.c new file mode 100644 index 0000000..876dd7b --- /dev/null +++ b/src/main/native/libjagged/status.c @@ -0,0 +1,270 @@ +#include + +#include +#include + +#include "oid.h" + +#define STATUS_SHOW_INDEX_ONLY "INDEX_ONLY" +#define STATUS_SHOW_WORKDIR_ONLY "WORKDIR_ONLY" +#define STATUS_SHOW_INDEX_AND_WORKDIR "INDEX_AND_WORKDIR" + +#pragma warning ( disable : 4090 ) +#pragma warning ( disable : 6386 ) + +jobject create_status_java(const git_status_entry *status, jni_constructor *status_constructor, int populate_oids, JNIEnv *env) { + jint status_flags = (jint) status->status; + const char *head_path = NULL; + jlong head_size = 0; + jint head_flags = 0; + jint head_mode = 0; + jobject head_id = NULL; + + const char *index_path = NULL; + jlong index_size = 0; + jint index_flags = 0; + jint index_mode = 0; + jobject index_id = NULL; + + const char *wt_path = NULL; + jlong wt_size = 0; + jint wt_flags = 0; + jint wt_mode = 0; + jobject wt_id = NULL; + + int head_index_flags = 0; + int head_index_similarity = 0; + int head_index_nfiles = 0; + + int index_wt_flags = 0; + int index_wt_similarity = 0; + int index_wt_nfiles = 0; + + git_diff_delta *head_to_index = status->head_to_index; + if (head_to_index) { + head_path = head_to_index->old_file.path; + head_size = head_to_index->old_file.size; + head_flags = (jint) head_to_index->old_file.flags; + head_mode = head_to_index->old_file.mode; + head_id = populate_oids ? git_java_objectid_init(env, &(head_to_index->old_file.id)) : NULL; + index_path = head_to_index->new_file.path; + index_size = head_to_index->new_file.size; + index_flags = (jint) head_to_index->new_file.flags; + index_mode = head_to_index->new_file.mode; + index_id = populate_oids ? git_java_objectid_init(env, &(head_to_index->new_file.id)) : NULL; + } + + git_diff_delta *index_to_workdir = status->index_to_workdir; + if (index_to_workdir) { + if (!head_to_index) { + index_path = index_to_workdir->old_file.path; + index_size = index_to_workdir->old_file.size; + index_flags = (jint) index_to_workdir->old_file.flags; + index_mode = index_to_workdir->old_file.mode; + index_id = populate_oids ? git_java_objectid_init(env, &(index_to_workdir->old_file.id)) : NULL; + } + wt_path = index_to_workdir->new_file.path; + wt_size = index_to_workdir->new_file.size; + wt_flags = (jint) index_to_workdir->new_file.flags; + wt_mode = index_to_workdir->new_file.mode; + wt_id = populate_oids ? git_java_objectid_init(env, &(index_to_workdir->new_file.id)) : NULL; + } + + jobject head_path_java = head_path != NULL ? (*env)->NewStringUTF(env, head_path) : NULL; + jobject index_path_java; + if (index_path == NULL) { + index_path_java = NULL; + } + else if (head_path != NULL && strcmp(head_path, index_path) == 0) { + index_path_java = head_path_java; + } + else { + index_path_java = (*env)->NewStringUTF(env, index_path); + } + + jobject wt_path_java; + if (wt_path == NULL) { + wt_path_java = NULL; + } + else if (index_path != NULL && strcmp(index_path, wt_path) == 0) { + wt_path_java = index_path_java; + } + else { + wt_path_java = (*env)->NewStringUTF(env, wt_path); + } + + // We are using only one large status-object and we are populating oids only if requested, + // because creating Java objects contributes a significant amount to the overall execution + // time. For example, using MSVC compiler and testing against https://github.com/varigit/linux-2.6-imx + // (~46K files), I'm seeing following average timings: + // - 822 ms when creating no Java objects at all + // - 875 ms with one big Status object without populating OIDs + // - 1132 ms with one big Status object and populating OIDs (1 + 3 objects) + // - 1266 ms with one Status object, 2 DiffDelta objects, 4 DiffFile objects and 4 ObjectId objects + jobject status_java = jni_constructor_invoke(status_constructor, env, status_flags, + head_path_java, head_size, head_flags, head_mode, head_id, + index_path_java, index_size, index_flags, index_mode, index_id, + wt_path_java, wt_size, wt_flags, wt_mode, wt_id, + head_index_flags, head_index_similarity, head_index_nfiles, + index_wt_flags, index_wt_similarity, index_wt_nfiles); + return status_java; +} + +JNIEXPORT jobject JNICALL +Java_org_libgit2_jagged_core_NativeMethods_statusOptionsInit( + JNIEnv *env, + jclass class) { + assert(env); + assert(class); + + int error; + git_status_options options; + if ((error = git_status_init_options(&options, GIT_STATUS_OPTIONS_VERSION)) < 0) { + git_java_exception_throw_giterr(env, error); + return NULL; + } + + char *enum_name; + if (options.show == GIT_STATUS_SHOW_INDEX_ONLY) { + enum_name = STATUS_SHOW_INDEX_ONLY; + } + else if (options.show == GIT_STATUS_SHOW_WORKDIR_ONLY) { + enum_name = STATUS_SHOW_WORKDIR_ONLY; + } + else { + enum_name = STATUS_SHOW_INDEX_AND_WORKDIR; + } + + jclass status_options_class; + jmethodID status_options_init; + jobject status_show_options_class; + jobject status_show_options; + if (!jni_get_class(&status_options_class, "org/libgit2/jagged/status/StatusOptions", env) || + !jni_get_method_id_by_class(&status_options_init, status_options_class, "", + "(Lorg/libgit2/jagged/status/StatusShowOptions;I[Ljava/lang/String;)V", + env) || + !jni_get_class(&status_show_options_class, "org/libgit2/jagged/status/StatusShowOptions", env) || + !jni_get_static_object_field(&status_show_options, status_show_options_class, enum_name, "Lorg/libgit2/jagged/status/StatusShowOptions;", env)) + return NULL; + + int count = (int) options.pathspec.count; + jobjectArray pathspecs_array; + if (!jni_create_object_array(&pathspecs_array, "java/lang/String", count, env)) + return NULL; + + jsize i; + for (i = 0; i < count; ++i) { + (*env)->SetObjectArrayElement(env, pathspecs_array, i, (*env)->NewStringUTF(env, options.pathspec.strings[i])); + } + + return (*env)->NewObject(env, status_options_class, status_options_init, status_show_options, options.flags, pathspecs_array); +} + +JNIEXPORT jobject JNICALL +Java_org_libgit2_jagged_core_NativeMethods_statusListNew( + JNIEnv *env, + jclass class, + jobject repo_java, + jobject options_java, + jboolean populate_oids) { + git_repository *repo; + int error; + git_status_options options; + git_status_list *status = NULL; + jobjectArray status_array = NULL; + + assert(env); + assert(class); + assert(repo_java); + + if ((error = git_status_init_options(&options, GIT_STATUS_OPTIONS_VERSION)) < 0) { + git_java_exception_throw_giterr(env, error); + return NULL; + } + + jobject status_show_options; + jobject status_show_options_name; + jint flags; + jni_constructor oid_constructor; + jni_constructor status_constructor; + if (!jni_get_object_field(&status_show_options, options_java, "show", "Lorg/libgit2/jagged/status/StatusShowOptions;", env) || + !jni_call_object_method(&status_show_options_name, status_show_options, "java/lang/Enum", "name", "()Ljava/lang/String;", env) || + !jni_get_int_field(&flags, options_java, "flags", env) || + !git_java_objectid_constructor(env, &oid_constructor) || + !jni_constructor_init(&status_constructor, "org/libgit2/jagged/status/Status", + "(I" + "Ljava/lang/String;JIILorg/libgit2/jagged/ObjectId;" + "Ljava/lang/String;JIILorg/libgit2/jagged/ObjectId;" + "Ljava/lang/String;JIILorg/libgit2/jagged/ObjectId;" + "III" + "III)V", env)) + return NULL; + + const char *show_name = git_java_jstring_to_utf8(env, status_show_options_name); + if (strcmp(show_name, STATUS_SHOW_INDEX_ONLY) == 0) { + options.show = GIT_STATUS_SHOW_INDEX_ONLY; + } + else if (strcmp(show_name, STATUS_SHOW_WORKDIR_ONLY) == 0) { + options.show = GIT_STATUS_SHOW_WORKDIR_ONLY; + } + else if (strcmp(show_name, STATUS_SHOW_INDEX_AND_WORKDIR) == 0) { + options.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; + } + else { + goto done; + } + + options.flags = (unsigned int) flags; + + jobject pathSpecs_field; + if (!jni_get_object_field(&pathSpecs_field, options_java, "pathSpecs", "[Ljava/lang/String;", env)) + goto done; + + if (pathSpecs_field != NULL) { + jsize count = (*env)->GetArrayLength(env, pathSpecs_field); + options.pathspec.count = (size_t) count; + options.pathspec.strings = malloc(sizeof(char *) * count); + + int i; + for (i = 0; i < count; i++) { + jobject pathspec_java = (*env)->GetObjectArrayElement(env, pathSpecs_field, i); + const char *pathspec = git_java_jstring_to_utf8(env, pathspec_java); + // TODO: what's wrong with this line so we have to disable warnings (see #pragma)? +#ifdef _WIN32 + options.pathspec.strings[i] = _strdup(pathspec); +#else + options.pathspec.strings[i] = strdup(pathspec); +#endif + git_java_utf8_free(env, pathspec_java, pathspec); + } + } + + repo = git_java_handle_get(env, repo_java); + + if ((error = git_status_list_new(&status, repo, &options)) < 0) { + git_java_exception_throw_giterr(env, error); + goto done; + } + + size_t i, count = git_status_list_entrycount(status); + + status_array = (*env)->NewObjectArray(env, (jsize) count, status_constructor.class, NULL); + if (status_array == NULL) + goto done; + + + for (i = 0; i < count; ++i) { + const git_status_entry *entry = git_status_byindex(status, i); + jobject status_java = create_status_java(entry, &status_constructor, (int)populate_oids, env); + (*env)->SetObjectArrayElement(env, status_array, (jsize) i, status_java); + } + + done: + if (status != NULL) + git_status_list_free(status); + if (show_name != NULL) + git_java_utf8_free(env, status_show_options_name, show_name); + git_strarray_free(&options.pathspec); + return status_array; +} + diff --git a/src/test/java/org/libgit2/jagged/GitLocalCommandTest.java b/src/test/java/org/libgit2/jagged/GitLocalCommandTest.java new file mode 100644 index 0000000..3c5af92 --- /dev/null +++ b/src/test/java/org/libgit2/jagged/GitLocalCommandTest.java @@ -0,0 +1,119 @@ +package org.libgit2.jagged; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Assert; +import org.junit.Before; +import org.libgit2.jagged.status.Status; +import org.libgit2.jagged.status.StatusOptions; + +public abstract class GitLocalCommandTest extends GitTest +{ + + private final Map pathToStatus = new HashMap(); + protected File repoPath; + + @Before + public void before() + { + pathToStatus.clear(); + } + + protected final void write(String path, String content) throws IOException + { + final File file = new File(repoPath, path); + file.getParentFile().mkdirs(); + + FileWriter fileWriter = new FileWriter(file); + try + { + fileWriter.write(content); + } finally + { + fileWriter.close(); + } + } + + protected final void statusChanged() + { + final StatusOptions options = StatusOptions.createInitializedInstance(); + options.setFlags(options.getFlags() | StatusOptions.GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS | StatusOptions.GIT_STATUS_OPT_INCLUDE_UNTRACKED); + status(options); + } + + protected final void status(StatusOptions options) + { + pathToStatus.clear(); + + Repository repository = new Repository(repoPath.getPath()); + final Status[] status = repository.statusListNew(options, true); + for (Status s : status) + { + String path = null; + if (s.getWtPath() != null) + { + path = s.getWtPath(); + } else if (s.getIndexPath() != null) + { + path = s.getIndexPath(); + } else + { + Assert.fail("either indexPath or wtPath must be set"); + } + pathToStatus.put(path, s); + } + repository.close(); + } + + protected final void assertStatus(String mainPath, int status) + { + final Status s = pathToStatus.remove(mainPath); + Assert.assertNotNull(s); + Assert.assertEquals(status, s.getStatus()); + } + + protected final void assertStatus(String mainPath, int status, + String headPath, long headSize, int headFlags, int headMode, ObjectId headId, + String indexPath, long indexSize, int indexFlags, int indexMode, ObjectId indexId, + String wtPath, Long wtSize, int wtFlags, int wtMode, ObjectId wtId, + int headIndexFlags, int headIndexSimilarity, int headIndexNFiles, + int indexWtFlags, int indexWtSimilarity, int indexWtNFiles) + { + final Status s = pathToStatus.remove(mainPath); + Assert.assertNotNull(s); + Assert.assertEquals(status, s.getStatus()); + Assert.assertEquals(headPath, s.getHeadPath()); + Assert.assertEquals(headSize, s.getHeadSize()); + Assert.assertEquals(headFlags, s.getHeadFlags()); + Assert.assertEquals(headMode, s.getHeadMode()); + Assert.assertEquals(headId, s.getHeadId()); + Assert.assertEquals(indexPath, s.getIndexPath()); + Assert.assertEquals(indexSize, s.getIndexSize()); + Assert.assertEquals(indexFlags, s.getIndexFlags()); + Assert.assertEquals(indexMode, s.getIndexMode()); + Assert.assertEquals(indexId, s.getIndexId()); + Assert.assertEquals(wtPath, s.getWtPath()); + if (wtSize != null) + { + Assert.assertEquals(wtSize.longValue(), s.getWtSize()); + } + Assert.assertEquals(wtFlags, s.getWtFlags()); + Assert.assertEquals(wtMode, s.getWtMode()); + Assert.assertEquals(wtId, s.getWtId()); + Assert.assertEquals(headIndexFlags, s.getHeadIndexFlags()); + Assert.assertEquals(headIndexSimilarity, s.getHeadIndexSimilarity()); + Assert.assertEquals(headIndexNFiles, s.getHeadIndexNFiles()); + Assert.assertEquals(indexWtFlags, s.getIndexWtFlags()); + Assert.assertEquals(indexWtSimilarity, s.getIndexWtSimilarity()); + Assert.assertEquals(indexWtNFiles, s.getIndexWtNFiles()); + } + + protected final void assertEmpty() + { + Assert.assertTrue(pathToStatus.isEmpty()); + } +} diff --git a/src/test/java/org/libgit2/jagged/StatusTest.java b/src/test/java/org/libgit2/jagged/StatusTest.java new file mode 100644 index 0000000..d02562c --- /dev/null +++ b/src/test/java/org/libgit2/jagged/StatusTest.java @@ -0,0 +1,118 @@ +package org.libgit2.jagged; + +import org.junit.Before; +import org.junit.Test; +import org.libgit2.jagged.status.Status; +import org.libgit2.jagged.status.StatusOptions; + +public class StatusTest extends GitLocalCommandTest +{ + + private static String[] ENTRY_PATHS = new String[]{ + "file_deleted", + "ignored_file", + "modified_file", + "new_file", + "staged_changes", + "staged_changes_file_deleted", + "staged_changes_modified_file", + "staged_delete_file_deleted", + "staged_delete_modified_file", + "staged_new_file", + "staged_new_file_deleted_file", + "staged_new_file_modified_file", + + "subdir/deleted_file", + "subdir/modified_file", + "subdir/new_file", + + "" + (char)36825, + }; + + private static int ENTRY_STATUSES[] = { + Status.GIT_STATUS_WT_DELETED, + Status.GIT_STATUS_IGNORED, + Status.GIT_STATUS_WT_MODIFIED, + Status.GIT_STATUS_WT_NEW, + Status.GIT_STATUS_INDEX_MODIFIED, + Status.GIT_STATUS_INDEX_MODIFIED | Status.GIT_STATUS_WT_DELETED, + Status.GIT_STATUS_INDEX_MODIFIED | Status.GIT_STATUS_WT_MODIFIED, + Status.GIT_STATUS_INDEX_DELETED, + Status.GIT_STATUS_INDEX_DELETED | Status.GIT_STATUS_WT_NEW, + Status.GIT_STATUS_INDEX_NEW, + Status.GIT_STATUS_INDEX_NEW | Status.GIT_STATUS_WT_DELETED, + Status.GIT_STATUS_INDEX_NEW | Status.GIT_STATUS_WT_MODIFIED, + + Status.GIT_STATUS_WT_DELETED, + Status.GIT_STATUS_WT_MODIFIED, + Status.GIT_STATUS_WT_NEW, + + Status.GIT_STATUS_WT_NEW, + }; + + @Before + public void before() + { + repoPath = setupRepository("status"); + } + + @Test + public void testStatuses() + { + final StatusOptions options = StatusOptions.createInitializedInstance(); + options.setFlags(options.getFlags() + | StatusOptions.GIT_STATUS_OPT_INCLUDE_UNTRACKED + | StatusOptions.GIT_STATUS_OPT_INCLUDE_IGNORED); + + status(options); + for (int index = 0; index < ENTRY_PATHS.length; index++) + { + String entryPath = ENTRY_PATHS[index]; + assertStatus(entryPath, ENTRY_STATUSES[index]); + } + + assertEmpty(); + } + + @Test + public void testSingleStatusDetails() + { + final StatusOptions options = StatusOptions.createInitializedInstance(); + options.setPathSpecs(new String[]{"current_file"}); + options.setFlags(options.getFlags() | StatusOptions.GIT_STATUS_OPT_INCLUDE_UNMODIFIED); + + status(options); + assertStatus("current_file", 0, + "current_file", 0, Status.GIT_DIFF_FLAG_EXISTS | Status.GIT_DIFF_FLAG_VALID_ID, Status.GIT_FILEMODE_BLOB, new ObjectId("a0de7e0ac200c489c41c59dfa910154a70264e6e"), + "current_file", 13, Status.GIT_DIFF_FLAG_EXISTS | Status.GIT_DIFF_FLAG_VALID_ID, Status.GIT_FILEMODE_BLOB, new ObjectId("a0de7e0ac200c489c41c59dfa910154a70264e6e"), + "current_file", null, Status.GIT_DIFF_FLAG_EXISTS, Status.GIT_FILEMODE_BLOB, new ObjectId("a0de7e0ac200c489c41c59dfa910154a70264e6e"), + 0, 0, 0, 0, 0, 0); + } + + @Test + public void testStatusForMultipleSingleFilesAtOnce() + { + final StatusOptions options = StatusOptions.createInitializedInstance(); + options.setFlags(options.getFlags() | StatusOptions.GIT_STATUS_OPT_INCLUDE_IGNORED); + options.setPathSpecs(new String[]{ENTRY_PATHS[0]}); + + status(options); + assertStatus(ENTRY_PATHS[0], ENTRY_STATUSES[0]); + assertEmpty(); + + options.setPathSpecs(new String[]{ENTRY_PATHS[0], ENTRY_PATHS[1]}); + + status(options); + assertStatus(ENTRY_PATHS[0], ENTRY_STATUSES[0]); + assertStatus(ENTRY_PATHS[1], ENTRY_STATUSES[1]); + assertEmpty(); + + options.setPathSpecs(new String[]{ENTRY_PATHS[0], ENTRY_PATHS[1], ENTRY_PATHS[13]}); + + status(options); + assertStatus(ENTRY_PATHS[0], ENTRY_STATUSES[0]); + assertStatus(ENTRY_PATHS[1], ENTRY_STATUSES[1]); + assertStatus(ENTRY_PATHS[13], ENTRY_STATUSES[13]); + assertEmpty(); + } +}