summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Sohn <matthias.sohn@sap.com>2015-12-15 00:07:22 +0100
committerMatthias Sohn <matthias.sohn@sap.com>2015-12-15 00:10:47 +0100
commitedec10dc3909e15cbb29a7936ec42ae72c526792 (patch)
tree53f91989049e554ed3c601530c3de8aed01ac70c
parent35109dc66cf7c4408b41a7dcdde6aec1b8e08372 (diff)
parent8179185d11bdf1687dbb6db4c39ae7cde5799d09 (diff)
downloadjgit-edec10dc3909e15cbb29a7936ec42ae72c526792.tar.gz
jgit-edec10dc3909e15cbb29a7936ec42ae72c526792.zip
Merge branch 'master' into stable-4.2
Change-Id: Ia92c91e1226da7d6455ab14f1e255a1546f8f357 Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java4
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java4
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java4
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java18
-rw-r--r--org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java11
-rw-r--r--org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java5
-rw-r--r--org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java1
-rw-r--r--org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java24
-rw-r--r--org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java6
-rw-r--r--org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java7).launch6
-rw-r--r--org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java8).launch1
-rw-r--r--org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java3
-rw-r--r--org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/RemoteTest.java159
-rw-r--r--org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ResetTest.java8
-rw-r--r--org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/StatusTest.java5
-rw-r--r--org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin1
-rw-r--r--org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties5
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Branch.java10
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Commit.java3
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java10
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Push.java4
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Remote.java197
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevParse.java18
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java7
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java5
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java7
-rw-r--r--org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java3
-rw-r--r--org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 7).launch1
-rw-r--r--org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 8).launch1
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AbstractRemoteCommandTest.java96
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java188
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java136
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java6
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java54
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NameRevCommandTest.java4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java8
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteAddCommandTest.java81
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteDeleteCommandTest.java68
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteListCommandTest.java68
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteSetUrlCommandTest.java100
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java6
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java102
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java20
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java (renamed from org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributeNodeTest.java)29
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java55
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java866
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java31
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheEntryTest.java4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java38
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java49
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java8
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/junit/TestRepositoryTest.java40
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java13
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java24
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java48
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SquashMessageFormatterTest.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java200
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushConnectionTest.java143
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java12
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java15
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java41
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/CanonicalTreeParserTest.java41
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorJava7Test.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/TreeWalkJava7Test.java1
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSJava7Test.java2
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtils7Test.java1
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java21
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java65
-rw-r--r--org.eclipse.jgit/.settings/.api_filters30
-rw-r--r--org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs2
-rw-r--r--org.eclipse.jgit/META-INF/MANIFEST.MF52
-rw-r--r--org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java69
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/annotations/Nullable.java41
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java44
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java27
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteAddCommand.java133
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteListCommand.java91
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java110
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java155
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/errors/FilterFailedException.java144
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java16
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attributes.java202
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java81
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java55
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java19
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java38
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java81
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java133
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java31
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/errors/IndexReadException.java82
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java37
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java180
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java63
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GlobalAttributesNode.java87
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java81
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java37
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java34
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java28
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java36
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RefRename.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java164
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java31
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java24
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java15
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java26
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java35
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java28
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java44
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java66
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java379
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java191
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java91
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java15
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java31
147 files changed, 5994 insertions, 776 deletions
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java
index dec9b59f2d..362a09d64f 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientDumbServerTest.java
@@ -209,7 +209,7 @@ public class DumbClientDumbServerTest extends HttpTestCase {
}
assertTrue(dst.hasObject(A_txt));
- assertEquals(B, dst.getRef(master).getObjectId());
+ assertEquals(B, dst.exactRef(master).getObjectId());
fsck(dst, B);
List<AccessEvent> loose = getRequests(loose(remoteURI, A_txt));
@@ -234,7 +234,7 @@ public class DumbClientDumbServerTest extends HttpTestCase {
}
assertTrue(dst.hasObject(A_txt));
- assertEquals(B, dst.getRef(master).getObjectId());
+ assertEquals(B, dst.exactRef(master).getObjectId());
fsck(dst, B);
List<AccessEvent> req;
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java
index e385b9538d..da3a09809b 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/DumbClientSmartServerTest.java
@@ -225,7 +225,7 @@ public class DumbClientSmartServerTest extends HttpTestCase {
}
assertTrue(dst.hasObject(A_txt));
- assertEquals(B, dst.getRef(master).getObjectId());
+ assertEquals(B, dst.exactRef(master).getObjectId());
fsck(dst, B);
List<AccessEvent> loose = getRequests(loose(remoteURI, A_txt));
@@ -253,7 +253,7 @@ public class DumbClientSmartServerTest extends HttpTestCase {
}
assertTrue(dst.hasObject(A_txt));
- assertEquals(B, dst.getRef(master).getObjectId());
+ assertEquals(B, dst.exactRef(master).getObjectId());
fsck(dst, B);
List<AccessEvent> req;
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
index f1056f2d6a..d67c8173cb 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HookMessageTest.java
@@ -164,8 +164,8 @@ public class HookMessageTest extends HttpTestCase {
}
assertTrue(remoteRepository.hasObject(Q_txt));
- assertNotNull("has " + dstName, remoteRepository.getRef(dstName));
- assertEquals(Q, remoteRepository.getRef(dstName).getObjectId());
+ assertNotNull("has " + dstName, remoteRepository.exactRef(dstName));
+ assertEquals(Q, remoteRepository.exactRef(dstName).getObjectId());
fsck(remoteRepository, Q);
List<AccessEvent> requests = getRequests();
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
index 1b6c552cef..9ca0789e29 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
@@ -296,7 +296,7 @@ public class SmartClientSmartServerTest extends HttpTestCase {
}
assertTrue(dst.hasObject(A_txt));
- assertEquals(B, dst.getRef(master).getObjectId());
+ assertEquals(B, dst.exactRef(master).getObjectId());
fsck(dst, B);
List<AccessEvent> requests = getRequests();
@@ -337,7 +337,7 @@ public class SmartClientSmartServerTest extends HttpTestCase {
} finally {
t.close();
}
- assertEquals(B, dst.getRepository().getRef(master).getObjectId());
+ assertEquals(B, dst.getRepository().exactRef(master).getObjectId());
List<AccessEvent> cloneRequests = getRequests();
// Only create a few new commits.
@@ -358,7 +358,7 @@ public class SmartClientSmartServerTest extends HttpTestCase {
} finally {
t.close();
}
- assertEquals(Z, dst.getRepository().getRef(master).getObjectId());
+ assertEquals(Z, dst.getRepository().exactRef(master).getObjectId());
List<AccessEvent> requests = getRequests();
requests.removeAll(cloneRequests);
@@ -400,7 +400,7 @@ public class SmartClientSmartServerTest extends HttpTestCase {
} finally {
t.close();
}
- assertEquals(B, dst.getRepository().getRef(master).getObjectId());
+ assertEquals(B, dst.getRepository().exactRef(master).getObjectId());
List<AccessEvent> cloneRequests = getRequests();
// Force enough into the local client that enumeration will
@@ -424,7 +424,7 @@ public class SmartClientSmartServerTest extends HttpTestCase {
} finally {
t.close();
}
- assertEquals(Z, dst.getRepository().getRef(master).getObjectId());
+ assertEquals(Z, dst.getRepository().exactRef(master).getObjectId());
List<AccessEvent> requests = getRequests();
requests.removeAll(cloneRequests);
@@ -579,8 +579,8 @@ public class SmartClientSmartServerTest extends HttpTestCase {
}
assertTrue(remoteRepository.hasObject(Q_txt));
- assertNotNull("has " + dstName, remoteRepository.getRef(dstName));
- assertEquals(Q, remoteRepository.getRef(dstName).getObjectId());
+ assertNotNull("has " + dstName, remoteRepository.exactRef(dstName));
+ assertEquals(Q, remoteRepository.exactRef(dstName).getObjectId());
fsck(remoteRepository, Q);
final ReflogReader log = remoteRepository.getReflogReader(dstName);
@@ -657,8 +657,8 @@ public class SmartClientSmartServerTest extends HttpTestCase {
}
assertTrue(remoteRepository.hasObject(Q_bin));
- assertNotNull("has " + dstName, remoteRepository.getRef(dstName));
- assertEquals(Q, remoteRepository.getRef(dstName).getObjectId());
+ assertNotNull("has " + dstName, remoteRepository.exactRef(dstName));
+ assertEquals(Q, remoteRepository.exactRef(dstName).getObjectId());
fsck(remoteRepository, Q);
List<AccessEvent> requests = getRequests();
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java
index 521593ea80..2962e7192f 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/JGitTestUtil.java
@@ -241,6 +241,17 @@ public abstract class JGitTestUtil {
FileUtils.delete(path);
}
+ /**
+ * @param db
+ * the repository
+ * @param link
+ * the path of the symbolic link to create
+ * @param target
+ * the target of the symbolic link
+ * @return the path to the symbolic link
+ * @throws Exception
+ * @since 4.2
+ */
public static Path writeLink(Repository db, String link,
String target) throws Exception {
return FileUtils.createSymLink(new File(db.getWorkTree(), link),
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
index bb5f9efb8f..4d713b5e73 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java
@@ -91,7 +91,10 @@ public abstract class LocalDiskRepositoryTestCase {
/** A fake (but stable) identity for committer fields in the test. */
protected PersonIdent committer;
- /** A {@link SystemReader} used to coordinate time, envars, etc. */
+ /**
+ * A {@link SystemReader} used to coordinate time, envars, etc.
+ * @since 4.2
+ */
protected MockSystemReader mockSystemReader;
private final List<Repository> toClose = new ArrayList<Repository>();
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
index 03a2b1a584..28a95569e8 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/MockSystemReader.java
@@ -151,6 +151,7 @@ public class MockSystemReader extends SystemReader {
*
* @param secDelta
* number of seconds to add to the current time.
+ * @since 4.2
*/
public void tick(final int secDelta) {
now += secDelta * 1000L;
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java
index 28c61778c7..c649eb9086 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java
@@ -108,6 +108,17 @@ public abstract class RepositoryTestCase extends LocalDiskRepositoryTestCase {
return JGitTestUtil.writeTrashFile(db, name, data);
}
+ /**
+ * Create a symbolic link
+ *
+ * @param link
+ * the path of the symbolic link to create
+ * @param target
+ * the target of the symbolic link
+ * @return the path to the symbolic link
+ * @throws Exception
+ * @since 4.2
+ */
protected Path writeLink(final String link, final String target)
throws Exception {
return JGitTestUtil.writeLink(db, link, target);
@@ -272,6 +283,19 @@ public abstract class RepositoryTestCase extends LocalDiskRepositoryTestCase {
}
/**
+ * Replaces '\' by '/'
+ *
+ * @param str
+ * the string in which backslashes should be replaced
+ * @return the resulting string with slashes
+ * @since 4.2
+ */
+ public static String slashify(String str) {
+ str = str.replace('\\', '/');
+ return str;
+ }
+
+ /**
* Waits until it is guaranteed that a subsequent file modification has a
* younger modification timestamp than the modification timestamp of the
* given file. This is done by touching a temporary file, reading the
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
index 251e65f553..ac9685d375 100644
--- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
+++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
@@ -175,6 +175,7 @@ public class TestRepository<R extends Repository> {
* the MockSystemReader to use for clock and other system
* operations.
* @throws IOException
+ * @since 4.2
*/
public TestRepository(R db, RevWalk rw, MockSystemReader reader)
throws IOException {
@@ -203,7 +204,10 @@ public class TestRepository<R extends Repository> {
return git;
}
- /** @return current date. */
+ /**
+ * @return current date.
+ * @since 4.2
+ */
public Date getDate() {
return new Date(mockSystemReader.getCurrentTime());
}
diff --git a/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java7).launch b/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java7).launch
index 600ce7b729..3df0dcb645 100644
--- a/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java7).launch
+++ b/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java7).launch
@@ -15,12 +15,6 @@
<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
-<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;org.eclipse.jgit.pgm.test&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry path=&quot;3&quot; projectName=&quot;org.eclipse.jgit.java7&quot; type=&quot;1&quot;/&gt;&#10;"/>
-</listAttribute>
-<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.jgit.pgm.test"/>
diff --git a/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java8).launch b/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java8).launch
index 1b61d3d3f1..ce473ed03d 100644
--- a/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java8).launch
+++ b/org.eclipse.jgit.pgm.test/org.eclipse.jgit.pgm--All-Tests (Java8).launch
@@ -19,7 +19,6 @@
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;org.eclipse.jgit.pgm.test&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry path=&quot;3&quot; projectName=&quot;org.eclipse.jgit.java7&quot; type=&quot;1&quot;/&gt;&#10;"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java
index 7bf4c26c38..939a9f6fdd 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java
@@ -198,7 +198,8 @@ public class CheckoutTest extends CLIRepositoryTestCase {
assertStringArrayEquals("Switched to a new branch 'new_branch'",
execute("git checkout --orphan new_branch"));
- assertEquals("refs/heads/new_branch", db.getRef("HEAD").getTarget().getName());
+ assertEquals("refs/heads/new_branch",
+ db.exactRef("HEAD").getTarget().getName());
RevCommit commit = git.commit().setMessage("orphan commit").call();
assertEquals(0, commit.getParentCount());
}
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/RemoteTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/RemoteTest.java
new file mode 100644
index 0000000000..58e0e19f8d
--- /dev/null
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/RemoteTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r@zend.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.pgm;
+
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.lib.CLIRepositoryTestCase;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.URIish;
+import org.junit.Before;
+import org.junit.Test;
+
+public class RemoteTest extends CLIRepositoryTestCase {
+
+ private StoredConfig config;
+
+ private RemoteConfig remote;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ // create another repository
+ Repository remoteRepository = createWorkRepository();
+
+ // set it up as a remote to this repository
+ config = db.getConfig();
+ remote = new RemoteConfig(config, "test");
+ remote.addFetchRefSpec(
+ new RefSpec("+refs/heads/*:refs/remotes/test/*"));
+ URIish uri = new URIish(
+ remoteRepository.getDirectory().toURI().toURL());
+ remote.addURI(uri);
+ remote.update(config);
+ config.save();
+
+ Git.wrap(remoteRepository).commit().setMessage("initial commit").call();
+ }
+
+ @Test
+ public void testList() throws Exception {
+ assertArrayEquals(new String[] { remote.getName(), "" },
+ execute("git remote"));
+ }
+
+ @Test
+ public void testVerboseList() throws Exception {
+ assertArrayEquals(
+ new String[] {
+ String.format("%s\t%s (fetch)", remote.getName(),
+ remote.getURIs().get(0)),
+ String.format("%s\t%s (push)", remote.getName(),
+ remote.getURIs().get(0)),
+ "" },
+ execute("git remote -v"));
+ }
+
+ @Test
+ public void testAdd() throws Exception {
+ assertArrayEquals(new String[] { "" },
+ execute("git remote add second git://test.com/second"));
+
+ List<RemoteConfig> remotes = RemoteConfig.getAllRemoteConfigs(config);
+ assertEquals(2, remotes.size());
+ assertEquals("second", remotes.get(0).getName());
+ assertEquals("test", remotes.get(1).getName());
+ }
+
+ @Test
+ public void testRemove() throws Exception {
+ assertArrayEquals(new String[] { "" },
+ execute("git remote remove test"));
+
+ assertTrue(RemoteConfig.getAllRemoteConfigs(config).isEmpty());
+ }
+
+ @Test
+ public void testSetUrl() throws Exception {
+ assertArrayEquals(new String[] { "" },
+ execute("git remote set-url test git://test.com/test"));
+
+ RemoteConfig result = new RemoteConfig(config, "test");
+ assertEquals("test", result.getName());
+ assertArrayEquals(new URIish[] { new URIish("git://test.com/test") },
+ result.getURIs().toArray());
+ assertTrue(result.getPushURIs().isEmpty());
+ }
+
+ @Test
+ public void testSetUrlPush() throws Exception {
+ assertArrayEquals(new String[] { "" },
+ execute("git remote set-url --push test git://test.com/test"));
+
+ RemoteConfig result = new RemoteConfig(config, "test");
+ assertEquals("test", result.getName());
+ assertEquals(remote.getURIs(), result.getURIs());
+ assertArrayEquals(new URIish[] { new URIish("git://test.com/test") },
+ result.getPushURIs().toArray());
+ }
+
+ @Test
+ public void testUpdate() throws Exception {
+ assertArrayEquals(new String[] {
+ "From " + remote.getURIs().get(0).toString(),
+ " * [new branch] master -> test/master", "", "" },
+ execute("git remote update test"));
+ }
+
+}
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ResetTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ResetTest.java
index 0785c5c5ff..dae477928b 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ResetTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ResetTest.java
@@ -67,7 +67,7 @@ public class ResetTest extends CLIRepositoryTestCase {
assertStringArrayEquals("",
execute("git reset --hard " + commit.getId().name()));
assertEquals(commit.getId(),
- git.getRepository().getRef("HEAD").getObjectId());
+ git.getRepository().exactRef("HEAD").getObjectId());
}
@Test
@@ -77,7 +77,7 @@ public class ResetTest extends CLIRepositoryTestCase {
assertStringArrayEquals("",
execute("git reset --hard " + commit.getId().name()));
assertEquals(commit.getId(),
- git.getRepository().getRef("HEAD").getObjectId());
+ git.getRepository().exactRef("HEAD").getObjectId());
}
@Test
@@ -86,7 +86,7 @@ public class ResetTest extends CLIRepositoryTestCase {
assertStringArrayEquals("",
execute("git reset --hard " + commit.getId().name() + " --"));
assertEquals(commit.getId(),
- git.getRepository().getRef("HEAD").getObjectId());
+ git.getRepository().exactRef("HEAD").getObjectId());
}
@Test
@@ -119,7 +119,7 @@ public class ResetTest extends CLIRepositoryTestCase {
(useDoubleDash) ? " --" : "");
assertStringArrayEquals("", execute(cmd));
assertEquals(commit.getId(),
- git.getRepository().getRef("HEAD").getObjectId());
+ git.getRepository().exactRef("HEAD").getObjectId());
org.eclipse.jgit.api.Status status = git.status().call();
// assert that file a is unstaged
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/StatusTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/StatusTest.java
index 793fc7daf6..854c52d88b 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/StatusTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/StatusTest.java
@@ -42,13 +42,14 @@
*/
package org.eclipse.jgit.pgm;
+import static org.eclipse.jgit.lib.Constants.MASTER;
+import static org.eclipse.jgit.lib.Constants.R_HEADS;
import java.io.IOException;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.CLIRepositoryTestCase;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Test;
@@ -254,7 +255,7 @@ public class StatusTest extends CLIRepositoryTestCase {
}
private void detachHead(Git git) throws IOException, GitAPIException {
- String commitId = db.getRef(Constants.MASTER).getObjectId().name();
+ String commitId = db.exactRef(R_HEADS + MASTER).getObjectId().name();
git.checkout().setName(commitId).call();
}
diff --git a/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin b/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
index e1b05491b7..c13f63e80f 100644
--- a/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
+++ b/org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
@@ -24,6 +24,7 @@ org.eclipse.jgit.pgm.MergeBase
org.eclipse.jgit.pgm.Push
org.eclipse.jgit.pgm.ReceivePack
org.eclipse.jgit.pgm.Reflog
+org.eclipse.jgit.pgm.Remote
org.eclipse.jgit.pgm.Repo
org.eclipse.jgit.pgm.Reset
org.eclipse.jgit.pgm.RevList
diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
index 64afdad51e..335336da28 100644
--- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
+++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
@@ -128,6 +128,7 @@ metaVar_user=USER
metaVar_version=VERSION
mostCommonlyUsedCommandsAre=The most commonly used commands are:
needApprovalToDestroyCurrentRepository=Need approval to destroy current repository
+needSingleRevision=Needed a single revision
noGitRepositoryConfigured=No Git repository configured.
noNamesFound=No names found, cannot describe anything.
noSuchFile=no such file: {0}
@@ -142,6 +143,7 @@ notAJgitCommand={0} is not a jgit command
notARevision=Not a revision: {0}
notATree={0} is not a tree
notAValidRefName={0} is not a valid ref name
+notAValidCommitName={0} is not a valid commit name
notAnIndexFile={0} is not an index file
notAnObject={0} is not an object
notFound=!! NOT FOUND !!
@@ -186,6 +188,7 @@ treeIsRequired=argument tree is required
tooManyRefsGiven=Too many refs given
unknownIoErrorStdout=An unknown I/O error occurred on standard output
unknownMergeStrategy=unknown merge strategy {0} specified
+unknownSubcommand=Unknown subcommand: {0}
unmergedPaths=Unmerged paths:
unsupportedOperation=Unsupported operation: {0}
untrackedFiles=Untracked files:
@@ -220,6 +223,7 @@ usage_MergeBase=Find as good common ancestors as possible for a merge
usage_MergesTwoDevelopmentHistories=Merges two development histories
usage_ReadDirCache= Read the DirCache 100 times
usage_RebuildCommitGraph=Recreate a repository from another one's commit graph
+usage_Remote=Manage set of tracked repositories
usage_RepositoryToReadFrom=Repository to read from
usage_RepositoryToReceiveInto=Repository to receive into
usage_RevList=List commit objects in reverse chronological order
@@ -327,6 +331,7 @@ usage_performFsckStyleChecksOnReceive=perform fsck style checks on receive
usage_portNumberToListenOn=port number to listen on
usage_printOnlyBranchesThatContainTheCommit=print only branches that contain the commit
usage_pruneStaleTrackingRefs=prune stale tracking refs
+usage_pushUrls=push URLs are manipulated
usage_quiet=don't show progress messages
usage_recordChangesToRepository=Record changes to the repository
usage_recurseIntoSubtrees=recurse into subtrees
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Branch.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Branch.java
index 83a1ca7e25..65aa24f356 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Branch.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Branch.java
@@ -154,10 +154,14 @@ class Branch extends TextBuiltin {
startBranch = Constants.HEAD;
Ref startRef = db.getRef(startBranch);
ObjectId startAt = db.resolve(startBranch + "^0"); //$NON-NLS-1$
- if (startRef != null)
+ if (startRef != null) {
startBranch = startRef.getName();
- else
+ } else if (startAt != null) {
startBranch = startAt.name();
+ } else {
+ throw die(MessageFormat.format(
+ CLIText.get().notAValidCommitName, startBranch));
+ }
startBranch = Repository.shortenRefName(startBranch);
String newRefName = newHead;
if (!newRefName.startsWith(Constants.R_HEADS))
@@ -249,7 +253,7 @@ class Branch extends TextBuiltin {
String current = db.getBranch();
ObjectId head = db.resolve(Constants.HEAD);
for (String branch : branches) {
- if (current.equals(branch)) {
+ if (branch.equals(current)) {
throw die(MessageFormat.format(CLIText.get().cannotDeleteTheBranchWhichYouAreCurrentlyOn, branch));
}
RefUpdate update = db.updateRef((remote ? Constants.R_REMOTES
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Commit.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Commit.java
index f18242d684..38d8d70cef 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Commit.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Commit.java
@@ -96,6 +96,9 @@ class Commit extends TextBuiltin {
commitCmd.setAmend(amend);
commitCmd.setAll(all);
Ref head = db.getRef(Constants.HEAD);
+ if (head == null) {
+ throw die(CLIText.get().onBranchToBeBorn);
+ }
RevCommit commit;
try {
commit = commitCmd.call();
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java
index e0ff0583cb..cd65af9549 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Merge.java
@@ -120,7 +120,7 @@ class Merge extends TextBuiltin {
throw die(MessageFormat.format(
CLIText.get().refDoesNotExistOrNoCommit, ref));
- Ref oldHead = db.getRef(Constants.HEAD);
+ Ref oldHead = getOldHead();
MergeResult result;
try (Git git = new Git(db)) {
MergeCommand mergeCmd = git.merge().setStrategy(mergeStrategy)
@@ -205,6 +205,14 @@ class Merge extends TextBuiltin {
}
}
+ private Ref getOldHead() throws IOException {
+ Ref oldHead = db.getRef(Constants.HEAD);
+ if (oldHead == null) {
+ throw die(CLIText.get().onBranchToBeBorn);
+ }
+ return oldHead;
+ }
+
private boolean isMergedInto(Ref oldHead, AnyObjectId src)
throws IOException {
try (RevWalk revWalk = new RevWalk(db)) {
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Push.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Push.java
index 1879ef51ff..9098c1263d 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Push.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Push.java
@@ -82,6 +82,9 @@ class Push extends TextBuiltin {
@Option(name = "--all")
private boolean all;
+ @Option(name = "--atomic")
+ private boolean atomic;
+
@Option(name = "--tags")
private boolean tags;
@@ -122,6 +125,7 @@ class Push extends TextBuiltin {
push.setPushTags();
push.setRemote(remote);
push.setThin(thin);
+ push.setAtomic(atomic);
push.setTimeout(timeout);
Iterable<PushResult> results = push.call();
for (PushResult result : results) {
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Remote.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Remote.java
new file mode 100644
index 0000000000..70868e920e
--- /dev/null
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Remote.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r@zend.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.pgm;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.RemoteAddCommand;
+import org.eclipse.jgit.api.RemoteListCommand;
+import org.eclipse.jgit.api.RemoteRemoveCommand;
+import org.eclipse.jgit.api.RemoteSetUrlCommand;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.pgm.internal.CLIText;
+import org.eclipse.jgit.pgm.opt.CmdLineParser;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.util.io.ThrowingPrintWriter;
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.Option;
+
+@Command(common = false, usage = "usage_Remote")
+class Remote extends TextBuiltin {
+
+ @Option(name = "--verbose", aliases = { "-v" }, usage = "usage_beVerbose")
+ private boolean verbose = false;
+
+ @Option(name = "--prune", aliases = {
+ "-p" }, usage = "usage_pruneStaleTrackingRefs")
+ private boolean prune;
+
+ @Option(name = "--push", usage = "usage_pushUrls")
+ private boolean push;
+
+ @Argument(index = 0, metaVar = "metaVar_command")
+ private String command;
+
+ @Argument(index = 1, metaVar = "metaVar_remoteName")
+ private String name;
+
+ @Argument(index = 2, metaVar = "metaVar_uriish")
+ private String uri;
+
+ @Override
+ protected void run() throws Exception {
+ try (Git git = new Git(db)) {
+ if (command == null) {
+ RemoteListCommand cmd = git.remoteList();
+ List<RemoteConfig> remotes = cmd.call();
+ print(remotes);
+ } else if ("add".equals(command)) { //$NON-NLS-1$
+ RemoteAddCommand cmd = git.remoteAdd();
+ cmd.setName(name);
+ cmd.setUri(new URIish(uri));
+ cmd.call();
+ } else if ("remove".equals(command) || "rm".equals(command)) { //$NON-NLS-1$ //$NON-NLS-2$
+ RemoteRemoveCommand cmd = git.remoteRemove();
+ cmd.setName(name);
+ cmd.call();
+ } else if ("set-url".equals(command)) { //$NON-NLS-1$
+ RemoteSetUrlCommand cmd = git.remoteSetUrl();
+ cmd.setName(name);
+ cmd.setUri(new URIish(uri));
+ cmd.setPush(push);
+ cmd.call();
+ } else if ("update".equals(command)) { //$NON-NLS-1$
+ // reuse fetch command for basic implementation of remote update
+ Fetch fetch = new Fetch();
+ fetch.init(db, gitdir);
+
+ // redirect the output stream
+ StringWriter osw = new StringWriter();
+ fetch.outw = new ThrowingPrintWriter(osw);
+ // redirect the error stream
+ StringWriter esw = new StringWriter();
+ fetch.errw = new ThrowingPrintWriter(esw);
+
+ List<String> fetchArgs = new ArrayList<>();
+ if (verbose) {
+ fetchArgs.add("--verbose"); //$NON-NLS-1$
+ }
+ if (prune) {
+ fetchArgs.add("--prune"); //$NON-NLS-1$
+ }
+ if (name != null) {
+ fetchArgs.add(name);
+ }
+
+ fetch.execute(fetchArgs.toArray(new String[fetchArgs.size()]));
+
+ // flush the streams
+ fetch.outw.flush();
+ fetch.errw.flush();
+ outw.println(osw.toString());
+ errw.println(esw.toString());
+ } else {
+ throw new JGitInternalException(MessageFormat
+ .format(CLIText.get().unknownSubcommand, command));
+ }
+ }
+ }
+
+ @Override
+ public void printUsageAndExit(final String message, final CmdLineParser clp)
+ throws IOException {
+ errw.println(message);
+ errw.println("jgit remote [--verbose (-v)] [--help (-h)]"); //$NON-NLS-1$
+ errw.println("jgit remote add name uri-ish [--help (-h)]"); //$NON-NLS-1$
+ errw.println("jgit remote remove name [--help (-h)]"); //$NON-NLS-1$
+ errw.println("jgit remote rm name [--help (-h)]"); //$NON-NLS-1$
+ errw.println(
+ "jgit remote [--verbose (-v)] update [name] [--prune (-p)] [--help (-h)]"); //$NON-NLS-1$
+ errw.println("jgit remote set-url name uri-ish [--push] [--help (-h)]"); //$NON-NLS-1$
+
+ errw.println();
+ clp.printUsage(errw, getResourceBundle());
+ errw.println();
+
+ errw.flush();
+ throw die(true);
+ }
+
+ private void print(List<RemoteConfig> remotes) throws IOException {
+ for (RemoteConfig remote : remotes) {
+ String remoteName = remote.getName();
+ if (verbose) {
+ List<URIish> fetchURIs = remote.getURIs();
+ List<URIish> pushURIs = remote.getPushURIs();
+
+ String fetchURI = ""; //$NON-NLS-1$
+ if (!fetchURIs.isEmpty()) {
+ fetchURI = fetchURIs.get(0).toString();
+ } else if (!pushURIs.isEmpty()) {
+ fetchURI = pushURIs.get(0).toString();
+ }
+
+ String pushURI = ""; //$NON-NLS-1$
+ if (!pushURIs.isEmpty()) {
+ pushURI = pushURIs.get(0).toString();
+ } else if (!fetchURIs.isEmpty()) {
+ pushURI = fetchURIs.get(0).toString();
+ }
+
+ outw.println(
+ String.format("%s\t%s (fetch)", remoteName, fetchURI)); //$NON-NLS-1$
+ outw.println(
+ String.format("%s\t%s (push)", remoteName, pushURI)); //$NON-NLS-1$
+ } else {
+ outw.println(remoteName);
+ }
+ }
+ }
+
+}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevParse.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevParse.java
index 5530ac5c99..4456fd5348 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevParse.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/RevParse.java
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2009, Daniel Cheng (aka SDiZ) <git@sdiz.net>
* Copyright (C) 2009, Daniel Cheng (aka SDiZ) <j16sdiz+freenet@gmail.com>
+ * Copyright (C) 2015 Thomas Meyer <thomas@m3y3r.de>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -51,14 +52,19 @@ import java.util.List;
import java.util.Map;
import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.Option;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.pgm.internal.CLIText;;
@Command(usage = "usage_RevParse")
class RevParse extends TextBuiltin {
@Option(name = "--all", usage = "usage_RevParseAll")
- boolean all = false;
+ boolean all;
+
+ @Option(name = "--verify", usage = "usage_RevParseVerify")
+ boolean verify;
@Argument(index = 0, metaVar = "metaVar_commitish")
private final List<ObjectId> commits = new ArrayList<ObjectId>();
@@ -67,11 +73,17 @@ class RevParse extends TextBuiltin {
protected void run() throws Exception {
if (all) {
Map<String, Ref> allRefs = db.getRefDatabase().getRefs(ALL);
- for (final Ref r : allRefs.values())
+ for (final Ref r : allRefs.values()) {
outw.println(r.getObjectId().name());
+ }
} else {
- for (final ObjectId o : commits)
+ if (verify && commits.size() > 1) {
+ throw new CmdLineException(CLIText.get().needSingleRevision);
+ }
+
+ for (final ObjectId o : commits) {
outw.println(o.name());
+ }
}
}
}
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java
index df7ebb78b8..d856989011 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/DiffAlgorithms.java
@@ -242,9 +242,10 @@ class DiffAlgorithms extends TextBuiltin {
}
});
- if (db.getDirectory() != null) {
- String name = db.getDirectory().getName();
- File parent = db.getDirectory().getParentFile();
+ File directory = db.getDirectory();
+ if (directory != null) {
+ String name = directory.getName();
+ File parent = directory.getParentFile();
if (name.equals(Constants.DOT_GIT) && parent != null)
name = parent.getName();
outw.println(name + ": start at " + startId.name());
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
index 494055a265..6260cd99fa 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/RebuildCommitGraph.java
@@ -117,9 +117,12 @@ class RebuildCommitGraph extends TextBuiltin {
@Override
protected void run() throws Exception {
if (!really && !db.getRefDatabase().getRefs(ALL).isEmpty()) {
+ File directory = db.getDirectory();
+ String absolutePath = directory == null ? "null" //$NON-NLS-1$
+ : directory.getAbsolutePath();
errw.println(
MessageFormat.format(CLIText.get().fatalThisProgramWillDestroyTheRepository
- , db.getDirectory().getAbsolutePath(), REALLY));
+ , absolutePath, REALLY));
throw die(CLIText.get().needApprovalToDestroyCurrentRepository);
}
if (!refList.isFile())
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java
index dcbc37bed6..887ad08af7 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/debug/TextHashFunctions.java
@@ -341,9 +341,10 @@ class TextHashFunctions extends TextBuiltin {
}
}
- if (db.getDirectory() != null) {
- String name = db.getDirectory().getName();
- File parent = db.getDirectory().getParentFile();
+ File directory = db.getDirectory();
+ if (directory != null) {
+ String name = directory.getName();
+ File parent = directory.getParentFile();
if (name.equals(Constants.DOT_GIT) && parent != null)
name = parent.getName();
outw.println(name + ":"); //$NON-NLS-1$
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
index 433ddf2b13..f5d581ad01 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
@@ -187,6 +187,7 @@ public class CLIText extends TranslationBundle {
/***/ public String metaVar_version;
/***/ public String mostCommonlyUsedCommandsAre;
/***/ public String needApprovalToDestroyCurrentRepository;
+ /***/ public String needSingleRevision;
/***/ public String noGitRepositoryConfigured;
/***/ public String noNamesFound;
/***/ public String noSuchFile;
@@ -201,6 +202,7 @@ public class CLIText extends TranslationBundle {
/***/ public String notARevision;
/***/ public String notATree;
/***/ public String notAValidRefName;
+ /***/ public String notAValidCommitName;
/***/ public String notAnIndexFile;
/***/ public String notAnObject;
/***/ public String notFound;
@@ -245,6 +247,7 @@ public class CLIText extends TranslationBundle {
/***/ public String treeIsRequired;
/***/ public char[] unknownIoErrorStdout;
/***/ public String unknownMergeStrategy;
+ /***/ public String unknownSubcommand;
/***/ public String unmergedPaths;
/***/ public String unsupportedOperation;
/***/ public String untrackedFiles;
diff --git a/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 7).launch b/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 7).launch
index af009c0d4b..a83fabb75e 100644
--- a/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 7).launch
+++ b/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 7).launch
@@ -18,7 +18,6 @@
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;org.eclipse.jgit.test&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry path=&quot;3&quot; projectName=&quot;org.eclipse.jgit.java7&quot; type=&quot;1&quot;/&gt;&#10;"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
diff --git a/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 8).launch b/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 8).launch
index 04aa3ea107..b221a11a55 100644
--- a/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 8).launch
+++ b/org.eclipse.jgit.test/org.eclipse.jgit.core--All-Tests (Java 8).launch
@@ -19,7 +19,6 @@
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;org.eclipse.jgit.test&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry path=&quot;3&quot; projectName=&quot;org.eclipse.jgit.java7&quot; type=&quot;1&quot;/&gt;&#10;"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AbstractRemoteCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AbstractRemoteCommandTest.java
new file mode 100644
index 0000000000..d6a6342cb7
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AbstractRemoteCommandTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r@zend.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.URIish;
+
+public class AbstractRemoteCommandTest extends RepositoryTestCase {
+
+ protected static final String REMOTE_NAME = "test";
+
+ protected RemoteConfig setupRemote()
+ throws IOException, URISyntaxException {
+ // create another repository
+ Repository remoteRepository = createWorkRepository();
+
+ // set it up as a remote to this repository
+ final StoredConfig config = db.getConfig();
+ RemoteConfig remoteConfig = new RemoteConfig(config, REMOTE_NAME);
+
+ RefSpec refSpec = new RefSpec();
+ refSpec = refSpec.setForceUpdate(true);
+ refSpec = refSpec.setSourceDestination(Constants.R_HEADS + "*",
+ Constants.R_REMOTES + REMOTE_NAME + "/*");
+ remoteConfig.addFetchRefSpec(refSpec);
+
+ URIish uri = new URIish(
+ remoteRepository.getDirectory().toURI().toURL());
+ remoteConfig.addURI(uri);
+
+ remoteConfig.update(config);
+ config.save();
+
+ return remoteConfig;
+ }
+
+ protected void assertRemoteConfigEquals(RemoteConfig expected,
+ RemoteConfig actual) {
+ assertEquals(expected.getName(), actual.getName());
+ assertEquals(expected.getURIs(), actual.getURIs());
+ assertEquals(expected.getPushURIs(), actual.getPushURIs());
+ assertEquals(expected.getFetchRefSpecs(), actual.getFetchRefSpecs());
+ assertEquals(expected.getPushRefSpecs(), actual.getPushRefSpecs());
+ }
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
index 2abed3adc8..a5ad18d102 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
@@ -45,6 +45,7 @@ package org.eclipse.jgit.api;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
@@ -52,11 +53,13 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import org.eclipse.jgit.api.errors.FilterFailedException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.NoFilepatternException;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
@@ -112,6 +115,191 @@ public class AddCommandTest extends RepositoryTestCase {
}
@Test
+ public void testCleanFilter() throws IOException,
+ GitAPIException {
+ writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
+ writeTrashFile("src/a.tmp", "foo");
+ // Caution: we need a trailing '\n' since sed on mac always appends
+ // linefeeds if missing
+ writeTrashFile("src/a.txt", "foo\n");
+ File script = writeTempFile("sed s/o/e/g");
+
+ Git git = new Git(db);
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("filter", "tstFilter", "clean",
+ "sh " + slashify(script.getPath()));
+ config.save();
+
+ git.add().addFilepattern("src/a.txt").addFilepattern("src/a.tmp")
+ .call();
+
+ assertEquals(
+ "[src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:fee\n]",
+ indexState(CONTENT));
+ }
+
+ @Test
+ public void testCleanFilterEnvironment()
+ throws IOException, GitAPIException {
+ writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
+ writeTrashFile("src/a.txt", "foo");
+ File script = writeTempFile("echo $GIT_DIR; echo 1 >xyz");
+
+ Git git = new Git(db);
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("filter", "tstFilter", "clean",
+ "sh " + slashify(script.getPath()));
+ config.save();
+ git.add().addFilepattern("src/a.txt").call();
+
+ String gitDir = db.getDirectory().getAbsolutePath();
+ assertEquals("[src/a.txt, mode:100644, content:" + gitDir
+ + "\n]", indexState(CONTENT));
+ assertTrue(new File(db.getWorkTree(), "xyz").exists());
+ }
+
+ @Test
+ public void testMultipleCleanFilter() throws IOException, GitAPIException {
+ writeTrashFile(".gitattributes",
+ "*.txt filter=tstFilter\n*.tmp filter=tstFilter2");
+ // Caution: we need a trailing '\n' since sed on mac always appends
+ // linefeeds if missing
+ writeTrashFile("src/a.tmp", "foo\n");
+ writeTrashFile("src/a.txt", "foo\n");
+ File script = writeTempFile("sed s/o/e/g");
+ File script2 = writeTempFile("sed s/f/x/g");
+
+ Git git = new Git(db);
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("filter", "tstFilter", "clean",
+ "sh " + slashify(script.getPath()));
+ config.setString("filter", "tstFilter2", "clean",
+ "sh " + slashify(script2.getPath()));
+ config.save();
+
+ git.add().addFilepattern("src/a.txt").addFilepattern("src/a.tmp")
+ .call();
+
+ assertEquals(
+ "[src/a.tmp, mode:100644, content:xoo\n][src/a.txt, mode:100644, content:fee\n]",
+ indexState(CONTENT));
+
+ // TODO: multiple clean filters for one file???
+ }
+
+ /**
+ * The path of an added file name contains ';' and afterwards malicious
+ * commands. Make sure when calling filter commands to properly escape the
+ * filenames
+ *
+ * @throws IOException
+ * @throws GitAPIException
+ */
+ @Test
+ public void testCommandInjection() throws IOException, GitAPIException {
+ // Caution: we need a trailing '\n' since sed on mac always appends
+ // linefeeds if missing
+ writeTrashFile("; echo virus", "foo\n");
+ File script = writeTempFile("sed s/o/e/g");
+
+ Git git = new Git(db);
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("filter", "tstFilter", "clean",
+ "sh " + slashify(script.getPath()) + " %f");
+ writeTrashFile(".gitattributes", "* filter=tstFilter");
+
+ git.add().addFilepattern("; echo virus").call();
+ // Without proper escaping the content would be "feovirus". The sed
+ // command and the "echo virus" would contribute to the content
+ assertEquals("[; echo virus, mode:100644, content:fee\n]",
+ indexState(CONTENT));
+ }
+
+ @Test
+ public void testBadCleanFilter() throws IOException, GitAPIException {
+ writeTrashFile("a.txt", "foo");
+ File script = writeTempFile("sedfoo s/o/e/g");
+
+ Git git = new Git(db);
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("filter", "tstFilter", "clean",
+ "sh " + script.getPath());
+ config.save();
+ writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
+
+ try {
+ git.add().addFilepattern("a.txt").call();
+ fail("Didn't received the expected exception");
+ } catch (FilterFailedException e) {
+ assertEquals(127, e.getReturnCode());
+ }
+ }
+
+ @Test
+ public void testBadCleanFilter2() throws IOException, GitAPIException {
+ writeTrashFile("a.txt", "foo");
+ File script = writeTempFile("sed s/o/e/g");
+
+ Git git = new Git(db);
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("filter", "tstFilter", "clean",
+ "shfoo " + script.getPath());
+ config.save();
+ writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
+
+ try {
+ git.add().addFilepattern("a.txt").call();
+ fail("Didn't received the expected exception");
+ } catch (FilterFailedException e) {
+ assertEquals(127, e.getReturnCode());
+ }
+ }
+
+ @Test
+ public void testCleanFilterReturning12() throws IOException,
+ GitAPIException {
+ writeTrashFile("a.txt", "foo");
+ File script = writeTempFile("exit 12");
+
+ Git git = new Git(db);
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("filter", "tstFilter", "clean",
+ "sh " + slashify(script.getPath()));
+ config.save();
+ writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
+
+ try {
+ git.add().addFilepattern("a.txt").call();
+ fail("Didn't received the expected exception");
+ } catch (FilterFailedException e) {
+ assertEquals(12, e.getReturnCode());
+ }
+ }
+
+ @Test
+ public void testNotApplicableFilter() throws IOException, GitAPIException {
+ writeTrashFile("a.txt", "foo");
+ File script = writeTempFile("sed s/o/e/g");
+
+ Git git = new Git(db);
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("filter", "tstFilter", "something",
+ "sh " + script.getPath());
+ config.save();
+ writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
+
+ git.add().addFilepattern("a.txt").call();
+
+ assertEquals("[a.txt, mode:100644, content:foo]", indexState(CONTENT));
+ }
+
+ private File writeTempFile(String body) throws IOException {
+ File f = File.createTempFile("AddCommandTest_", "");
+ JGitTestUtil.write(f, body);
+ return f;
+ }
+
+ @Test
public void testAddExistingSingleSmallFileWithNewLine() throws IOException,
GitAPIException {
File file = new File(db.getWorkTree(), "a.txt");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java
index f7a50dffcb..4bfb128cbc 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java
@@ -43,6 +43,8 @@
*/
package org.eclipse.jgit.api;
+import static org.eclipse.jgit.lib.Constants.MASTER;
+import static org.eclipse.jgit.lib.Constants.R_HEADS;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -70,12 +72,14 @@ import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.api.errors.TransportException;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.Sets;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.storage.file.FileBasedConfig;
@@ -84,6 +88,7 @@ import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.util.FileUtils;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
public class CheckoutCommandTest extends RepositoryTestCase {
@@ -134,7 +139,7 @@ public class CheckoutCommandTest extends RepositoryTestCase {
@Test
public void testCreateBranchOnCheckout() throws Exception {
git.checkout().setCreateBranch(true).setName("test2").call();
- assertNotNull(db.getRef("test2"));
+ assertNotNull(db.exactRef("refs/heads/test2"));
}
@Test
@@ -237,8 +242,8 @@ public class CheckoutCommandTest extends RepositoryTestCase {
.setStartPoint("origin/test")
.setUpstreamMode(SetupUpstreamMode.TRACK).call();
- assertEquals("refs/heads/test", db2.getRef(Constants.HEAD).getTarget()
- .getName());
+ assertEquals("refs/heads/test",
+ db2.exactRef(Constants.HEAD).getTarget().getName());
StoredConfig config = db2.getConfig();
assertEquals("origin", config.getString(
ConfigConstants.CONFIG_BRANCH_SECTION, "test",
@@ -345,7 +350,7 @@ public class CheckoutCommandTest extends RepositoryTestCase {
CheckoutCommand co = git.checkout();
co.setName("master").call();
- String commitId = db.getRef(Constants.MASTER).getObjectId().name();
+ String commitId = db.exactRef(R_HEADS + MASTER).getObjectId().name();
co = git.checkout();
co.setName(commitId).call();
@@ -443,7 +448,7 @@ public class CheckoutCommandTest extends RepositoryTestCase {
}
private void assertHeadDetached() throws IOException {
- Ref head = db.getRef(Constants.HEAD);
+ Ref head = db.exactRef(Constants.HEAD);
assertFalse(head.isSymbolic());
assertSame(head, head.getTarget());
}
@@ -554,4 +559,125 @@ public class CheckoutCommandTest extends RepositoryTestCase {
}
org.junit.Assume.assumeTrue(foundUnsmudged);
}
+
+ @Test
+ public void testSmudgeFilter_modifyExisting() throws IOException, GitAPIException {
+ File script = writeTempFile("sed s/o/e/g");
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("filter", "tstFilter", "smudge",
+ "sh " + slashify(script.getPath()));
+ config.save();
+
+ writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
+ git.add().addFilepattern(".gitattributes").call();
+ git.commit().setMessage("add filter").call();
+
+ writeTrashFile("src/a.tmp", "x");
+ // Caution: we need a trailing '\n' since sed on mac always appends
+ // linefeeds if missing
+ writeTrashFile("src/a.txt", "x\n");
+ git.add().addFilepattern("src/a.tmp").addFilepattern("src/a.txt")
+ .call();
+ RevCommit content1 = git.commit().setMessage("add content").call();
+
+ writeTrashFile("src/a.tmp", "foo");
+ writeTrashFile("src/a.txt", "foo\n");
+ git.add().addFilepattern("src/a.tmp").addFilepattern("src/a.txt")
+ .call();
+ RevCommit content2 = git.commit().setMessage("changed content").call();
+
+ git.checkout().setName(content1.getName()).call();
+ git.checkout().setName(content2.getName()).call();
+
+ assertEquals(
+ "[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some change][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:foo\n]",
+ indexState(CONTENT));
+ assertEquals(Sets.of("src/a.txt"), git.status().call().getModified());
+ assertEquals("foo", read("src/a.tmp"));
+ assertEquals("fee\n", read("src/a.txt"));
+ }
+
+ @Test
+ public void testSmudgeFilter_createNew()
+ throws IOException, GitAPIException {
+ File script = writeTempFile("sed s/o/e/g");
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("filter", "tstFilter", "smudge",
+ "sh " + slashify(script.getPath()));
+ config.save();
+
+ writeTrashFile("foo", "foo");
+ git.add().addFilepattern("foo").call();
+ RevCommit initial = git.commit().setMessage("initial").call();
+
+ writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
+ git.add().addFilepattern(".gitattributes").call();
+ git.commit().setMessage("add filter").call();
+
+ writeTrashFile("src/a.tmp", "foo");
+ // Caution: we need a trailing '\n' since sed on mac always appends
+ // linefeeds if missing
+ writeTrashFile("src/a.txt", "foo\n");
+ git.add().addFilepattern("src/a.tmp").addFilepattern("src/a.txt")
+ .call();
+ RevCommit content = git.commit().setMessage("added content").call();
+
+ git.checkout().setName(initial.getName()).call();
+ git.checkout().setName(content.getName()).call();
+
+ assertEquals(
+ "[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some change][foo, mode:100644, content:foo][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:foo\n]",
+ indexState(CONTENT));
+ assertEquals("foo", read("src/a.tmp"));
+ assertEquals("fee\n", read("src/a.txt"));
+ }
+
+ @Test
+ @Ignore
+ public void testSmudgeAndClean() throws IOException, GitAPIException {
+ // @TODO: fix this test
+ File clean_filter = writeTempFile("sed s/V1/@version/g -");
+ File smudge_filter = writeTempFile("sed s/@version/V1/g -");
+
+ Git git = new Git(db);
+ StoredConfig config = git.getRepository().getConfig();
+ config.setString("filter", "tstFilter", "smudge",
+ "sh " + slashify(smudge_filter.getPath()));
+ config.setString("filter", "tstFilter", "clean",
+ "sh " + slashify(clean_filter.getPath()));
+ config.save();
+ writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
+ git.add().addFilepattern(".gitattributes").call();
+ git.commit().setMessage("add attributes").call();
+
+ writeTrashFile("filterTest.txt", "hello world, V1");
+ git.add().addFilepattern("filterTest.txt").call();
+ git.commit().setMessage("add filterText.txt").call();
+ assertEquals(
+ "[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some other change][filterTest.txt, mode:100644, content:hello world, @version]",
+ indexState(CONTENT));
+
+ git.checkout().setCreateBranch(true).setName("test2").call();
+ writeTrashFile("filterTest.txt", "bon giorno world, V1");
+ git.add().addFilepattern("filterTest.txt").call();
+ git.commit().setMessage("modified filterText.txt").call();
+
+ assertTrue(git.status().call().isClean());
+ assertEquals(
+ "[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some other change][filterTest.txt, mode:100644, content:bon giorno world, @version]",
+ indexState(CONTENT));
+
+ git.checkout().setName("refs/heads/test").call();
+ assertTrue(git.status().call().isClean());
+ assertEquals(
+ "[.gitattributes, mode:100644, content:*.txt filter=tstFilter][Test.txt, mode:100644, content:Some other change][filterTest.txt, mode:100644, content:hello world, @version]",
+ indexState(CONTENT));
+ assertEquals("hello world, V1", read("filterTest.txt"));
+ }
+
+ private File writeTempFile(String body) throws IOException {
+ File f = File.createTempFile("AddCommandTest_", "");
+ JGitTestUtil.write(f, body);
+ return f;
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
index 060a5b65c6..0d03047d53 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
@@ -408,8 +408,10 @@ public class CommitCommandTest extends RepositoryTestCase {
checkoutBranch("refs/heads/master");
- MergeResult result = git.merge().include(db.getRef("branch1"))
- .setSquash(true).call();
+ MergeResult result = git.merge()
+ .include(db.exactRef("refs/heads/branch1"))
+ .setSquash(true)
+ .call();
assertTrue(new File(db.getWorkTree(), "file1").exists());
assertTrue(new File(db.getWorkTree(), "file2").exists());
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 0c0c6e5b55..cea8393ff7 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/MergeCommandTest.java
@@ -43,6 +43,8 @@
*/
package org.eclipse.jgit.api;
+import static org.eclipse.jgit.lib.Constants.MASTER;
+import static org.eclipse.jgit.lib.Constants.R_HEADS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
@@ -97,7 +99,7 @@ public class MergeCommandTest extends RepositoryTestCase {
Git git = new Git(db);
git.commit().setMessage("initial commit").call();
- MergeResult result = git.merge().include(db.getRef(Constants.HEAD)).call();
+ MergeResult result = git.merge().include(db.exactRef(Constants.HEAD)).call();
assertEquals(MergeResult.MergeStatus.ALREADY_UP_TO_DATE, result.getMergeStatus());
// no reflog entry written by merge
assertEquals("commit (initial): initial commit",
@@ -115,7 +117,7 @@ public class MergeCommandTest extends RepositoryTestCase {
createBranch(first, "refs/heads/branch1");
RevCommit second = git.commit().setMessage("second commit").call();
- MergeResult result = git.merge().include(db.getRef("refs/heads/branch1")).call();
+ MergeResult result = git.merge().include(db.exactRef("refs/heads/branch1")).call();
assertEquals(MergeResult.MergeStatus.ALREADY_UP_TO_DATE, result.getMergeStatus());
assertEquals(second, result.getNewHead());
// no reflog entry written by merge
@@ -135,7 +137,7 @@ public class MergeCommandTest extends RepositoryTestCase {
checkoutBranch("refs/heads/branch1");
- MergeResult result = git.merge().include(db.getRef(Constants.MASTER)).call();
+ MergeResult result = git.merge().include(db.exactRef(R_HEADS + MASTER)).call();
assertEquals(MergeResult.MergeStatus.FAST_FORWARD, result.getMergeStatus());
assertEquals(second, result.getNewHead());
@@ -155,7 +157,7 @@ public class MergeCommandTest extends RepositoryTestCase {
checkoutBranch("refs/heads/branch1");
- MergeResult result = git.merge().include(db.getRef(Constants.MASTER))
+ MergeResult result = git.merge().include(db.exactRef(R_HEADS + MASTER))
.setCommit(false).call();
assertEquals(MergeResult.MergeStatus.FAST_FORWARD,
@@ -186,7 +188,7 @@ public class MergeCommandTest extends RepositoryTestCase {
checkoutBranch("refs/heads/branch1");
assertFalse(new File(db.getWorkTree(), "file2").exists());
- MergeResult result = git.merge().include(db.getRef(Constants.MASTER)).call();
+ MergeResult result = git.merge().include(db.exactRef(R_HEADS + MASTER)).call();
assertTrue(new File(db.getWorkTree(), "file1").exists());
assertTrue(new File(db.getWorkTree(), "file2").exists());
@@ -221,7 +223,7 @@ public class MergeCommandTest extends RepositoryTestCase {
MergeCommand merge = git.merge();
merge.include(second.getId());
- merge.include(db.getRef(Constants.MASTER));
+ merge.include(db.exactRef(R_HEADS + MASTER));
try {
merge.call();
fail("Expected exception not thrown when merging multiple heads");
@@ -248,7 +250,7 @@ public class MergeCommandTest extends RepositoryTestCase {
git.commit().setMessage("third").call();
MergeResult result = git.merge().setStrategy(mergeStrategy)
- .include(db.getRef(Constants.MASTER)).call();
+ .include(db.exactRef(R_HEADS + MASTER)).call();
assertEquals(MergeStatus.MERGED, result.getMergeStatus());
assertEquals(
"merge refs/heads/master: Merge made by "
@@ -279,9 +281,9 @@ public class MergeCommandTest extends RepositoryTestCase {
MergeResult result = git.merge().setStrategy(mergeStrategy)
.setCommit(false)
- .include(db.getRef(Constants.MASTER)).call();
+ .include(db.exactRef(R_HEADS + MASTER)).call();
assertEquals(MergeStatus.MERGED_NOT_COMMITTED, result.getMergeStatus());
- assertEquals(db.getRef(Constants.HEAD).getTarget().getObjectId(),
+ assertEquals(db.exactRef(Constants.HEAD).getTarget().getObjectId(),
thirdCommit.getId());
}
@@ -378,7 +380,7 @@ public class MergeCommandTest extends RepositoryTestCase {
git.add().addFilepattern("a").call();
git.commit().setMessage("main").call();
- Ref sideBranch = db.getRef("side");
+ Ref sideBranch = db.exactRef("refs/heads/side");
git.merge().include(sideBranch)
.setStrategy(MergeStrategy.RESOLVE).call();
@@ -590,7 +592,7 @@ public class MergeCommandTest extends RepositoryTestCase {
.setCommit(false)
.setStrategy(MergeStrategy.RESOLVE).call();
assertEquals(MergeStatus.MERGED_NOT_COMMITTED, result.getMergeStatus());
- assertEquals(db.getRef(Constants.HEAD).getTarget().getObjectId(),
+ assertEquals(db.exactRef(Constants.HEAD).getTarget().getObjectId(),
thirdCommit.getId());
assertEquals("1(side)\na\n3(main)\n", read(new File(db.getWorkTree(),
@@ -1310,8 +1312,10 @@ public class MergeCommandTest extends RepositoryTestCase {
assertFalse(new File(db.getWorkTree(), "file2").exists());
assertFalse(new File(db.getWorkTree(), "file3").exists());
- MergeResult result = git.merge().include(db.getRef("branch1"))
- .setSquash(true).call();
+ MergeResult result = git.merge()
+ .include(db.exactRef("refs/heads/branch1"))
+ .setSquash(true)
+ .call();
assertTrue(new File(db.getWorkTree(), "file1").exists());
assertTrue(new File(db.getWorkTree(), "file2").exists());
@@ -1375,8 +1379,10 @@ public class MergeCommandTest extends RepositoryTestCase {
assertTrue(new File(db.getWorkTree(), "file2").exists());
assertFalse(new File(db.getWorkTree(), "file3").exists());
- MergeResult result = git.merge().include(db.getRef("branch1"))
- .setSquash(true).call();
+ MergeResult result = git.merge()
+ .include(db.exactRef("refs/heads/branch1"))
+ .setSquash(true)
+ .call();
assertTrue(new File(db.getWorkTree(), "file1").exists());
assertTrue(new File(db.getWorkTree(), "file2").exists());
@@ -1430,8 +1436,10 @@ public class MergeCommandTest extends RepositoryTestCase {
assertTrue(new File(db.getWorkTree(), "file1").exists());
assertTrue(new File(db.getWorkTree(), "file2").exists());
- MergeResult result = git.merge().include(db.getRef("branch1"))
- .setSquash(true).call();
+ MergeResult result = git.merge()
+ .include(db.exactRef("refs/heads/branch1"))
+ .setSquash(true)
+ .call();
assertTrue(new File(db.getWorkTree(), "file1").exists());
assertTrue(new File(db.getWorkTree(), "file2").exists());
@@ -1468,7 +1476,7 @@ public class MergeCommandTest extends RepositoryTestCase {
MergeCommand merge = git.merge();
merge.setFastForward(FastForwardMode.FF_ONLY);
- merge.include(db.getRef(Constants.MASTER));
+ merge.include(db.exactRef(R_HEADS + MASTER));
MergeResult result = merge.call();
assertEquals(MergeStatus.FAST_FORWARD, result.getMergeStatus());
@@ -1485,7 +1493,7 @@ public class MergeCommandTest extends RepositoryTestCase {
MergeCommand merge = git.merge();
merge.setFastForward(FastForwardMode.NO_FF);
- merge.include(db.getRef(Constants.MASTER));
+ merge.include(db.exactRef(R_HEADS + MASTER));
MergeResult result = merge.call();
assertEquals(MergeStatus.MERGED, result.getMergeStatus());
@@ -1505,7 +1513,7 @@ public class MergeCommandTest extends RepositoryTestCase {
// when
MergeCommand merge = git.merge();
merge.setFastForward(FastForwardMode.NO_FF);
- merge.include(db.getRef(Constants.MASTER));
+ merge.include(db.exactRef(R_HEADS + MASTER));
merge.setCommit(false);
MergeResult result = merge.call();
@@ -1531,7 +1539,7 @@ public class MergeCommandTest extends RepositoryTestCase {
git.commit().setMessage("second commit on branch1").call();
MergeCommand merge = git.merge();
merge.setFastForward(FastForwardMode.FF_ONLY);
- merge.include(db.getRef(Constants.MASTER));
+ merge.include(db.exactRef(R_HEADS + MASTER));
MergeResult result = merge.call();
assertEquals(MergeStatus.ABORTED, result.getMergeStatus());
@@ -1588,7 +1596,7 @@ public class MergeCommandTest extends RepositoryTestCase {
git.add().addFilepattern("c").call();
git.commit().setMessage("main").call();
- Ref sideBranch = db.getRef("side");
+ Ref sideBranch = db.exactRef("refs/heads/side");
git.merge().include(sideBranch).setStrategy(MergeStrategy.RESOLVE)
.setMessage("user message").call();
@@ -1621,7 +1629,7 @@ public class MergeCommandTest extends RepositoryTestCase {
git.add().addFilepattern("a").call();
git.commit().setMessage("main").call();
- Ref sideBranch = db.getRef("side");
+ Ref sideBranch = db.exactRef("refs/heads/side");
git.merge().include(sideBranch).setStrategy(MergeStrategy.RESOLVE)
.setMessage("user message").call();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NameRevCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NameRevCommandTest.java
index 491595498e..bd62200fce 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NameRevCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/NameRevCommandTest.java
@@ -95,9 +95,9 @@ public class NameRevCommandTest extends RepositoryTestCase {
tr.update("refs/heads/master", c);
tr.update("refs/tags/tag", c);
assertOneResult("master",
- git.nameRev().addRef(db.getRef("refs/heads/master")), c);
+ git.nameRev().addRef(db.exactRef("refs/heads/master")), c);
assertOneResult("tag",
- git.nameRev().addRef(db.getRef("refs/tags/tag")), c);
+ git.nameRev().addRef(db.exactRef("refs/tags/tag")), c);
}
@Test
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java
index ccf1a51f1b..1fcfef9c71 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java
@@ -138,11 +138,9 @@ public class PushCommandTest extends RepositoryTestCase {
RefSpec spec = new RefSpec("refs/heads/master:refs/heads/x");
git1.push().setRemote("test").setRefSpecs(spec).call();
- assertEquals(
- "1:test, 2:file://" + db2.getDirectory().toPath() //
- + "/, 3:\n" + "refs/heads/master " + commit.getName()
- + " refs/heads/x " + ObjectId.zeroId().name(),
- read(hookOutput));
+ assertEquals("1:test, 2:" + uri + ", 3:\n" + "refs/heads/master "
+ + commit.getName() + " refs/heads/x "
+ + ObjectId.zeroId().name(), read(hookOutput));
}
private File writeHookFile(final String name, final String data)
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteAddCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteAddCommandTest.java
new file mode 100644
index 0000000000..ed0944676a
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteAddCommandTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r@zend.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.URIish;
+import org.junit.Test;
+
+public class RemoteAddCommandTest extends AbstractRemoteCommandTest {
+
+ @Test
+ public void testAdd() throws Exception {
+ // create another repository
+ Repository remoteRepository = createWorkRepository();
+ URIish uri = new URIish(
+ remoteRepository.getDirectory().toURI().toURL());
+
+ // execute the command to add a new remote
+ RemoteAddCommand cmd = Git.wrap(db).remoteAdd();
+ cmd.setName(REMOTE_NAME);
+ cmd.setUri(uri);
+ RemoteConfig remote = cmd.call();
+
+ // assert that the added remote represents the remote repository
+ assertEquals(REMOTE_NAME, remote.getName());
+ assertArrayEquals(new URIish[] { uri }, remote.getURIs().toArray());
+ assertEquals(1, remote.getFetchRefSpecs().size());
+ assertEquals(
+ String.format("+refs/heads/*:refs/remotes/%s/*", REMOTE_NAME),
+ remote.getFetchRefSpecs().get(0).toString());
+
+ // assert that the added remote is available in the git configuration
+ assertRemoteConfigEquals(remote,
+ new RemoteConfig(db.getConfig(), REMOTE_NAME));
+ }
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteDeleteCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteDeleteCommandTest.java
new file mode 100644
index 0000000000..7055daff9a
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteDeleteCommandTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r@zend.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import static org.junit.Assert.assertTrue;
+
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.junit.Test;
+
+public class RemoteDeleteCommandTest extends AbstractRemoteCommandTest {
+
+ @Test
+ public void testDelete() throws Exception {
+ // setup an initial remote
+ RemoteConfig remoteConfig = setupRemote();
+
+ // execute the command to remove the remote
+ RemoteRemoveCommand cmd = Git.wrap(db).remoteRemove();
+ cmd.setName(REMOTE_NAME);
+ RemoteConfig remote = cmd.call();
+
+ // assert that the removed remote is the initial remote
+ assertRemoteConfigEquals(remoteConfig, remote);
+ // assert that there are no remotes left
+ assertTrue(RemoteConfig.getAllRemoteConfigs(db.getConfig()).isEmpty());
+ }
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteListCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteListCommandTest.java
new file mode 100644
index 0000000000..cf522ff664
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteListCommandTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r@zend.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.junit.Test;
+
+public class RemoteListCommandTest extends AbstractRemoteCommandTest {
+
+ @Test
+ public void testList() throws Exception {
+ // setup an initial remote
+ RemoteConfig remoteConfig = setupRemote();
+
+ // execute the command to list the remotes
+ List<RemoteConfig> remotes = Git.wrap(db).remoteList().call();
+
+ // assert that there is only one remote
+ assertEquals(1, remotes.size());
+ // assert that the available remote is the initial remote
+ assertRemoteConfigEquals(remoteConfig, remotes.get(0));
+ }
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteSetUrlCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteSetUrlCommandTest.java
new file mode 100644
index 0000000000..6969c3df6c
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RemoteSetUrlCommandTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r@zend.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.URIish;
+import org.junit.Test;
+
+public class RemoteSetUrlCommandTest extends AbstractRemoteCommandTest {
+
+ @Test
+ public void testSetUrl() throws Exception {
+ // setup an initial remote
+ setupRemote();
+
+ // execute the command to change the fetch url
+ RemoteSetUrlCommand cmd = Git.wrap(db).remoteSetUrl();
+ cmd.setName(REMOTE_NAME);
+ URIish newUri = new URIish("git://test.com/test");
+ cmd.setUri(newUri);
+ RemoteConfig remote = cmd.call();
+
+ // assert that the changed remote has the new fetch url
+ assertEquals(REMOTE_NAME, remote.getName());
+ assertArrayEquals(new URIish[] { newUri }, remote.getURIs().toArray());
+
+ // assert that the changed remote is available in the git configuration
+ assertRemoteConfigEquals(remote,
+ new RemoteConfig(db.getConfig(), REMOTE_NAME));
+ }
+
+ @Test
+ public void testSetPushUrl() throws Exception {
+ // setup an initial remote
+ RemoteConfig remoteConfig = setupRemote();
+
+ // execute the command to change the push url
+ RemoteSetUrlCommand cmd = Git.wrap(db).remoteSetUrl();
+ cmd.setName(REMOTE_NAME);
+ URIish newUri = new URIish("git://test.com/test");
+ cmd.setUri(newUri);
+ cmd.setPush(true);
+ RemoteConfig remote = cmd.call();
+
+ // assert that the changed remote has the old fetch url and the new push
+ // url
+ assertEquals(REMOTE_NAME, remote.getName());
+ assertEquals(remoteConfig.getURIs(), remote.getURIs());
+ assertArrayEquals(new URIish[] { newUri },
+ remote.getPushURIs().toArray());
+
+ // assert that the changed remote is available in the git configuration
+ assertRemoteConfigEquals(remote,
+ new RemoteConfig(db.getConfig(), REMOTE_NAME));
+ }
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java
index 9997c8c426..a67f2b912a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ResetCommandTest.java
@@ -477,8 +477,10 @@ public class ResetCommandTest extends RepositoryTestCase {
checkoutBranch("refs/heads/master");
- MergeResult result = g.merge().include(db.getRef("branch1"))
- .setSquash(true).call();
+ MergeResult result = g.merge()
+ .include(db.exactRef("refs/heads/branch1"))
+ .setSquash(true)
+ .call();
assertEquals(MergeResult.MergeStatus.FAST_FORWARD_SQUASHED,
result.getMergeStatus());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java
index c317e3beef..ae8551e641 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashCreateCommandTest.java
@@ -110,7 +110,7 @@ public class StashCreateCommandTest extends RepositoryTestCase {
int parentCount)
throws IOException {
assertNotNull(commit);
- Ref stashRef = db.getRef(Constants.R_STASH);
+ Ref stashRef = db.exactRef(Constants.R_STASH);
assertNotNull(stashRef);
assertEquals(commit, stashRef.getObjectId());
assertNotNull(commit.getAuthorIdent());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java
index cfad817d23..859277e93f 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/StashDropCommandTest.java
@@ -96,13 +96,13 @@ public class StashDropCommandTest extends RepositoryTestCase {
@Test
public void dropWithInvalidLogIndex() throws Exception {
write(committedFile, "content2");
- Ref stashRef = git.getRepository().getRef(Constants.R_STASH);
+ Ref stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNull(stashRef);
RevCommit stashed = git.stashCreate().call();
assertNotNull(stashed);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
- assertEquals(stashed, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
+ assertEquals(stashed,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
try {
assertNull(git.stashDrop().setStashRef(100).call());
fail("Exception not thrown");
@@ -115,15 +115,15 @@ public class StashDropCommandTest extends RepositoryTestCase {
@Test
public void dropSingleStashedCommit() throws Exception {
write(committedFile, "content2");
- Ref stashRef = git.getRepository().getRef(Constants.R_STASH);
+ Ref stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNull(stashRef);
RevCommit stashed = git.stashCreate().call();
assertNotNull(stashed);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
- assertEquals(stashed, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
+ assertEquals(stashed,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
assertNull(git.stashDrop().call());
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNull(stashRef);
ReflogReader reader = git.getRepository().getReflogReader(
@@ -134,25 +134,25 @@ public class StashDropCommandTest extends RepositoryTestCase {
@Test
public void dropAll() throws Exception {
write(committedFile, "content2");
- Ref stashRef = git.getRepository().getRef(Constants.R_STASH);
+ Ref stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNull(stashRef);
RevCommit firstStash = git.stashCreate().call();
assertNotNull(firstStash);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
- assertEquals(firstStash, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ assertEquals(firstStash,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
write(committedFile, "content3");
RevCommit secondStash = git.stashCreate().call();
assertNotNull(secondStash);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
- assertEquals(secondStash, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ assertEquals(secondStash,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
assertNull(git.stashDrop().setAll(true).call());
- assertNull(git.getRepository().getRef(Constants.R_STASH));
+ assertNull(git.getRepository().exactRef(Constants.R_STASH));
ReflogReader reader = git.getRepository().getReflogReader(
Constants.R_STASH);
@@ -162,25 +162,25 @@ public class StashDropCommandTest extends RepositoryTestCase {
@Test
public void dropFirstStashedCommit() throws Exception {
write(committedFile, "content2");
- Ref stashRef = git.getRepository().getRef(Constants.R_STASH);
+ Ref stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNull(stashRef);
RevCommit firstStash = git.stashCreate().call();
assertNotNull(firstStash);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
- assertEquals(firstStash, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ assertEquals(firstStash,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
write(committedFile, "content3");
RevCommit secondStash = git.stashCreate().call();
assertNotNull(secondStash);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
- assertEquals(secondStash, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ assertEquals(secondStash,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
assertEquals(firstStash, git.stashDrop().call());
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
assertEquals(firstStash, stashRef.getObjectId());
@@ -196,33 +196,33 @@ public class StashDropCommandTest extends RepositoryTestCase {
@Test
public void dropMiddleStashCommit() throws Exception {
write(committedFile, "content2");
- Ref stashRef = git.getRepository().getRef(Constants.R_STASH);
+ Ref stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNull(stashRef);
RevCommit firstStash = git.stashCreate().call();
assertNotNull(firstStash);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
- assertEquals(firstStash, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ assertEquals(firstStash,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
write(committedFile, "content3");
RevCommit secondStash = git.stashCreate().call();
assertNotNull(secondStash);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
- assertEquals(secondStash, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ assertEquals(secondStash,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
write(committedFile, "content4");
RevCommit thirdStash = git.stashCreate().call();
assertNotNull(thirdStash);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
- assertEquals(thirdStash, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ assertEquals(thirdStash,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
assertEquals(thirdStash, git.stashDrop().setStashRef(1).call());
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
assertEquals(thirdStash, stashRef.getObjectId());
@@ -241,46 +241,46 @@ public class StashDropCommandTest extends RepositoryTestCase {
@Test
public void dropBoundaryStashedCommits() throws Exception {
write(committedFile, "content2");
- Ref stashRef = git.getRepository().getRef(Constants.R_STASH);
+ Ref stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNull(stashRef);
RevCommit firstStash = git.stashCreate().call();
assertNotNull(firstStash);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
- assertEquals(firstStash, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ assertEquals(firstStash,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
write(committedFile, "content3");
RevCommit secondStash = git.stashCreate().call();
assertNotNull(secondStash);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
- assertEquals(secondStash, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ assertEquals(secondStash,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
write(committedFile, "content4");
RevCommit thirdStash = git.stashCreate().call();
assertNotNull(thirdStash);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
- assertEquals(thirdStash, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ assertEquals(thirdStash,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
write(committedFile, "content5");
RevCommit fourthStash = git.stashCreate().call();
assertNotNull(fourthStash);
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
- assertEquals(fourthStash, git.getRepository().getRef(Constants.R_STASH)
- .getObjectId());
+ assertEquals(fourthStash,
+ git.getRepository().exactRef(Constants.R_STASH).getObjectId());
assertEquals(thirdStash, git.stashDrop().call());
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
assertEquals(thirdStash, stashRef.getObjectId());
assertEquals(thirdStash, git.stashDrop().setStashRef(2).call());
- stashRef = git.getRepository().getRef(Constants.R_STASH);
+ stashRef = git.getRepository().exactRef(Constants.R_STASH);
assertNotNull(stashRef);
assertEquals(thirdStash, stashRef.getObjectId());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java
index 49279e6e5a..0e595e61f8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java
@@ -52,9 +52,7 @@ import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.Collections;
-import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Map;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.attributes.Attribute.State;
@@ -243,27 +241,29 @@ public class AttributesNodeDirCacheIteratorTest extends RepositoryTestCase {
DirCacheIterator itr = walk.getTree(0, DirCacheIterator.class);
assertNotNull("has tree", itr);
- AttributesNode attributeNode = itr.getEntryAttributesNode(db
+ AttributesNode attributesNode = itr.getEntryAttributesNode(db
.newObjectReader());
- assertAttributeNode(pathName, attributeNode, nodeAttrs);
+ assertAttributesNode(pathName, attributesNode, nodeAttrs);
if (D.equals(type))
walk.enterSubtree();
}
- private void assertAttributeNode(String pathName,
- AttributesNode attributeNode, List<Attribute> nodeAttrs) {
- if (attributeNode == null)
+ private void assertAttributesNode(String pathName,
+ AttributesNode attributesNode, List<Attribute> nodeAttrs) {
+ if (attributesNode == null)
assertTrue(nodeAttrs == null || nodeAttrs.isEmpty());
else {
- Map<String, Attribute> entryAttributes = new LinkedHashMap<String, Attribute>();
- attributeNode.getAttributes(pathName, false, entryAttributes);
+ Attributes entryAttributes = new Attributes();
+ attributesNode.getAttributes(pathName,
+ false, entryAttributes);
if (nodeAttrs != null && !nodeAttrs.isEmpty()) {
for (Attribute attribute : nodeAttrs) {
- assertThat(entryAttributes.values(), hasItem(attribute));
+ assertThat(entryAttributes.getAll(),
+ hasItem(attribute));
}
} else {
assertTrue(
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributeNodeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java
index ea250369a0..d478a7cf08 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributeNodeTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java
@@ -49,10 +49,6 @@ import static org.junit.Assert.assertEquals;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
import org.junit.After;
import org.junit.Test;
@@ -60,7 +56,7 @@ import org.junit.Test;
/**
* Test {@link AttributesNode}
*/
-public class AttributeNodeTest {
+public class AttributesNodeTest {
private static final Attribute A_SET_ATTR = new Attribute("A", SET);
@@ -104,8 +100,8 @@ public class AttributeNodeTest {
is = new ByteArrayInputStream(attributeFileContent.getBytes());
AttributesNode node = new AttributesNode();
node.parse(is);
- assertAttribute("file.type1", node, Collections.<Attribute> emptySet());
- assertAttribute("file.type2", node, Collections.<Attribute> emptySet());
+ assertAttribute("file.type1", node, new Attributes());
+ assertAttribute("file.type2", node, new Attributes());
}
@Test
@@ -115,7 +111,7 @@ public class AttributeNodeTest {
is = new ByteArrayInputStream(attributeFileContent.getBytes());
AttributesNode node = new AttributesNode();
node.parse(is);
- assertAttribute("file.type1", node, Collections.<Attribute> emptySet());
+ assertAttribute("file.type1", node, new Attributes());
assertAttribute("file.type2", node, asSet(A_UNSET_ATTR));
}
@@ -127,8 +123,8 @@ public class AttributeNodeTest {
is = new ByteArrayInputStream(attributeFileContent.getBytes());
AttributesNode node = new AttributesNode();
node.parse(is);
- assertAttribute("file.type1", node, Collections.<Attribute> emptySet());
- assertAttribute("file.type2", node, Collections.<Attribute> emptySet());
+ assertAttribute("file.type1", node, new Attributes());
+ assertAttribute("file.type2", node, new Attributes());
assertAttribute("file.type3", node, asSet(new Attribute("attr", "")));
}
@@ -166,17 +162,14 @@ public class AttributeNodeTest {
}
private void assertAttribute(String path, AttributesNode node,
- Set<Attribute> attrs) {
- HashMap<String, Attribute> attributes = new HashMap<String, Attribute>();
+ Attributes attrs) {
+ Attributes attributes = new Attributes();
node.getAttributes(path, false, attributes);
- assertEquals(attrs, new HashSet<Attribute>(attributes.values()));
+ assertEquals(attrs, attributes);
}
- static Set<Attribute> asSet(Attribute... attrs) {
- Set<Attribute> result = new HashSet<Attribute>();
- for (Attribute attr : attrs)
- result.add(attr);
- return result;
+ static Attributes asSet(Attribute... attrs) {
+ return new Attributes(attrs);
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java
index 64b0535d6a..6ad19a2491 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java
@@ -53,9 +53,7 @@ import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
-import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Map;
import org.eclipse.jgit.attributes.Attribute.State;
import org.eclipse.jgit.errors.CorruptObjectException;
@@ -76,14 +74,10 @@ public class AttributesNodeWorkingTreeIteratorTest extends RepositoryTestCase {
private static final FileMode F = FileMode.REGULAR_FILE;
- private static Attribute EOL_CRLF = new Attribute("eol", "crlf");
-
private static Attribute EOL_LF = new Attribute("eol", "lf");
private static Attribute DELTA_UNSET = new Attribute("delta", State.UNSET);
- private static Attribute CUSTOM_VALUE = new Attribute("custom", "value");
-
private TreeWalk walk;
@Test
@@ -112,25 +106,19 @@ public class AttributesNodeWorkingTreeIteratorTest extends RepositoryTestCase {
walk = beginWalk();
assertIteration(F, ".gitattributes");
- assertIteration(F, "global.txt", asList(EOL_LF), null,
- asList(CUSTOM_VALUE));
- assertIteration(F, "readme.txt", asList(EOL_LF), null,
- asList(CUSTOM_VALUE));
+ assertIteration(F, "global.txt", asList(EOL_LF));
+ assertIteration(F, "readme.txt", asList(EOL_LF));
assertIteration(D, "src");
assertIteration(D, "src/config");
assertIteration(F, "src/config/.gitattributes");
- assertIteration(F, "src/config/readme.txt", asList(DELTA_UNSET), null,
- asList(CUSTOM_VALUE));
- assertIteration(F, "src/config/windows.file", null, asList(EOL_CRLF),
- null);
- assertIteration(F, "src/config/windows.txt", asList(DELTA_UNSET),
- asList(EOL_CRLF), asList(CUSTOM_VALUE));
+ assertIteration(F, "src/config/readme.txt", asList(DELTA_UNSET));
+ assertIteration(F, "src/config/windows.file", null);
+ assertIteration(F, "src/config/windows.txt", asList(DELTA_UNSET));
- assertIteration(F, "windows.file", null, asList(EOL_CRLF), null);
- assertIteration(F, "windows.txt", asList(EOL_LF), asList(EOL_CRLF),
- asList(CUSTOM_VALUE));
+ assertIteration(F, "windows.file", null);
+ assertIteration(F, "windows.txt", asList(EOL_LF));
endWalk();
}
@@ -212,14 +200,11 @@ public class AttributesNodeWorkingTreeIteratorTest extends RepositoryTestCase {
private void assertIteration(FileMode type, String pathName)
throws IOException {
- assertIteration(type, pathName, Collections.<Attribute> emptyList(),
- Collections.<Attribute> emptyList(),
- Collections.<Attribute> emptyList());
+ assertIteration(type, pathName, Collections.<Attribute> emptyList());
}
private void assertIteration(FileMode type, String pathName,
- List<Attribute> nodeAttrs, List<Attribute> infoAttrs,
- List<Attribute> globalAttrs)
+ List<Attribute> nodeAttrs)
throws IOException {
assertTrue("walk has entry", walk.next());
assertEquals(pathName, walk.getPathString());
@@ -227,29 +212,27 @@ public class AttributesNodeWorkingTreeIteratorTest extends RepositoryTestCase {
WorkingTreeIterator itr = walk.getTree(0, WorkingTreeIterator.class);
assertNotNull("has tree", itr);
- AttributesNode attributeNode = itr.getEntryAttributesNode();
- assertAttributeNode(pathName, attributeNode, nodeAttrs);
- AttributesNode infoAttributeNode = itr.getInfoAttributesNode();
- assertAttributeNode(pathName, infoAttributeNode, infoAttrs);
- AttributesNode globalAttributeNode = itr.getGlobalAttributesNode();
- assertAttributeNode(pathName, globalAttributeNode, globalAttrs);
+ AttributesNode attributesNode = itr.getEntryAttributesNode();
+ assertAttributesNode(pathName, attributesNode, nodeAttrs);
if (D.equals(type))
walk.enterSubtree();
}
- private void assertAttributeNode(String pathName,
- AttributesNode attributeNode, List<Attribute> nodeAttrs) {
- if (attributeNode == null)
+ private void assertAttributesNode(String pathName,
+ AttributesNode attributesNode, List<Attribute> nodeAttrs) {
+ if (attributesNode == null)
assertTrue(nodeAttrs == null || nodeAttrs.isEmpty());
else {
- Map<String, Attribute> entryAttributes = new LinkedHashMap<String, Attribute>();
- attributeNode.getAttributes(pathName, false, entryAttributes);
+ Attributes entryAttributes = new Attributes();
+ attributesNode.getAttributes(pathName,
+ false, entryAttributes);
if (nodeAttrs != null && !nodeAttrs.isEmpty()) {
for (Attribute attribute : nodeAttrs) {
- assertThat(entryAttributes.values(), hasItem(attribute));
+ assertThat(entryAttributes.getAll(),
+ hasItem(attribute));
}
} else {
assertTrue(
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java
new file mode 100644
index 0000000000..b044c01db6
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java
@@ -0,0 +1,866 @@
+/*
+ * Copyright (C) 2014, Obeo.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.attributes;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.NoFilepatternException;
+import org.eclipse.jgit.attributes.Attribute.State;
+import org.eclipse.jgit.dircache.DirCacheIterator;
+import org.eclipse.jgit.errors.NoWorkTreeException;
+import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.treewalk.FileTreeIterator;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests the attributes are correctly computed in a {@link TreeWalk}.
+ *
+ * @see TreeWalk#getAttributes()
+ */
+public class TreeWalkAttributeTest extends RepositoryTestCase {
+
+ private static final FileMode M = FileMode.MISSING;
+
+ private static final FileMode D = FileMode.TREE;
+
+ private static final FileMode F = FileMode.REGULAR_FILE;
+
+ private static Attribute EOL_CRLF = new Attribute("eol", "crlf");
+
+ private static Attribute EOL_LF = new Attribute("eol", "lf");
+
+ private static Attribute TEXT_SET = new Attribute("text", State.SET);
+
+ private static Attribute TEXT_UNSET = new Attribute("text", State.UNSET);
+
+ private static Attribute DELTA_UNSET = new Attribute("delta", State.UNSET);
+
+ private static Attribute DELTA_SET = new Attribute("delta", State.SET);
+
+ private static Attribute CUSTOM_GLOBAL = new Attribute("custom", "global");
+
+ private static Attribute CUSTOM_INFO = new Attribute("custom", "info");
+
+ private static Attribute CUSTOM_ROOT = new Attribute("custom", "root");
+
+ private static Attribute CUSTOM_PARENT = new Attribute("custom", "parent");
+
+ private static Attribute CUSTOM_CURRENT = new Attribute("custom", "current");
+
+ private static Attribute CUSTOM2_UNSET = new Attribute("custom2",
+ State.UNSET);
+
+ private static Attribute CUSTOM2_SET = new Attribute("custom2", State.SET);
+
+ private TreeWalk walk;
+
+ private TreeWalk ci_walk;
+
+ private Git git;
+
+ private File customAttributeFile;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ git = new Git(db);
+ }
+
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ if (customAttributeFile != null)
+ customAttributeFile.delete();
+ }
+
+ /**
+ * Checks that the attributes are computed correctly depending on the
+ * operation type.
+ * <p>
+ * In this test we changed the content of the attribute files in the working
+ * tree compared to the one in the index.
+ * </p>
+ *
+ * @throws IOException
+ * @throws NoFilepatternException
+ * @throws GitAPIException
+ */
+ @Test
+ public void testCheckinCheckoutDifferences() throws IOException,
+ NoFilepatternException, GitAPIException {
+
+ writeGlobalAttributeFile("globalAttributesFile", "*.txt -custom2");
+ writeAttributesFile(".git/info/attributes", "*.txt eol=crlf");
+ writeAttributesFile(".gitattributes", "*.txt custom=root");
+ writeAttributesFile("level1/.gitattributes", "*.txt text");
+ writeAttributesFile("level1/level2/.gitattributes", "*.txt -delta");
+
+ writeTrashFile("l0.txt", "");
+
+ writeTrashFile("level1/l1.txt", "");
+
+ writeTrashFile("level1/level2/l2.txt", "");
+
+ git.add().addFilepattern(".").call();
+
+ beginWalk();
+
+ // Modify all attributes
+ writeGlobalAttributeFile("globalAttributesFile", "*.txt custom2");
+ writeAttributesFile(".git/info/attributes", "*.txt eol=lf");
+ writeAttributesFile(".gitattributes", "*.txt custom=info");
+ writeAttributesFile("level1/.gitattributes", "*.txt -text");
+ writeAttributesFile("level1/level2/.gitattributes", "*.txt delta");
+
+ assertEntry(F, ".gitattributes");
+ assertEntry(F, "l0.txt", asSet(EOL_LF, CUSTOM_INFO, CUSTOM2_SET),
+ asSet(EOL_LF, CUSTOM_ROOT, CUSTOM2_SET));
+
+ assertEntry(D, "level1");
+ assertEntry(F, "level1/.gitattributes");
+ assertEntry(F, "level1/l1.txt",
+ asSet(EOL_LF, CUSTOM_INFO, CUSTOM2_SET, TEXT_UNSET),
+ asSet(EOL_LF, CUSTOM_ROOT, CUSTOM2_SET, TEXT_SET));
+
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/.gitattributes");
+ assertEntry(F, "level1/level2/l2.txt",
+ asSet(EOL_LF, CUSTOM_INFO, CUSTOM2_SET, TEXT_UNSET, DELTA_SET),
+ asSet(EOL_LF, CUSTOM_ROOT, CUSTOM2_SET, TEXT_SET, DELTA_UNSET));
+
+ endWalk();
+ }
+
+ /**
+ * Checks that the index is used as fallback when the git attributes file
+ * are missing in the working tree.
+ *
+ * @throws IOException
+ * @throws NoFilepatternException
+ * @throws GitAPIException
+ */
+ @Test
+ public void testIndexOnly() throws IOException, NoFilepatternException,
+ GitAPIException {
+ List<File> attrFiles = new ArrayList<File>();
+ attrFiles.add(writeGlobalAttributeFile("globalAttributesFile",
+ "*.txt -custom2"));
+ attrFiles.add(writeAttributesFile(".git/info/attributes",
+ "*.txt eol=crlf"));
+ attrFiles
+ .add(writeAttributesFile(".gitattributes", "*.txt custom=root"));
+ attrFiles
+ .add(writeAttributesFile("level1/.gitattributes", "*.txt text"));
+ attrFiles.add(writeAttributesFile("level1/level2/.gitattributes",
+ "*.txt -delta"));
+
+ writeTrashFile("l0.txt", "");
+
+ writeTrashFile("level1/l1.txt", "");
+
+ writeTrashFile("level1/level2/l2.txt", "");
+
+ git.add().addFilepattern(".").call();
+
+ // Modify all attributes
+ for (File attrFile : attrFiles)
+ attrFile.delete();
+
+ beginWalk();
+
+ assertEntry(M, ".gitattributes");
+ assertEntry(F, "l0.txt", asSet(CUSTOM_ROOT));
+
+ assertEntry(D, "level1");
+ assertEntry(M, "level1/.gitattributes");
+ assertEntry(F, "level1/l1.txt",
+
+ asSet(CUSTOM_ROOT, TEXT_SET));
+
+ assertEntry(D, "level1/level2");
+ assertEntry(M, "level1/level2/.gitattributes");
+ assertEntry(F, "level1/level2/l2.txt",
+
+ asSet(CUSTOM_ROOT, TEXT_SET, DELTA_UNSET));
+
+ endWalk();
+ }
+
+ /**
+ * Check that we search in the working tree for attributes although the file
+ * we are currently inspecting does not exist anymore in the working tree.
+ *
+ * @throws IOException
+ * @throws NoFilepatternException
+ * @throws GitAPIException
+ */
+ @Test
+ public void testIndexOnly2()
+ throws IOException, NoFilepatternException, GitAPIException {
+ File l2 = writeTrashFile("level1/level2/l2.txt", "");
+ writeTrashFile("level1/level2/l1.txt", "");
+
+ git.add().addFilepattern(".").call();
+
+ writeAttributesFile(".gitattributes", "*.txt custom=root");
+ assertTrue(l2.delete());
+
+ beginWalk();
+
+ assertEntry(F, ".gitattributes");
+ assertEntry(D, "level1");
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/l1.txt", asSet(CUSTOM_ROOT));
+ assertEntry(M, "level1/level2/l2.txt", asSet(CUSTOM_ROOT));
+
+ endWalk();
+ }
+
+ /**
+ * Basic test for git attributes.
+ * <p>
+ * In this use case files are present in both the working tree and the index
+ * </p>
+ *
+ * @throws IOException
+ * @throws NoFilepatternException
+ * @throws GitAPIException
+ */
+ @Test
+ public void testRules() throws IOException, NoFilepatternException,
+ GitAPIException {
+ writeAttributesFile(".git/info/attributes", "windows* eol=crlf");
+
+ writeAttributesFile(".gitattributes", "*.txt eol=lf");
+ writeTrashFile("windows.file", "");
+ writeTrashFile("windows.txt", "");
+ writeTrashFile("readme.txt", "");
+
+ writeAttributesFile("src/config/.gitattributes", "*.txt -delta");
+ writeTrashFile("src/config/readme.txt", "");
+ writeTrashFile("src/config/windows.file", "");
+ writeTrashFile("src/config/windows.txt", "");
+
+ beginWalk();
+
+ git.add().addFilepattern(".").call();
+
+ assertEntry(F, ".gitattributes");
+ assertEntry(F, "readme.txt", asSet(EOL_LF));
+
+ assertEntry(D, "src");
+ assertEntry(D, "src/config");
+ assertEntry(F, "src/config/.gitattributes");
+ assertEntry(F, "src/config/readme.txt", asSet(DELTA_UNSET, EOL_LF));
+ assertEntry(F, "src/config/windows.file", asSet(EOL_CRLF));
+ assertEntry(F, "src/config/windows.txt", asSet(DELTA_UNSET, EOL_CRLF));
+
+ assertEntry(F, "windows.file", asSet(EOL_CRLF));
+ assertEntry(F, "windows.txt", asSet(EOL_CRLF));
+
+ endWalk();
+ }
+
+ /**
+ * Checks that if there is no .gitattributes file in the repository
+ * everything still work fine.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testNoAttributes() throws IOException {
+ writeTrashFile("l0.txt", "");
+ writeTrashFile("level1/l1.txt", "");
+ writeTrashFile("level1/level2/l2.txt", "");
+
+ beginWalk();
+
+ assertEntry(F, "l0.txt");
+
+ assertEntry(D, "level1");
+ assertEntry(F, "level1/l1.txt");
+
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/l2.txt");
+
+ endWalk();
+ }
+
+ /**
+ * Checks that an empty .gitattribute file does not return incorrect value.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testEmptyGitAttributeFile() throws IOException {
+ writeAttributesFile(".git/info/attributes", "");
+ writeTrashFile("l0.txt", "");
+ writeAttributesFile(".gitattributes", "");
+ writeTrashFile("level1/l1.txt", "");
+ writeTrashFile("level1/level2/l2.txt", "");
+
+ beginWalk();
+
+ assertEntry(F, ".gitattributes");
+ assertEntry(F, "l0.txt");
+
+ assertEntry(D, "level1");
+ assertEntry(F, "level1/l1.txt");
+
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/l2.txt");
+
+ endWalk();
+ }
+
+ @Test
+ public void testNoMatchingAttributes() throws IOException {
+ writeAttributesFile(".git/info/attributes", "*.java delta");
+ writeAttributesFile(".gitattributes", "*.java -delta");
+ writeAttributesFile("levelA/.gitattributes", "*.java eol=lf");
+ writeAttributesFile("levelB/.gitattributes", "*.txt eol=lf");
+
+ writeTrashFile("levelA/lA.txt", "");
+
+ beginWalk();
+
+ assertEntry(F, ".gitattributes");
+
+ assertEntry(D, "levelA");
+ assertEntry(F, "levelA/.gitattributes");
+ assertEntry(F, "levelA/lA.txt");
+
+ assertEntry(D, "levelB");
+ assertEntry(F, "levelB/.gitattributes");
+
+ endWalk();
+ }
+
+ /**
+ * Checks that $GIT_DIR/info/attributes file has the highest precedence.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testPrecedenceInfo() throws IOException {
+ writeGlobalAttributeFile("globalAttributesFile", "*.txt custom=global");
+ writeAttributesFile(".git/info/attributes", "*.txt custom=info");
+ writeAttributesFile(".gitattributes", "*.txt custom=root");
+ writeAttributesFile("level1/.gitattributes", "*.txt custom=parent");
+ writeAttributesFile("level1/level2/.gitattributes",
+ "*.txt custom=current");
+
+ writeTrashFile("level1/level2/file.txt", "");
+
+ beginWalk();
+
+ assertEntry(F, ".gitattributes");
+
+ assertEntry(D, "level1");
+ assertEntry(F, "level1/.gitattributes");
+
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/.gitattributes");
+ assertEntry(F, "level1/level2/file.txt", asSet(CUSTOM_INFO));
+
+ endWalk();
+ }
+
+ /**
+ * Checks that a subfolder ".gitattributes" file has precedence over its
+ * parent.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testPrecedenceCurrent() throws IOException {
+ writeGlobalAttributeFile("globalAttributesFile", "*.txt custom=global");
+ writeAttributesFile(".gitattributes", "*.txt custom=root");
+ writeAttributesFile("level1/.gitattributes", "*.txt custom=parent");
+ writeAttributesFile("level1/level2/.gitattributes",
+ "*.txt custom=current");
+
+ writeTrashFile("level1/level2/file.txt", "");
+
+ beginWalk();
+
+ assertEntry(F, ".gitattributes");
+
+ assertEntry(D, "level1");
+ assertEntry(F, "level1/.gitattributes");
+
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/.gitattributes");
+ assertEntry(F, "level1/level2/file.txt", asSet(CUSTOM_CURRENT));
+
+ endWalk();
+ }
+
+ /**
+ * Checks that the parent ".gitattributes" file is used as fallback.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testPrecedenceParent() throws IOException {
+ writeGlobalAttributeFile("globalAttributesFile", "*.txt custom=global");
+ writeAttributesFile(".gitattributes", "*.txt custom=root");
+ writeAttributesFile("level1/.gitattributes", "*.txt custom=parent");
+
+ writeTrashFile("level1/level2/file.txt", "");
+
+ beginWalk();
+
+ assertEntry(F, ".gitattributes");
+
+ assertEntry(D, "level1");
+ assertEntry(F, "level1/.gitattributes");
+
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/file.txt", asSet(CUSTOM_PARENT));
+
+ endWalk();
+ }
+
+ /**
+ * Checks that the grand parent ".gitattributes" file is used as fallback.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testPrecedenceRoot() throws IOException {
+ writeGlobalAttributeFile("globalAttributesFile", "*.txt custom=global");
+ writeAttributesFile(".gitattributes", "*.txt custom=root");
+
+ writeTrashFile("level1/level2/file.txt", "");
+
+ beginWalk();
+
+ assertEntry(F, ".gitattributes");
+
+ assertEntry(D, "level1");
+
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/file.txt", asSet(CUSTOM_ROOT));
+
+ endWalk();
+ }
+
+ /**
+ * Checks that the global attribute file is used as fallback.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testPrecedenceGlobal() throws IOException {
+ writeGlobalAttributeFile("globalAttributesFile", "*.txt custom=global");
+
+ writeTrashFile("level1/level2/file.txt", "");
+
+ beginWalk();
+
+ assertEntry(D, "level1");
+
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/file.txt", asSet(CUSTOM_GLOBAL));
+
+ endWalk();
+ }
+
+ /**
+ * Checks the precedence on a hierarchy with multiple attributes.
+ * <p>
+ * In this test all file are present in both the working tree and the index.
+ * </p>
+ *
+ * @throws IOException
+ * @throws GitAPIException
+ * @throws NoFilepatternException
+ */
+ @Test
+ public void testHierarchyBothIterator() throws IOException,
+ NoFilepatternException, GitAPIException {
+ writeAttributesFile(".git/info/attributes", "*.global eol=crlf");
+ writeAttributesFile(".gitattributes", "*.local eol=lf");
+ writeAttributesFile("level1/.gitattributes", "*.local text");
+ writeAttributesFile("level1/level2/.gitattributes", "*.local -text");
+
+ writeTrashFile("l0.global", "");
+ writeTrashFile("l0.local", "");
+
+ writeTrashFile("level1/l1.global", "");
+ writeTrashFile("level1/l1.local", "");
+
+ writeTrashFile("level1/level2/l2.global", "");
+ writeTrashFile("level1/level2/l2.local", "");
+
+ beginWalk();
+
+ git.add().addFilepattern(".").call();
+
+ assertEntry(F, ".gitattributes");
+ assertEntry(F, "l0.global", asSet(EOL_CRLF));
+ assertEntry(F, "l0.local", asSet(EOL_LF));
+
+ assertEntry(D, "level1");
+ assertEntry(F, "level1/.gitattributes");
+ assertEntry(F, "level1/l1.global", asSet(EOL_CRLF));
+ assertEntry(F, "level1/l1.local", asSet(EOL_LF, TEXT_SET));
+
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/.gitattributes");
+ assertEntry(F, "level1/level2/l2.global", asSet(EOL_CRLF));
+ assertEntry(F, "level1/level2/l2.local", asSet(EOL_LF, TEXT_UNSET));
+
+ endWalk();
+
+ }
+
+ /**
+ * Checks the precedence on a hierarchy with multiple attributes.
+ * <p>
+ * In this test all file are present only in the working tree.
+ * </p>
+ *
+ * @throws IOException
+ * @throws GitAPIException
+ * @throws NoFilepatternException
+ */
+ @Test
+ public void testHierarchyWorktreeOnly()
+ throws IOException, NoFilepatternException, GitAPIException {
+ writeAttributesFile(".git/info/attributes", "*.global eol=crlf");
+ writeAttributesFile(".gitattributes", "*.local eol=lf");
+ writeAttributesFile("level1/.gitattributes", "*.local text");
+ writeAttributesFile("level1/level2/.gitattributes", "*.local -text");
+
+ writeTrashFile("l0.global", "");
+ writeTrashFile("l0.local", "");
+
+ writeTrashFile("level1/l1.global", "");
+ writeTrashFile("level1/l1.local", "");
+
+ writeTrashFile("level1/level2/l2.global", "");
+ writeTrashFile("level1/level2/l2.local", "");
+
+ beginWalk();
+
+ assertEntry(F, ".gitattributes");
+ assertEntry(F, "l0.global", asSet(EOL_CRLF));
+ assertEntry(F, "l0.local", asSet(EOL_LF));
+
+ assertEntry(D, "level1");
+ assertEntry(F, "level1/.gitattributes");
+ assertEntry(F, "level1/l1.global", asSet(EOL_CRLF));
+ assertEntry(F, "level1/l1.local", asSet(EOL_LF, TEXT_SET));
+
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/.gitattributes");
+ assertEntry(F, "level1/level2/l2.global", asSet(EOL_CRLF));
+ assertEntry(F, "level1/level2/l2.local", asSet(EOL_LF, TEXT_UNSET));
+
+ endWalk();
+
+ }
+
+ /**
+ * Checks that the list of attributes is an aggregation of all the
+ * attributes from the attributes files hierarchy.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testAggregation() throws IOException {
+ writeGlobalAttributeFile("globalAttributesFile", "*.txt -custom2");
+ writeAttributesFile(".git/info/attributes", "*.txt eol=crlf");
+ writeAttributesFile(".gitattributes", "*.txt custom=root");
+ writeAttributesFile("level1/.gitattributes", "*.txt text");
+ writeAttributesFile("level1/level2/.gitattributes", "*.txt -delta");
+
+ writeTrashFile("l0.txt", "");
+
+ writeTrashFile("level1/l1.txt", "");
+
+ writeTrashFile("level1/level2/l2.txt", "");
+
+ beginWalk();
+
+ assertEntry(F, ".gitattributes");
+ assertEntry(F, "l0.txt", asSet(EOL_CRLF, CUSTOM_ROOT, CUSTOM2_UNSET));
+
+ assertEntry(D, "level1");
+ assertEntry(F, "level1/.gitattributes");
+ assertEntry(F, "level1/l1.txt",
+ asSet(EOL_CRLF, CUSTOM_ROOT, TEXT_SET, CUSTOM2_UNSET));
+
+ assertEntry(D, "level1/level2");
+ assertEntry(F, "level1/level2/.gitattributes");
+ assertEntry(
+ F,
+ "level1/level2/l2.txt",
+ asSet(EOL_CRLF, CUSTOM_ROOT, TEXT_SET, DELTA_UNSET,
+ CUSTOM2_UNSET));
+
+ endWalk();
+
+ }
+
+ /**
+ * Checks that the last entry in .gitattributes is used if 2 lines match the
+ * same attribute
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testOverriding() throws IOException {
+ writeAttributesFile(".git/info/attributes",//
+ //
+ "*.txt custom=current",//
+ "*.txt custom=parent",//
+ "*.txt custom=root",//
+ "*.txt custom=info",
+ //
+ "*.txt delta",//
+ "*.txt -delta",
+ //
+ "*.txt eol=lf",//
+ "*.txt eol=crlf",
+ //
+ "*.txt text",//
+ "*.txt -text");
+
+ writeTrashFile("l0.txt", "");
+ beginWalk();
+
+ assertEntry(F, "l0.txt",
+ asSet(TEXT_UNSET, EOL_CRLF, DELTA_UNSET, CUSTOM_INFO));
+
+ endWalk();
+ }
+
+ /**
+ * Checks that the last value of an attribute is used if in the same line an
+ * attribute is defined several time.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testOverriding2() throws IOException {
+ writeAttributesFile(".git/info/attributes",
+ "*.txt custom=current custom=parent custom=root custom=info",//
+ "*.txt delta -delta",//
+ "*.txt eol=lf eol=crlf",//
+ "*.txt text -text");
+ writeTrashFile("l0.txt", "");
+ beginWalk();
+
+ assertEntry(F, "l0.txt",
+ asSet(TEXT_UNSET, EOL_CRLF, DELTA_UNSET, CUSTOM_INFO));
+
+ endWalk();
+ }
+
+ @Test
+ public void testRulesInherited() throws Exception {
+ writeAttributesFile(".gitattributes", "**/*.txt eol=lf");
+ writeTrashFile("src/config/readme.txt", "");
+ writeTrashFile("src/config/windows.file", "");
+
+ beginWalk();
+
+ assertEntry(F, ".gitattributes");
+ assertEntry(D, "src");
+ assertEntry(D, "src/config");
+
+ assertEntry(F, "src/config/readme.txt", asSet(EOL_LF));
+ assertEntry(F, "src/config/windows.file",
+ Collections.<Attribute> emptySet());
+
+ endWalk();
+ }
+
+ private void beginWalk() throws NoWorkTreeException, IOException {
+ walk = new TreeWalk(db);
+ walk.addTree(new FileTreeIterator(db));
+ walk.addTree(new DirCacheIterator(db.readDirCache()));
+
+ ci_walk = new TreeWalk(db);
+ ci_walk.setOperationType(OperationType.CHECKIN_OP);
+ ci_walk.addTree(new FileTreeIterator(db));
+ ci_walk.addTree(new DirCacheIterator(db.readDirCache()));
+ }
+
+ /**
+ * Assert an entry in which checkin and checkout attributes are expected to
+ * be the same.
+ *
+ * @param type
+ * @param pathName
+ * @param forBothOperaiton
+ * @throws IOException
+ */
+ private void assertEntry(FileMode type, String pathName,
+ Set<Attribute> forBothOperaiton) throws IOException {
+ assertEntry(type, pathName, forBothOperaiton, forBothOperaiton);
+ }
+
+ /**
+ * Assert an entry with no attribute expected.
+ *
+ * @param type
+ * @param pathName
+ * @throws IOException
+ */
+ private void assertEntry(FileMode type, String pathName) throws IOException {
+ assertEntry(type, pathName, Collections.<Attribute> emptySet(),
+ Collections.<Attribute> emptySet());
+ }
+
+ /**
+ * Assert that an entry;
+ * <ul>
+ * <li>Has the correct type</li>
+ * <li>Exist in the tree walk</li>
+ * <li>Has the expected attributes on a checkin operation</li>
+ * <li>Has the expected attributes on a checkout operation</li>
+ * </ul>
+ *
+ * @param type
+ * @param pathName
+ * @param checkinAttributes
+ * @param checkoutAttributes
+ * @throws IOException
+ */
+ private void assertEntry(FileMode type, String pathName,
+ Set<Attribute> checkinAttributes, Set<Attribute> checkoutAttributes)
+ throws IOException {
+ assertTrue("walk has entry", walk.next());
+ assertTrue("walk has entry", ci_walk.next());
+ assertEquals(pathName, walk.getPathString());
+ assertEquals(type, walk.getFileMode(0));
+
+ assertEquals(checkinAttributes,
+ asSet(ci_walk.getAttributes().getAll()));
+ assertEquals(checkoutAttributes,
+ asSet(walk.getAttributes().getAll()));
+
+ if (D.equals(type)) {
+ walk.enterSubtree();
+ ci_walk.enterSubtree();
+ }
+ }
+
+ private static Set<Attribute> asSet(Collection<Attribute> attributes) {
+ Set<Attribute> ret = new HashSet<Attribute>();
+ for (Attribute a : attributes) {
+ ret.add(a);
+ }
+ return (ret);
+ }
+
+ private File writeAttributesFile(String name, String... rules)
+ throws IOException {
+ StringBuilder data = new StringBuilder();
+ for (String line : rules)
+ data.append(line + "\n");
+ return writeTrashFile(name, data.toString());
+ }
+
+ /**
+ * Creates an attributes file and set its location in the git configuration.
+ *
+ * @param fileName
+ * @param attributes
+ * @return The attribute file
+ * @throws IOException
+ * @see Repository#getConfig()
+ */
+ private File writeGlobalAttributeFile(String fileName, String... attributes)
+ throws IOException {
+ customAttributeFile = File.createTempFile("tmp_", fileName, null);
+ customAttributeFile.deleteOnExit();
+ StringBuilder attributesFileContent = new StringBuilder();
+ for (String attr : attributes) {
+ attributesFileContent.append(attr).append("\n");
+ }
+ JGitTestUtil.write(customAttributeFile,
+ attributesFileContent.toString());
+ db.getConfig().setString("core", null, "attributesfile",
+ customAttributeFile.getAbsolutePath());
+ return customAttributeFile;
+ }
+
+ static Set<Attribute> asSet(Attribute... attrs) {
+ HashSet<Attribute> result = new HashSet<Attribute>();
+ for (Attribute attr : attrs)
+ result.add(attr);
+ return result;
+ }
+
+ private void endWalk() throws IOException {
+ assertFalse("Not all files tested", walk.next());
+ assertFalse("Not all files tested", ci_walk.next());
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java
index e83ef772ff..4315be9e49 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/RenameDetectorTest.java
@@ -48,6 +48,7 @@ import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import java.util.Arrays;
import java.util.List;
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
@@ -227,6 +228,19 @@ public class RenameDetectorTest extends RepositoryTestCase {
}
@Test
+ public void testExactRename_UnstagedFile() throws Exception {
+ ObjectId aId = blob("foo");
+ DiffEntry a = DiffEntry.delete(PATH_A, aId);
+ DiffEntry b = DiffEntry.add(PATH_B, aId);
+
+ rd.addAll(Arrays.asList(a, b));
+ List<DiffEntry> entries = rd.compute();
+
+ assertEquals(1, entries.size());
+ assertRename(a, b, 100, entries.get(0));
+ }
+
+ @Test
public void testInexactRename_OnePair() throws Exception {
ObjectId aId = blob("foo\nbar\nbaz\nblarg\n");
ObjectId bId = blob("foo\nbar\nbaz\nblah\n");
@@ -430,6 +444,23 @@ public class RenameDetectorTest extends RepositoryTestCase {
}
@Test
+ public void testNoRenames_UntrackedFile() throws Exception {
+ ObjectId aId = blob("foo");
+ ObjectId bId = ObjectId
+ .fromString("3049eb6eee7e1318f4e78e799bf33f1e54af9cbf");
+
+ DiffEntry a = DiffEntry.delete(PATH_A, aId);
+ DiffEntry b = DiffEntry.add(PATH_B, bId);
+
+ rd.addAll(Arrays.asList(a, b));
+ List<DiffEntry> entries = rd.compute();
+
+ assertEquals(2, entries.size());
+ assertSame(a, entries.get(0));
+ assertSame(b, entries.get(1));
+ }
+
+ @Test
public void testBreakModify_BreakAll() throws Exception {
ObjectId aId = blob("foo");
ObjectId bId = blob("bar");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheEntryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheEntryTest.java
index e159ed939e..dc7181aece 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheEntryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheEntryTest.java
@@ -69,9 +69,9 @@ public class DirCacheEntryTest {
assertFalse(isValidPath("a\u0000b"));
}
- private static boolean isValidPath(final String path) {
+ private static boolean isValidPath(String path) {
try {
- DirCacheCheckout.checkValidPath(path);
+ new DirCacheEntry(path);
return true;
} catch (InvalidPathException e) {
return false;
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java
index c7336da408..fa40458f34 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java
@@ -77,7 +77,7 @@ public class GcPackRefsTest extends GcTestCase {
tr.lightweightTag("t", a);
gc.packRefs();
- assertSame(repo.getRef("t").getStorage(), Storage.PACKED);
+ assertSame(repo.exactRef("refs/tags/t").getStorage(), Storage.PACKED);
}
@Test
@@ -126,8 +126,8 @@ public class GcPackRefsTest extends GcTestCase {
refLock.unlock();
}
- assertSame(repo.getRef("refs/tags/t1").getStorage(), Storage.LOOSE);
- assertSame(repo.getRef("refs/tags/t2").getStorage(), Storage.PACKED);
+ assertSame(repo.exactRef("refs/tags/t1").getStorage(), Storage.LOOSE);
+ assertSame(repo.exactRef("refs/tags/t2").getStorage(), Storage.PACKED);
}
@Test
@@ -146,7 +146,7 @@ public class GcPackRefsTest extends GcTestCase {
public Result call() throws Exception {
RefUpdate update = new RefDirectoryUpdate(
(RefDirectory) repo.getRefDatabase(),
- repo.getRef("refs/tags/t")) {
+ repo.exactRef("refs/tags/t")) {
@Override
public boolean isForceUpdate() {
try {
@@ -182,7 +182,7 @@ public class GcPackRefsTest extends GcTestCase {
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
}
- assertEquals(repo.getRef("refs/tags/t").getObjectId(), b);
+ assertEquals(repo.exactRef("refs/tags/t").getObjectId(), b);
}
@Test
@@ -194,23 +194,23 @@ public class GcPackRefsTest extends GcTestCase {
// check for the unborn branch master. HEAD should point to master and
// master doesn't exist.
- assertEquals(repo.getRef("HEAD").getTarget().getName(),
+ assertEquals(repo.exactRef("HEAD").getTarget().getName(),
"refs/heads/master");
- assertNull(repo.getRef("HEAD").getTarget().getObjectId());
+ assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
gc.packRefs();
- assertSame(repo.getRef("HEAD").getStorage(), Storage.LOOSE);
- assertEquals(repo.getRef("HEAD").getTarget().getName(),
+ assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
+ assertEquals(repo.exactRef("HEAD").getTarget().getName(),
"refs/heads/master");
- assertNull(repo.getRef("HEAD").getTarget().getObjectId());
+ assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
git.checkout().setName("refs/heads/side").call();
gc.packRefs();
- assertSame(repo.getRef("HEAD").getStorage(), Storage.LOOSE);
+ assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
// check for detached HEAD
git.checkout().setName(first.getName()).call();
gc.packRefs();
- assertSame(repo.getRef("HEAD").getStorage(), Storage.LOOSE);
+ assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
}
@Test
@@ -229,20 +229,20 @@ public class GcPackRefsTest extends GcTestCase {
// check for the unborn branch master. HEAD should point to master and
// master doesn't exist.
- assertEquals(repo.getRef("HEAD").getTarget().getName(),
+ assertEquals(repo.exactRef("HEAD").getTarget().getName(),
"refs/heads/master");
- assertNull(repo.getRef("HEAD").getTarget().getObjectId());
+ assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
gc.packRefs();
- assertSame(repo.getRef("HEAD").getStorage(), Storage.LOOSE);
- assertEquals(repo.getRef("HEAD").getTarget().getName(),
+ assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
+ assertEquals(repo.exactRef("HEAD").getTarget().getName(),
"refs/heads/master");
- assertNull(repo.getRef("HEAD").getTarget().getObjectId());
+ assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
// check for non-detached HEAD
repo.updateRef(Constants.HEAD).link("refs/heads/side");
gc.packRefs();
- assertSame(repo.getRef("HEAD").getStorage(), Storage.LOOSE);
- assertEquals(repo.getRef("HEAD").getTarget().getObjectId(),
+ assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
+ assertEquals(repo.exactRef("HEAD").getTarget().getObjectId(),
second.getId());
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java
index 0991598920..52e181bc80 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
@@ -858,6 +858,36 @@ public class RefDirectoryTest extends LocalDiskRepositoryTestCase {
}
@Test
+ public void testGetRef_CycleInSymbolicRef() throws IOException {
+ Ref r;
+
+ writeLooseRef("refs/1", "ref: refs/2\n");
+ writeLooseRef("refs/2", "ref: refs/3\n");
+ writeLooseRef("refs/3", "ref: refs/4\n");
+ writeLooseRef("refs/4", "ref: refs/5\n");
+ writeLooseRef("refs/5", "ref: refs/end\n");
+ writeLooseRef("refs/end", A);
+
+ r = refdir.getRef("1");
+ assertEquals("refs/1", r.getName());
+ assertEquals(A, r.getObjectId());
+ assertTrue(r.isSymbolic());
+
+ writeLooseRef("refs/5", "ref: refs/6\n");
+ writeLooseRef("refs/6", "ref: refs/end\n");
+
+ r = refdir.getRef("1");
+ assertNull("missing 1 due to cycle", r);
+
+ writeLooseRef("refs/heads/1", B);
+
+ r = refdir.getRef("1");
+ assertEquals("refs/heads/1", r.getName());
+ assertEquals(B, r.getObjectId());
+ assertFalse(r.isSymbolic());
+ }
+
+ @Test
public void testGetRefs_PackedNotPeeled_Sorted() throws IOException {
Map<String, Ref> all;
@@ -1025,6 +1055,25 @@ public class RefDirectoryTest extends LocalDiskRepositoryTestCase {
}
@Test
+ public void testExactRef_EmptyDatabase() throws IOException {
+ Ref r;
+
+ r = refdir.exactRef(HEAD);
+ assertTrue(r.isSymbolic());
+ assertSame(LOOSE, r.getStorage());
+ assertEquals("refs/heads/master", r.getTarget().getName());
+ assertSame(NEW, r.getTarget().getStorage());
+ assertNull(r.getTarget().getObjectId());
+
+ assertNull(refdir.exactRef("refs/heads/master"));
+ assertNull(refdir.exactRef("refs/tags/v1.0"));
+ assertNull(refdir.exactRef("FETCH_HEAD"));
+ assertNull(refdir.exactRef("NOT.A.REF.NAME"));
+ assertNull(refdir.exactRef("master"));
+ assertNull(refdir.exactRef("v1.0"));
+ }
+
+ @Test
public void testGetRef_FetchHead() throws IOException {
// This is an odd special case where we need to make sure we read
// exactly the first 40 bytes of the file and nothing further on
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
index 098b31fccf..8744ed1bd6 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefUpdateTest.java
@@ -347,7 +347,7 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
Result update = updateRef.update();
assertEquals(Result.FORCED, update);
assertEquals(ppid, db.resolve("HEAD"));
- Ref ref = db.getRef("HEAD");
+ Ref ref = db.exactRef("HEAD");
assertEquals("HEAD", ref.getName());
assertTrue("is detached", !ref.isSymbolic());
@@ -377,7 +377,7 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
Result update = updateRef.update();
assertEquals(Result.NEW, update);
assertEquals(ppid, db.resolve("HEAD"));
- Ref ref = db.getRef("HEAD");
+ Ref ref = db.exactRef("HEAD");
assertEquals("HEAD", ref.getName());
assertTrue("is detached", !ref.isSymbolic());
@@ -681,13 +681,13 @@ public class RefUpdateTest extends SampleDataRepositoryTestCase {
public void testRenameBranchAlsoInPack() throws IOException {
ObjectId rb = db.resolve("refs/heads/b");
ObjectId rb2 = db.resolve("refs/heads/b~1");
- assertEquals(Ref.Storage.PACKED, db.getRef("refs/heads/b").getStorage());
+ assertEquals(Ref.Storage.PACKED, db.exactRef("refs/heads/b").getStorage());
RefUpdate updateRef = db.updateRef("refs/heads/b");
updateRef.setNewObjectId(rb2);
updateRef.setForceUpdate(true);
Result update = updateRef.update();
assertEquals("internal check new ref is loose", Result.FORCED, update);
- assertEquals(Ref.Storage.LOOSE, db.getRef("refs/heads/b").getStorage());
+ assertEquals(Ref.Storage.LOOSE, db.exactRef("refs/heads/b").getStorage());
writeReflog(db, rb, "Just a message", "refs/heads/b");
assertTrue("log on old branch", new File(db.getDirectory(),
"logs/refs/heads/b").exists());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/junit/TestRepositoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/junit/TestRepositoryTest.java
index b69b8e01e0..8b54dabce3 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/junit/TestRepositoryTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/junit/TestRepositoryTest.java
@@ -127,25 +127,25 @@ public class TestRepositoryTest {
@Test
public void resetFromSymref() throws Exception {
repo.updateRef("HEAD").link("refs/heads/master");
- Ref head = repo.getRef("HEAD");
+ Ref head = repo.exactRef("HEAD");
RevCommit master = tr.branch("master").commit().create();
RevCommit branch = tr.branch("branch").commit().create();
RevCommit detached = tr.commit().create();
assertTrue(head.isSymbolic());
assertEquals("refs/heads/master", head.getTarget().getName());
- assertEquals(master, repo.getRef("refs/heads/master").getObjectId());
- assertEquals(branch, repo.getRef("refs/heads/branch").getObjectId());
+ assertEquals(master, repo.exactRef("refs/heads/master").getObjectId());
+ assertEquals(branch, repo.exactRef("refs/heads/branch").getObjectId());
// Reset to branches preserves symref.
tr.reset("master");
- head = repo.getRef("HEAD");
+ head = repo.exactRef("HEAD");
assertEquals(master, head.getObjectId());
assertTrue(head.isSymbolic());
assertEquals("refs/heads/master", head.getTarget().getName());
tr.reset("branch");
- head = repo.getRef("HEAD");
+ head = repo.exactRef("HEAD");
assertEquals(branch, head.getObjectId());
assertTrue(head.isSymbolic());
assertEquals("refs/heads/master", head.getTarget().getName());
@@ -153,50 +153,50 @@ public class TestRepositoryTest {
// Reset to a SHA-1 detaches.
tr.reset(detached);
- head = repo.getRef("HEAD");
+ head = repo.exactRef("HEAD");
assertEquals(detached, head.getObjectId());
assertFalse(head.isSymbolic());
tr.reset(detached.name());
- head = repo.getRef("HEAD");
+ head = repo.exactRef("HEAD");
assertEquals(detached, head.getObjectId());
assertFalse(head.isSymbolic());
// Reset back to a branch remains detached.
tr.reset("master");
- head = repo.getRef("HEAD");
+ head = repo.exactRef("HEAD");
assertEquals(lastHeadBeforeDetach, head.getObjectId());
assertFalse(head.isSymbolic());
}
@Test
public void resetFromDetachedHead() throws Exception {
- Ref head = repo.getRef("HEAD");
+ Ref head = repo.exactRef("HEAD");
RevCommit master = tr.branch("master").commit().create();
RevCommit branch = tr.branch("branch").commit().create();
RevCommit detached = tr.commit().create();
assertNull(head);
- assertEquals(master, repo.getRef("refs/heads/master").getObjectId());
- assertEquals(branch, repo.getRef("refs/heads/branch").getObjectId());
+ assertEquals(master, repo.exactRef("refs/heads/master").getObjectId());
+ assertEquals(branch, repo.exactRef("refs/heads/branch").getObjectId());
tr.reset("master");
- head = repo.getRef("HEAD");
+ head = repo.exactRef("HEAD");
assertEquals(master, head.getObjectId());
assertFalse(head.isSymbolic());
tr.reset("branch");
- head = repo.getRef("HEAD");
+ head = repo.exactRef("HEAD");
assertEquals(branch, head.getObjectId());
assertFalse(head.isSymbolic());
tr.reset(detached);
- head = repo.getRef("HEAD");
+ head = repo.exactRef("HEAD");
assertEquals(detached, head.getObjectId());
assertFalse(head.isSymbolic());
tr.reset(detached.name());
- head = repo.getRef("HEAD");
+ head = repo.exactRef("HEAD");
assertEquals(detached, head.getObjectId());
assertFalse(head.isSymbolic());
}
@@ -222,7 +222,7 @@ public class TestRepositoryTest {
.tick(3)
.add("bar", "fixed bar contents")
.create();
- assertEquals(amended, repo.getRef("refs/heads/master").getObjectId());
+ assertEquals(amended, repo.exactRef("refs/heads/master").getObjectId());
rw.parseBody(amended);
assertEquals(1, amended.getParentCount());
@@ -257,7 +257,7 @@ public class TestRepositoryTest {
.add("foo", "fixed foo contents")
.create();
- Ref head = repo.getRef(Constants.HEAD);
+ Ref head = repo.exactRef(Constants.HEAD);
assertEquals(amended, head.getObjectId());
assertTrue(head.isSymbolic());
assertEquals("refs/heads/master", head.getTarget().getName());
@@ -291,7 +291,7 @@ public class TestRepositoryTest {
public void commitToUnbornHead() throws Exception {
repo.updateRef("HEAD").link("refs/heads/master");
RevCommit root = tr.branch("HEAD").commit().create();
- Ref ref = repo.getRef(Constants.HEAD);
+ Ref ref = repo.exactRef(Constants.HEAD);
assertEquals(root, ref.getObjectId());
assertTrue(ref.isSymbolic());
assertEquals("refs/heads/master", ref.getTarget().getName());
@@ -316,7 +316,7 @@ public class TestRepositoryTest {
RevCommit result = tr.cherryPick(toPick);
rw.parseBody(result);
- Ref headRef = tr.getRepository().getRef("HEAD");
+ Ref headRef = tr.getRepository().exactRef("HEAD");
assertEquals(result, headRef.getObjectId());
assertTrue(headRef.isSymbolic());
assertEquals("refs/heads/master", headRef.getLeaf().getName());
@@ -371,7 +371,7 @@ public class TestRepositoryTest {
.create();
assertNotEquals(head, toPick);
assertNull(tr.cherryPick(toPick));
- assertEquals(head, repo.getRef("HEAD").getObjectId());
+ assertEquals(head, repo.exactRef("HEAD").getObjectId());
}
private String blobAsString(AnyObjectId treeish, String path)
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
index 6d62528f85..d768e0fa0b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
@@ -79,6 +79,7 @@ import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils;
+import org.junit.Assume;
import org.junit.Test;
public class DirCacheCheckoutTest extends RepositoryTestCase {
@@ -112,7 +113,7 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
return dco.getRemoved();
}
- private Map<String, ObjectId> getUpdated() {
+ private Map<String, String> getUpdated() {
return dco.getUpdated();
}
@@ -267,8 +268,6 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
@Test
public void testRules1thru3_NoIndexEntry() throws IOException {
ObjectId head = buildTree(mk("foo"));
- TreeWalk tw = TreeWalk.forPath(db, "foo", head);
- ObjectId objectId = tw.getObjectId(0);
ObjectId merge = db.newObjectInserter().insert(Constants.OBJ_TREE,
new byte[0]);
@@ -278,10 +277,9 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
prescanTwoTrees(merge, head);
- assertEquals(objectId, getUpdated().get("foo"));
+ assertTrue(getUpdated().containsKey("foo"));
merge = buildTree(mkmap("foo", "a"));
- tw = TreeWalk.forPath(db, "foo", merge);
prescanTwoTrees(head, merge);
@@ -925,6 +923,7 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
@Test
public void testCheckoutChangeLinkToEmptyDir() throws Exception {
+ Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
String fname = "was_file";
Git git = Git.wrap(db);
@@ -961,6 +960,7 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
@Test
public void testCheckoutChangeLinkToEmptyDirs() throws Exception {
+ Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
String fname = "was_file";
Git git = Git.wrap(db);
@@ -999,6 +999,7 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
@Test
public void testCheckoutChangeLinkToNonEmptyDirs() throws Exception {
+ Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
String fname = "file";
Git git = Git.wrap(db);
@@ -1043,6 +1044,7 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
@Test
public void testCheckoutChangeLinkToNonEmptyDirsAndNewIndexEntry()
throws Exception {
+ Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
String fname = "file";
Git git = Git.wrap(db);
@@ -1364,6 +1366,7 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
@Test
public void testOverwriteUntrackedLinkModeChange()
throws Exception {
+ Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
String fname = "file.txt";
Git git = Git.wrap(db);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java
index 863d79ddee..3259f622f3 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/IndexDiffSubmoduleTest.java
@@ -87,7 +87,7 @@ public class IndexDiffSubmoduleTest extends RepositoryTestCase {
.call();
submodule_db = (FileRepository) Git.wrap(db).submoduleAdd()
- .setPath("submodule")
+ .setPath("modules/submodule")
.setURI(submoduleStandalone.getDirectory().toURI().toString())
.call();
submoduleStandalone.close();
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
index 109f401898..707757b343 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RefTest.java
@@ -163,7 +163,7 @@ public class RefTest extends SampleDataRepositoryTestCase {
@Test
public void testReadSymRefToPacked() throws IOException {
writeSymref("HEAD", "refs/heads/b");
- Ref ref = db.getRef("HEAD");
+ Ref ref = db.exactRef("HEAD");
assertEquals(Ref.Storage.LOOSE, ref.getStorage());
assertTrue("is symref", ref.isSymbolic());
ref = ref.getTarget();
@@ -181,7 +181,7 @@ public class RefTest extends SampleDataRepositoryTestCase {
assertEquals(Result.FORCED, update); // internal
writeSymref("HEAD", "refs/heads/master");
- Ref ref = db.getRef("HEAD");
+ Ref ref = db.exactRef("HEAD");
assertEquals(Ref.Storage.LOOSE, ref.getStorage());
ref = ref.getTarget();
assertEquals("refs/heads/master", ref.getName());
@@ -194,13 +194,13 @@ public class RefTest extends SampleDataRepositoryTestCase {
updateRef.setNewObjectId(db.resolve("refs/heads/master"));
Result update = updateRef.update();
assertEquals(Result.NEW, update);
- Ref ref = db.getRef("ref/heads/new");
+ Ref ref = db.exactRef("ref/heads/new");
assertEquals(Storage.LOOSE, ref.getStorage());
}
@Test
public void testGetShortRef() throws IOException {
- Ref ref = db.getRef("master");
+ Ref ref = db.exactRef("refs/heads/master");
assertEquals("refs/heads/master", ref.getName());
assertEquals(db.resolve("refs/heads/master"), ref.getObjectId());
}
@@ -222,7 +222,7 @@ public class RefTest extends SampleDataRepositoryTestCase {
assertNull(db.getRefDatabase().exactRef("refs/foo/bar"));
- Ref ref = db.getRef("refs/foo/bar");
+ Ref ref = db.findRef("refs/foo/bar");
assertEquals("refs/heads/refs/foo/bar", ref.getName());
assertEquals(db.resolve("refs/heads/master"), ref.getObjectId());
}
@@ -237,7 +237,7 @@ public class RefTest extends SampleDataRepositoryTestCase {
assertEquals("refs/foo/bar", exactRef.getName());
assertEquals(masterId, exactRef.getObjectId());
- Ref ref = db.getRef("refs/foo/bar");
+ Ref ref = db.findRef("refs/foo/bar");
assertEquals("refs/foo/bar", ref.getName());
assertEquals(masterId, ref.getObjectId());
}
@@ -251,7 +251,7 @@ public class RefTest extends SampleDataRepositoryTestCase {
@Test
public void testReadLoosePackedRef() throws IOException,
InterruptedException {
- Ref ref = db.getRef("refs/heads/master");
+ Ref ref = db.exactRef("refs/heads/master");
assertEquals(Storage.PACKED, ref.getStorage());
FileOutputStream os = new FileOutputStream(new File(db.getDirectory(),
"refs/heads/master"));
@@ -259,7 +259,7 @@ public class RefTest extends SampleDataRepositoryTestCase {
os.write('\n');
os.close();
- ref = db.getRef("refs/heads/master");
+ ref = db.exactRef("refs/heads/master");
assertEquals(Storage.LOOSE, ref.getStorage());
}
@@ -271,7 +271,7 @@ public class RefTest extends SampleDataRepositoryTestCase {
*/
@Test
public void testReadSimplePackedRefSameRepo() throws IOException {
- Ref ref = db.getRef("refs/heads/master");
+ Ref ref = db.exactRef("refs/heads/master");
ObjectId pid = db.resolve("refs/heads/master^");
assertEquals(Storage.PACKED, ref.getStorage());
RefUpdate updateRef = db.updateRef("refs/heads/master");
@@ -280,19 +280,19 @@ public class RefTest extends SampleDataRepositoryTestCase {
Result update = updateRef.update();
assertEquals(Result.FORCED, update);
- ref = db.getRef("refs/heads/master");
+ ref = db.exactRef("refs/heads/master");
assertEquals(Storage.LOOSE, ref.getStorage());
}
@Test
public void testResolvedNamesBranch() throws IOException {
- Ref ref = db.getRef("a");
+ Ref ref = db.findRef("a");
assertEquals("refs/heads/a", ref.getName());
}
@Test
public void testResolvedSymRef() throws IOException {
- Ref ref = db.getRef(Constants.HEAD);
+ Ref ref = db.exactRef(Constants.HEAD);
assertEquals(Constants.HEAD, ref.getName());
assertTrue("is symbolic ref", ref.isSymbolic());
assertSame(Ref.Storage.LOOSE, ref.getStorage());
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java
index 21ef7479a2..6c90f7d44b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergeMessageFormatterTest.java
@@ -84,44 +84,44 @@ public class MergeMessageFormatterTest extends SampleDataRepositoryTestCase {
@Test
public void testOneBranch() throws IOException {
- Ref a = db.getRef("refs/heads/a");
- Ref master = db.getRef("refs/heads/master");
+ Ref a = db.exactRef("refs/heads/a");
+ Ref master = db.exactRef("refs/heads/master");
String message = formatter.format(Arrays.asList(a), master);
assertEquals("Merge branch 'a'", message);
}
@Test
public void testTwoBranches() throws IOException {
- Ref a = db.getRef("refs/heads/a");
- Ref b = db.getRef("refs/heads/b");
- Ref master = db.getRef("refs/heads/master");
+ Ref a = db.exactRef("refs/heads/a");
+ Ref b = db.exactRef("refs/heads/b");
+ Ref master = db.exactRef("refs/heads/master");
String message = formatter.format(Arrays.asList(a, b), master);
assertEquals("Merge branches 'a' and 'b'", message);
}
@Test
public void testThreeBranches() throws IOException {
- Ref c = db.getRef("refs/heads/c");
- Ref b = db.getRef("refs/heads/b");
- Ref a = db.getRef("refs/heads/a");
- Ref master = db.getRef("refs/heads/master");
+ Ref c = db.exactRef("refs/heads/c");
+ Ref b = db.exactRef("refs/heads/b");
+ Ref a = db.exactRef("refs/heads/a");
+ Ref master = db.exactRef("refs/heads/master");
String message = formatter.format(Arrays.asList(c, b, a), master);
assertEquals("Merge branches 'c', 'b' and 'a'", message);
}
@Test
public void testRemoteBranch() throws Exception {
- Ref remoteA = db.getRef("refs/remotes/origin/remote-a");
- Ref master = db.getRef("refs/heads/master");
+ Ref remoteA = db.exactRef("refs/remotes/origin/remote-a");
+ Ref master = db.exactRef("refs/heads/master");
String message = formatter.format(Arrays.asList(remoteA), master);
assertEquals("Merge remote-tracking branch 'origin/remote-a'", message);
}
@Test
public void testMixed() throws IOException {
- Ref c = db.getRef("refs/heads/c");
- Ref remoteA = db.getRef("refs/remotes/origin/remote-a");
- Ref master = db.getRef("refs/heads/master");
+ Ref c = db.exactRef("refs/heads/c");
+ Ref remoteA = db.exactRef("refs/remotes/origin/remote-a");
+ Ref master = db.exactRef("refs/heads/master");
String message = formatter.format(Arrays.asList(c, remoteA), master);
assertEquals("Merge branch 'c', remote-tracking branch 'origin/remote-a'",
message);
@@ -129,8 +129,8 @@ public class MergeMessageFormatterTest extends SampleDataRepositoryTestCase {
@Test
public void testTag() throws IOException {
- Ref tagA = db.getRef("refs/tags/A");
- Ref master = db.getRef("refs/heads/master");
+ Ref tagA = db.exactRef("refs/tags/A");
+ Ref master = db.exactRef("refs/heads/master");
String message = formatter.format(Arrays.asList(tagA), master);
assertEquals("Merge tag 'A'", message);
}
@@ -141,7 +141,7 @@ public class MergeMessageFormatterTest extends SampleDataRepositoryTestCase {
.fromString("6db9c2ebf75590eef973081736730a9ea169a0c4");
Ref commit = new ObjectIdRef.Unpeeled(Storage.LOOSE,
objectId.getName(), objectId);
- Ref master = db.getRef("refs/heads/master");
+ Ref master = db.exactRef("refs/heads/master");
String message = formatter.format(Arrays.asList(commit), master);
assertEquals("Merge commit '6db9c2ebf75590eef973081736730a9ea169a0c4'",
message);
@@ -154,7 +154,7 @@ public class MergeMessageFormatterTest extends SampleDataRepositoryTestCase {
.fromString("6db9c2ebf75590eef973081736730a9ea169a0c4");
Ref remoteBranch = new ObjectIdRef.Unpeeled(Storage.LOOSE, name,
objectId);
- Ref master = db.getRef("refs/heads/master");
+ Ref master = db.exactRef("refs/heads/master");
String message = formatter.format(Arrays.asList(remoteBranch), master);
assertEquals("Merge branch 'test' of http://egit.eclipse.org/jgit.git",
message);
@@ -162,16 +162,16 @@ public class MergeMessageFormatterTest extends SampleDataRepositoryTestCase {
@Test
public void testIntoOtherThanMaster() throws IOException {
- Ref a = db.getRef("refs/heads/a");
- Ref b = db.getRef("refs/heads/b");
+ Ref a = db.exactRef("refs/heads/a");
+ Ref b = db.exactRef("refs/heads/b");
String message = formatter.format(Arrays.asList(a), b);
assertEquals("Merge branch 'a' into b", message);
}
@Test
public void testIntoHeadOtherThanMaster() throws IOException {
- Ref a = db.getRef("refs/heads/a");
- Ref b = db.getRef("refs/heads/b");
+ Ref a = db.exactRef("refs/heads/a");
+ Ref b = db.exactRef("refs/heads/b");
SymbolicRef head = new SymbolicRef("HEAD", b);
String message = formatter.format(Arrays.asList(a), head);
assertEquals("Merge branch 'a' into b", message);
@@ -179,8 +179,8 @@ public class MergeMessageFormatterTest extends SampleDataRepositoryTestCase {
@Test
public void testIntoSymbolicRefHeadPointingToMaster() throws IOException {
- Ref a = db.getRef("refs/heads/a");
- Ref master = db.getRef("refs/heads/master");
+ Ref a = db.exactRef("refs/heads/a");
+ Ref master = db.exactRef("refs/heads/master");
SymbolicRef head = new SymbolicRef("HEAD", master);
String message = formatter.format(Arrays.asList(a), head);
assertEquals("Merge branch 'a'", message);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java
index cd6a4bea2e..674619f0de 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/ResolveMergerTest.java
@@ -630,7 +630,7 @@ public class ResolveMergerTest extends RepositoryTestCase {
// ResolveMerge
try {
MergeResult mergeResult = git.merge().setStrategy(strategy)
- .include(git.getRepository().getRef("refs/heads/side"))
+ .include(git.getRepository().exactRef("refs/heads/side"))
.call();
assertEquals(MergeStrategy.RECURSIVE, strategy);
assertEquals(MergeResult.MergeStatus.MERGED,
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SquashMessageFormatterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SquashMessageFormatterTest.java
index b7b2291429..cddbbd528b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SquashMessageFormatterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/SquashMessageFormatterTest.java
@@ -76,7 +76,7 @@ public class SquashMessageFormatterTest extends SampleDataRepositoryTestCase {
Git git = new Git(db);
revCommit = git.commit().setMessage("squash_me").call();
- Ref master = db.getRef("refs/heads/master");
+ Ref master = db.exactRef("refs/heads/master");
String message = msgFormatter.format(Arrays.asList(revCommit), master);
assertEquals(
"Squashed commit of the following:\n\ncommit "
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java
new file mode 100644
index 0000000000..782e414b62
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/AtomicPushTest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
+import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
+import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class AtomicPushTest {
+ private URIish uri;
+ private TestProtocol<Object> testProtocol;
+ private Object ctx = new Object();
+ private InMemoryRepository server;
+ private InMemoryRepository client;
+ private ObjectId obj1;
+ private ObjectId obj2;
+
+ @Before
+ public void setUp() throws Exception {
+ server = newRepo("server");
+ client = newRepo("client");
+ testProtocol = new TestProtocol<>(
+ null,
+ new ReceivePackFactory<Object>() {
+ @Override
+ public ReceivePack create(Object req, Repository db)
+ throws ServiceNotEnabledException,
+ ServiceNotAuthorizedException {
+ return new ReceivePack(db);
+ }
+ });
+ uri = testProtocol.register(ctx, server);
+
+ try (ObjectInserter ins = client.newObjectInserter()) {
+ obj1 = ins.insert(Constants.OBJ_BLOB, Constants.encode("test"));
+ obj2 = ins.insert(Constants.OBJ_BLOB, Constants.encode("file"));
+ ins.flush();
+ }
+ }
+
+ @After
+ public void tearDown() {
+ Transport.unregister(testProtocol);
+ }
+
+ private static InMemoryRepository newRepo(String name) {
+ return new InMemoryRepository(new DfsRepositoryDescription(name));
+ }
+
+ @Test
+ public void pushNonAtomic() throws Exception {
+ PushResult r;
+ server.setPerformsAtomicTransactions(false);
+ Transport tn = testProtocol.open(uri, client, "server");
+ try {
+ tn.setPushAtomic(false);
+ r = tn.push(NullProgressMonitor.INSTANCE, commands());
+ } finally {
+ tn.close();
+ }
+
+ RemoteRefUpdate one = r.getRemoteUpdate("refs/heads/one");
+ RemoteRefUpdate two = r.getRemoteUpdate("refs/heads/two");
+ assertSame(RemoteRefUpdate.Status.OK, one.getStatus());
+ assertSame(
+ RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED,
+ two.getStatus());
+ }
+
+ @Test
+ public void pushAtomicClientGivesUpEarly() throws Exception {
+ PushResult r;
+ Transport tn = testProtocol.open(uri, client, "server");
+ try {
+ tn.setPushAtomic(true);
+ r = tn.push(NullProgressMonitor.INSTANCE, commands());
+ } finally {
+ tn.close();
+ }
+
+ RemoteRefUpdate one = r.getRemoteUpdate("refs/heads/one");
+ RemoteRefUpdate two = r.getRemoteUpdate("refs/heads/two");
+ assertSame(
+ RemoteRefUpdate.Status.REJECTED_OTHER_REASON,
+ one.getStatus());
+ assertSame(
+ RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED,
+ two.getStatus());
+ assertEquals(JGitText.get().transactionAborted, one.getMessage());
+ }
+
+ @Test
+ public void pushAtomicDisabled() throws Exception {
+ List<RemoteRefUpdate> cmds = new ArrayList<>();
+ cmds.add(new RemoteRefUpdate(
+ null, null,
+ obj1, "refs/heads/one",
+ true /* force update */,
+ null /* no local tracking ref */,
+ ObjectId.zeroId()));
+ cmds.add(new RemoteRefUpdate(
+ null, null,
+ obj2, "refs/heads/two",
+ true /* force update */,
+ null /* no local tracking ref */,
+ ObjectId.zeroId()));
+
+ server.setPerformsAtomicTransactions(false);
+ Transport tn = testProtocol.open(uri, client, "server");
+ try {
+ tn.setPushAtomic(true);
+ tn.push(NullProgressMonitor.INSTANCE, cmds);
+ fail("did not throw TransportException");
+ } catch (TransportException e) {
+ assertEquals(
+ uri + ": " + JGitText.get().atomicPushNotSupported,
+ e.getMessage());
+ } finally {
+ tn.close();
+ }
+ }
+
+ private List<RemoteRefUpdate> commands() throws IOException {
+ List<RemoteRefUpdate> cmds = new ArrayList<>();
+ cmds.add(new RemoteRefUpdate(
+ null, null,
+ obj1, "refs/heads/one",
+ true /* force update */,
+ null /* no local tracking ref */,
+ ObjectId.zeroId()));
+ cmds.add(new RemoteRefUpdate(
+ null, null,
+ obj2, "refs/heads/two",
+ true /* force update */,
+ null /* no local tracking ref */,
+ obj1));
+ return cmds;
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushConnectionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushConnectionTest.java
new file mode 100644
index 0000000000..a3b4134aea
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/PushConnectionTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2015, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import static org.eclipse.jgit.transport.RemoteRefUpdate.Status.REJECTED_OTHER_REASON;
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
+import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
+import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PushConnectionTest {
+ private URIish uri;
+ private TestProtocol<Object> testProtocol;
+ private Object ctx = new Object();
+ private InMemoryRepository server;
+ private InMemoryRepository client;
+ private ObjectId obj1;
+ private ObjectId obj2;
+ private ObjectId obj3;
+ private String refName = "refs/tags/blob";
+
+ @Before
+ public void setUp() throws Exception {
+ server = newRepo("server");
+ client = newRepo("client");
+ testProtocol = new TestProtocol<>(
+ null,
+ new ReceivePackFactory<Object>() {
+ @Override
+ public ReceivePack create(Object req, Repository db)
+ throws ServiceNotEnabledException,
+ ServiceNotAuthorizedException {
+ return new ReceivePack(db);
+ }
+ });
+ uri = testProtocol.register(ctx, server);
+
+ try (ObjectInserter ins = server.newObjectInserter()) {
+ obj1 = ins.insert(Constants.OBJ_BLOB, Constants.encode("test"));
+ obj3 = ins.insert(Constants.OBJ_BLOB, Constants.encode("not"));
+ ins.flush();
+
+ RefUpdate u = server.updateRef(refName);
+ u.setNewObjectId(obj1);
+ assertEquals(RefUpdate.Result.NEW, u.update());
+ }
+
+ try (ObjectInserter ins = client.newObjectInserter()) {
+ obj2 = ins.insert(Constants.OBJ_BLOB, Constants.encode("file"));
+ ins.flush();
+ }
+ }
+
+ @After
+ public void tearDown() {
+ Transport.unregister(testProtocol);
+ }
+
+ private static InMemoryRepository newRepo(String name) {
+ return new InMemoryRepository(new DfsRepositoryDescription(name));
+ }
+
+ @Test
+ public void testWrongOldIdDoesNotReplace() throws IOException {
+ RemoteRefUpdate rru = new RemoteRefUpdate(null, null, obj2, refName,
+ false, null, obj3);
+
+ Map<String, RemoteRefUpdate> updates = new HashMap<>();
+ updates.put(rru.getRemoteName(), rru);
+
+ Transport tn = testProtocol.open(uri, client, "server");
+ try {
+ PushConnection connection = tn.openPush();
+ try {
+ connection.push(NullProgressMonitor.INSTANCE, updates);
+ } finally {
+ connection.close();
+ }
+ } finally {
+ tn.close();
+ }
+
+ assertEquals(REJECTED_OTHER_REASON, rru.getStatus());
+ assertEquals("invalid old id sent", rru.getMessage());
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java
index 7f865263d6..31b64187c8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TestProtocolTest.java
@@ -125,7 +125,7 @@ public class TestProtocolTest {
.setRefSpecs(HEADS)
.call();
assertEquals(master,
- local.getRepository().getRef("master").getObjectId());
+ local.getRepository().exactRef("refs/heads/master").getObjectId());
}
}
@@ -142,7 +142,7 @@ public class TestProtocolTest {
.setRefSpecs(HEADS)
.call();
assertEquals(master,
- remote.getRepository().getRef("master").getObjectId());
+ remote.getRepository().exactRef("refs/heads/master").getObjectId());
}
}
@@ -177,7 +177,7 @@ public class TestProtocolTest {
// Expected.
}
assertEquals(1, rejected.get());
- assertNull(local.getRepository().getRef("master"));
+ assertNull(local.getRepository().exactRef("refs/heads/master"));
git.fetch()
.setRemote(user2Uri.toString())
@@ -185,7 +185,7 @@ public class TestProtocolTest {
.call();
assertEquals(1, rejected.get());
assertEquals(master,
- local.getRepository().getRef("master").getObjectId());
+ local.getRepository().exactRef("refs/heads/master").getObjectId());
}
}
@@ -222,7 +222,7 @@ public class TestProtocolTest {
JGitText.get().pushNotPermitted));
}
assertEquals(1, rejected.get());
- assertNull(remote.getRepository().getRef("master"));
+ assertNull(remote.getRepository().exactRef("refs/heads/master"));
git.push()
.setRemote(user2Uri.toString())
@@ -230,7 +230,7 @@ public class TestProtocolTest {
.call();
assertEquals(1, rejected.get());
assertEquals(master,
- remote.getRepository().getRef("master").getObjectId());
+ remote.getRepository().exactRef("refs/heads/master").getObjectId());
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java
index 745c322013..76eb18afdf 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/URIishTest.java
@@ -928,4 +928,19 @@ public class URIishTest {
}
}
}
+
+ @Test
+ public void testStringConstructor() throws Exception {
+ String str = "http://example.com/";
+ URIish u = new URIish(str);
+ assertEquals("example.com", u.getHost());
+ assertEquals("/", u.getPath());
+ assertEquals(str, u.toString());
+
+ str = "http://example.com";
+ u = new URIish(str);
+ assertEquals("example.com", u.getHost());
+ assertEquals("", u.getPath());
+ assertEquals(str, u.toString());
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java
index 90d78e4b62..ac2bfd12f9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/WalkEncryptionTest.java
@@ -43,6 +43,20 @@
package org.eclipse.jgit.transport;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.UTF_8;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.cryptoCipherListPBE;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.cryptoCipherListTrans;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.folderDelete;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.permitLongTests;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.policySetup;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.product;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.proxySetup;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.publicAddress;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.reportPolicy;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.securityProviderName;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.textWrite;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.transferStream;
+import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.verifyFileContent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -59,7 +73,10 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
+import java.net.SocketTimeoutException;
import java.net.URL;
+import java.net.URLConnection;
+import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.security.GeneralSecurityException;
@@ -94,8 +111,6 @@ import org.junit.runners.Suite;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import static org.eclipse.jgit.transport.WalkEncryptionTest.Util.*;
-
/**
* Amazon S3 encryption pipeline test.
*
@@ -401,14 +416,22 @@ public class WalkEncryptionTest {
* @throws Exception
*/
static String publicAddress() throws Exception {
- String service = "http://checkip.amazonaws.com";
- URL url = new URL(service);
- BufferedReader reader = new BufferedReader(
- new InputStreamReader(url.openStream()));
try {
- return reader.readLine();
- } finally {
- reader.close();
+ String service = "http://checkip.amazonaws.com";
+ URL url = new URL(service);
+ URLConnection c = url.openConnection();
+ c.setConnectTimeout(500);
+ c.setReadTimeout(500);
+ BufferedReader reader = new BufferedReader(
+ new InputStreamReader(c.getInputStream()));
+ try {
+ return reader.readLine();
+ } finally {
+ reader.close();
+ }
+ } catch (UnknownHostException | SocketTimeoutException e) {
+ return "Can't reach http://checkip.amazonaws.com to"
+ + " determine public address";
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/CanonicalTreeParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/CanonicalTreeParserTest.java
index 52da69ef52..f5e97c2dc2 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/CanonicalTreeParserTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/CanonicalTreeParserTest.java
@@ -43,6 +43,8 @@
package org.eclipse.jgit.treewalk;
+import static org.eclipse.jgit.lib.FileMode.REGULAR_FILE;
+import static org.eclipse.jgit.lib.FileMode.SYMLINK;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
@@ -50,9 +52,11 @@ import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream;
+import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.TreeFormatter;
import org.eclipse.jgit.util.RawParseUtils;
import org.junit.Before;
import org.junit.Test;
@@ -369,4 +373,41 @@ public class CanonicalTreeParserTest {
assertEquals(name, RawParseUtils.decode(Constants.CHARSET, ctp.path,
ctp.pathOffset, ctp.pathLen));
}
+
+ @Test
+ public void testFindAttributesWhenFirst() throws CorruptObjectException {
+ TreeFormatter tree = new TreeFormatter();
+ tree.append(".gitattributes", REGULAR_FILE, hash_a);
+ ctp.reset(tree.toByteArray());
+
+ assertTrue(ctp.findFile(".gitattributes"));
+ assertEquals(REGULAR_FILE.getBits(), ctp.getEntryRawMode());
+ assertEquals(".gitattributes", ctp.getEntryPathString());
+ assertEquals(hash_a, ctp.getEntryObjectId());
+ }
+
+ @Test
+ public void testFindAttributesWhenSecond() throws CorruptObjectException {
+ TreeFormatter tree = new TreeFormatter();
+ tree.append(".config", SYMLINK, hash_a);
+ tree.append(".gitattributes", REGULAR_FILE, hash_foo);
+ ctp.reset(tree.toByteArray());
+
+ assertTrue(ctp.findFile(".gitattributes"));
+ assertEquals(REGULAR_FILE.getBits(), ctp.getEntryRawMode());
+ assertEquals(".gitattributes", ctp.getEntryPathString());
+ assertEquals(hash_foo, ctp.getEntryObjectId());
+ }
+
+ @Test
+ public void testFindAttributesWhenMissing() throws CorruptObjectException {
+ TreeFormatter tree = new TreeFormatter();
+ tree.append("src", REGULAR_FILE, hash_a);
+ tree.append("zoo", REGULAR_FILE, hash_foo);
+ ctp.reset(tree.toByteArray());
+
+ assertFalse(ctp.findFile(".gitattributes"));
+ assertEquals(11, ctp.idOffset()); // Did not walk the entire tree.
+ assertEquals("src", ctp.getEntryPathString());
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorJava7Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorJava7Test.java
index 88754109f7..429f35ea4b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorJava7Test.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorJava7Test.java
@@ -67,6 +67,7 @@ import org.junit.Test;
public class FileTreeIteratorJava7Test extends RepositoryTestCase {
@Test
public void testFileModeSymLinkIsNotATree() throws IOException {
+ org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
FS fs = db.getFS();
// mål = target in swedish, just to get som unicode in here
writeTrashFile("mål/data", "targetdata");
@@ -163,6 +164,7 @@ public class FileTreeIteratorJava7Test extends RepositoryTestCase {
*/
@Test
public void testSymlinkActuallyModified() throws Exception {
+ org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
final String NORMALIZED = "target";
final byte[] NORMALIZED_BYTES = Constants.encode(NORMALIZED);
try (ObjectInserter oi = db.newObjectInserter()) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/TreeWalkJava7Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/TreeWalkJava7Test.java
index bb1f2a639b..1328b38e63 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/TreeWalkJava7Test.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/TreeWalkJava7Test.java
@@ -55,6 +55,7 @@ import org.junit.Test;
public class TreeWalkJava7Test extends RepositoryTestCase {
@Test
public void testSymlinkToDirNotRecursingViaSymlink() throws Exception {
+ org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
FS fs = db.getFS();
assertTrue(fs.supportsSymlinks());
writeTrashFile("target/data", "targetdata");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSJava7Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSJava7Test.java
index e6a244e142..53b6fecfd8 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSJava7Test.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSJava7Test.java
@@ -57,6 +57,7 @@ import java.util.Set;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.junit.After;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
@@ -87,6 +88,7 @@ public class FSJava7Test {
*/
@Test
public void testSymlinkAttributes() throws IOException, InterruptedException {
+ Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
FS fs = FS.DETECTED;
File link = new File(trash, "ä");
File target = new File(trash, "Ã¥");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtils7Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtils7Test.java
index 4625f30683..cc1fdc21b9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtils7Test.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FileUtils7Test.java
@@ -73,6 +73,7 @@ public class FileUtils7Test {
@Test
public void testDeleteSymlinkToDirectoryDoesNotDeleteTarget()
throws IOException {
+ org.junit.Assume.assumeTrue(FS.DETECTED.supportsSymlinks());
FS fs = FS.DETECTED;
File dir = new File(trash, "dir");
File file = new File(dir, "file");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java
index 8aa14c5218..e07076e322 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/HookTest.java
@@ -92,11 +92,9 @@ public class HookTest extends RepositoryTestCase {
fail("expected commit-msg hook to abort commit");
} catch (AbortedByHookException e) {
assertEquals("unexpected error message from commit-msg hook",
- "Rejected by \"commit-msg\" hook.\nstderr"
- + System.lineSeparator(),
+ "Rejected by \"commit-msg\" hook.\nstderr\n",
e.getMessage());
- assertEquals("unexpected output from commit-msg hook",
- "test" + System.lineSeparator(),
+ assertEquals("unexpected output from commit-msg hook", "test\n",
out.toString());
}
}
@@ -114,7 +112,7 @@ public class HookTest extends RepositoryTestCase {
ByteArrayOutputStream out = new ByteArrayOutputStream();
git.commit().setMessage("commit")
.setHookOutputStream(new PrintStream(out)).call();
- assertEquals(".git/COMMIT_EDITMSG" + System.lineSeparator(),
+ assertEquals(".git/COMMIT_EDITMSG\n",
out.toString("UTF-8"));
}
@@ -147,11 +145,10 @@ public class HookTest extends RepositoryTestCase {
new String[] {
"arg1", "arg2" },
new PrintStream(out), new PrintStream(err), "stdin");
- assertEquals("unexpected hook output", "test arg1 arg2"
- + System.lineSeparator() + "stdin" + System.lineSeparator(),
+
+ assertEquals("unexpected hook output", "test arg1 arg2\nstdin\n",
out.toString("UTF-8"));
- assertEquals("unexpected output on stderr stream",
- "stderr" + System.lineSeparator(),
+ assertEquals("unexpected output on stderr stream", "stderr\n",
err.toString("UTF-8"));
assertEquals("unexpected exit code", 0, res.getExitCode());
assertEquals("unexpected process status", ProcessResult.Status.OK,
@@ -175,11 +172,9 @@ public class HookTest extends RepositoryTestCase {
fail("expected pre-commit hook to abort commit");
} catch (AbortedByHookException e) {
assertEquals("unexpected error message from pre-commit hook",
- "Rejected by \"pre-commit\" hook.\nstderr"
- + System.lineSeparator(),
+ "Rejected by \"pre-commit\" hook.\nstderr\n",
e.getMessage());
- assertEquals("unexpected output from pre-commit hook",
- "test" + System.lineSeparator(),
+ assertEquals("unexpected output from pre-commit hook", "test\n",
out.toString());
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java
index 82beab2dc8..7c0985ef42 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RunExternalScriptTest.java
@@ -52,16 +52,17 @@ import java.io.IOException;
import java.io.InputStream;
import org.eclipse.jgit.junit.JGitTestUtil;
+import org.eclipse.jgit.util.FS.ExecutionResult;
import org.junit.Before;
import org.junit.Test;
public class RunExternalScriptTest {
+ private static final String LF = "\n";
+
private ByteArrayOutputStream out;
private ByteArrayOutputStream err;
- private String sep = System.getProperty("line.separator");
-
@Before
public void setUp() throws Exception {
out = new ByteArrayOutputStream();
@@ -73,7 +74,7 @@ public class RunExternalScriptTest {
String inputStr = "a\nb\rc\r\nd";
File script = writeTempFile("cat -");
int rc = FS.DETECTED.runProcess(
- new ProcessBuilder("/bin/sh", script.getPath()), out, err,
+ new ProcessBuilder("sh", script.getPath()), out, err,
new ByteArrayInputStream(inputStr.getBytes()));
assertEquals(0, rc);
assertEquals(inputStr, new String(out.toByteArray()));
@@ -84,7 +85,7 @@ public class RunExternalScriptTest {
public void testCopyNullStdIn() throws IOException, InterruptedException {
File script = writeTempFile("cat -");
int rc = FS.DETECTED.runProcess(
- new ProcessBuilder("/bin/sh", script.getPath()), out, err,
+ new ProcessBuilder("sh", script.getPath()), out, err,
(InputStream) null);
assertEquals(0, rc);
assertEquals("", new String(out.toByteArray()));
@@ -94,7 +95,8 @@ public class RunExternalScriptTest {
@Test
public void testArguments() throws IOException, InterruptedException {
File script = writeTempFile("echo $#,$1,$2,$3,$4,$5,$6");
- int rc = FS.DETECTED.runProcess(new ProcessBuilder("/bin/bash",
+ int rc = FS.DETECTED.runProcess(
+ new ProcessBuilder("sh",
script.getPath(), "a", "b", "c"), out, err, (InputStream) null);
assertEquals(0, rc);
assertEquals("3,a,b,c,,,\n", new String(out.toByteArray()));
@@ -105,7 +107,7 @@ public class RunExternalScriptTest {
public void testRc() throws IOException, InterruptedException {
File script = writeTempFile("exit 3");
int rc = FS.DETECTED.runProcess(
- new ProcessBuilder("/bin/sh", script.getPath(), "a", "b", "c"),
+ new ProcessBuilder("sh", script.getPath(), "a", "b", "c"),
out, err, (InputStream) null);
assertEquals(3, rc);
assertEquals("", new String(out.toByteArray()));
@@ -116,7 +118,7 @@ public class RunExternalScriptTest {
public void testNullStdout() throws IOException, InterruptedException {
File script = writeTempFile("echo hi");
int rc = FS.DETECTED.runProcess(
- new ProcessBuilder("/bin/sh", script.getPath()), null, err,
+ new ProcessBuilder("sh", script.getPath()), null, err,
(InputStream) null);
assertEquals(0, rc);
assertEquals("", new String(out.toByteArray()));
@@ -127,11 +129,11 @@ public class RunExternalScriptTest {
public void testStdErr() throws IOException, InterruptedException {
File script = writeTempFile("echo hi >&2");
int rc = FS.DETECTED.runProcess(
- new ProcessBuilder("/bin/sh", script.getPath()), null, err,
+ new ProcessBuilder("sh", script.getPath()), null, err,
(InputStream) null);
assertEquals(0, rc);
assertEquals("", new String(out.toByteArray()));
- assertEquals("hi" + sep, new String(err.toByteArray()));
+ assertEquals("hi" + LF, new String(err.toByteArray()));
}
@Test
@@ -139,11 +141,11 @@ public class RunExternalScriptTest {
String inputStr = "a\nb\rc\r\nd";
File script = writeTempFile("echo $#,$1,$2,$3,$4,$5,$6 >&2 ; cat -; exit 5");
int rc = FS.DETECTED.runProcess(
- new ProcessBuilder("/bin/sh", script.getPath(), "a", "b", "c"),
+ new ProcessBuilder("sh", script.getPath(), "a", "b", "c"),
out, err, new ByteArrayInputStream(inputStr.getBytes()));
assertEquals(5, rc);
assertEquals(inputStr, new String(out.toByteArray()));
- assertEquals("3,a,b,c,,," + sep, new String(err.toByteArray()));
+ assertEquals("3,a,b,c,,," + LF, new String(err.toByteArray()));
}
@Test(expected = IOException.class)
@@ -158,11 +160,50 @@ public class RunExternalScriptTest {
public void testWrongScript() throws IOException, InterruptedException {
File script = writeTempFile("cat-foo -");
int rc = FS.DETECTED.runProcess(
- new ProcessBuilder("/bin/sh", script.getPath(), "a", "b", "c"),
+ new ProcessBuilder("sh", script.getPath(), "a", "b", "c"),
out, err, (InputStream) null);
assertEquals(127, rc);
}
+ @Test
+ public void testCopyStdInExecute()
+ throws IOException, InterruptedException {
+ String inputStr = "a\nb\rc\r\nd";
+ File script = writeTempFile("cat -");
+ ProcessBuilder pb = new ProcessBuilder("sh", script.getPath());
+ ExecutionResult res = FS.DETECTED.execute(pb,
+ new ByteArrayInputStream(inputStr.getBytes()));
+ assertEquals(0, res.getRc());
+ assertEquals(inputStr, new String(res.getStdout().toByteArray()));
+ assertEquals("", new String(res.getStderr().toByteArray()));
+ }
+
+ @Test
+ public void testStdErrExecute() throws IOException, InterruptedException {
+ File script = writeTempFile("echo hi >&2");
+ ProcessBuilder pb = new ProcessBuilder("sh", script.getPath());
+ ExecutionResult res = FS.DETECTED.execute(pb, null);
+ assertEquals(0, res.getRc());
+ assertEquals("", new String(res.getStdout().toByteArray()));
+ assertEquals("hi" + LF, new String(res.getStderr().toByteArray()));
+ }
+
+ @Test
+ public void testAllTogetherBinExecute()
+ throws IOException, InterruptedException {
+ String inputStr = "a\nb\rc\r\nd";
+ File script = writeTempFile(
+ "echo $#,$1,$2,$3,$4,$5,$6 >&2 ; cat -; exit 5");
+ ProcessBuilder pb = new ProcessBuilder("sh", script.getPath(), "a",
+ "b", "c");
+ ExecutionResult res = FS.DETECTED.execute(pb,
+ new ByteArrayInputStream(inputStr.getBytes()));
+ assertEquals(5, res.getRc());
+ assertEquals(inputStr, new String(res.getStdout().toByteArray()));
+ assertEquals("3,a,b,c,,," + LF,
+ new String(res.getStderr().toByteArray()));
+ }
+
private File writeTempFile(String body) throws IOException {
File f = File.createTempFile("RunProcessTestScript_", "");
JGitTestUtil.write(f, body);
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index ed330b1517..b2a8f677f3 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -1,5 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<component id="org.eclipse.jgit" version="2">
+ <resource path="src/org/eclipse/jgit/attributes/AttributesNode.java" type="org.eclipse.jgit.attributes.AttributesNode">
+ <filter comment="attributes weren't really usable in earlier versions" id="338792546">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.attributes.AttributesNode"/>
+ <message_argument value="getAttributes(String, boolean, Map&lt;String,Attribute&gt;)"/>
+ </message_arguments>
+ </filter>
+ </resource>
<resource path="src/org/eclipse/jgit/lib/BitmapIndex.java" type="org.eclipse.jgit.lib.BitmapIndex$BitmapBuilder">
<filter comment="interface is implemented by extenders but not clients of the API" id="403804204">
<message_arguments>
@@ -14,6 +22,14 @@
</message_arguments>
</filter>
</resource>
+ <resource path="src/org/eclipse/jgit/lib/Repository.java" type="org.eclipse.jgit.lib.Repository">
+ <filter comment="Only implementors of Repository are affected. That should be allowed" id="336695337">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lib.Repository"/>
+ <message_argument value="createAttributesNodeProvider()"/>
+ </message_arguments>
+ </filter>
+ </resource>
<resource path="src/org/eclipse/jgit/transport/PushCertificate.java" type="org.eclipse.jgit.transport.PushCertificate">
<filter comment="PushCertificate wasn't really usable in 4.0" id="338722907">
<message_arguments>
@@ -35,6 +51,20 @@
</message_arguments>
</filter>
</resource>
+ <resource path="src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java" type="org.eclipse.jgit.treewalk.WorkingTreeIterator">
+ <filter comment="attributes weren't really usable in earlier versions" id="338792546">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.treewalk.WorkingTreeIterator"/>
+ <message_argument value="getGlobalAttributesNode()"/>
+ </message_arguments>
+ </filter>
+ <filter comment="attributes weren't really usable in earlier versions" id="338792546">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.treewalk.WorkingTreeIterator"/>
+ <message_argument value="getInfoAttributesNode()"/>
+ </message_arguments>
+ </filter>
+ </resource>
<resource path="src/org/eclipse/jgit/util/FileUtils.java" type="org.eclipse.jgit.util.FileUtils">
<filter id="338792546">
<message_arguments>
diff --git a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
index 195987db64..45d6d2c4c0 100644
--- a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
@@ -2,7 +2,7 @@ eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=enabled
org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jgit.annotations.NonNull
-org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jgit.annotations.NonByDefault
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jgit.annotations.NonNullByDefault
org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jgit.annotations.Nullable
org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index b6899b2801..2a953b559d 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -6,7 +6,8 @@ Bundle-Version: 4.2.0.qualifier
Bundle-Localization: plugin
Bundle-Vendor: %provider_name
Bundle-ActivationPolicy: lazy
-Export-Package: org.eclipse.jgit.api;version="4.2.0";
+Export-Package: org.eclipse.jgit.annotations;version="4.2.0",
+ org.eclipse.jgit.api;version="4.2.0";
uses:="org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.diff,
@@ -20,9 +21,7 @@ Export-Package: org.eclipse.jgit.api;version="4.2.0";
org.eclipse.jgit.submodule,
org.eclipse.jgit.transport,
org.eclipse.jgit.merge",
- org.eclipse.jgit.api.errors;version="4.2.0";
- uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.errors",
+ org.eclipse.jgit.api.errors;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
org.eclipse.jgit.attributes;version="4.2.0",
org.eclipse.jgit.blame;version="4.2.0";
uses:="org.eclipse.jgit.lib,
@@ -47,8 +46,7 @@ Export-Package: org.eclipse.jgit.api;version="4.2.0";
org.eclipse.jgit.internal.storage.pack,
org.eclipse.jgit.transport,
org.eclipse.jgit.dircache",
- org.eclipse.jgit.events;version="4.2.0";
- uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.events;version="4.2.0";uses:="org.eclipse.jgit.lib",
org.eclipse.jgit.fnmatch;version="4.2.0",
org.eclipse.jgit.gitrepo;version="4.2.0";
uses:="org.eclipse.jgit.api,
@@ -57,14 +55,11 @@ Export-Package: org.eclipse.jgit.api;version="4.2.0";
org.xml.sax.helpers,
org.xml.sax",
org.eclipse.jgit.gitrepo.internal;version="4.2.0";x-internal:=true,
- org.eclipse.jgit.hooks;version="4.2.0";
- uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.hooks;version="4.2.0";uses:="org.eclipse.jgit.lib",
org.eclipse.jgit.ignore;version="4.2.0",
org.eclipse.jgit.ignore.internal;version="4.2.0";x-friends:="org.eclipse.jgit.test",
org.eclipse.jgit.internal;version="4.2.0";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
- org.eclipse.jgit.internal.storage.dfs;version="4.2.0";
- x-friends:="org.eclipse.jgit.test,
- org.eclipse.jgit.http.server",
+ org.eclipse.jgit.internal.storage.dfs;version="4.2.0";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.server",
org.eclipse.jgit.internal.storage.file;version="4.2.0";
x-friends:="org.eclipse.jgit.test,
org.eclipse.jgit.junit,
@@ -96,31 +91,18 @@ Export-Package: org.eclipse.jgit.api;version="4.2.0";
org.eclipse.jgit.treewalk,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.merge",
- org.eclipse.jgit.patch;version="4.2.0";
- uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.diff",
- org.eclipse.jgit.revplot;version="4.2.0";
- uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.revwalk",
+ org.eclipse.jgit.patch;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
+ org.eclipse.jgit.revplot;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
org.eclipse.jgit.revwalk;version="4.2.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.diff,
org.eclipse.jgit.revwalk.filter",
- org.eclipse.jgit.revwalk.filter;version="4.2.0";
- uses:="org.eclipse.jgit.revwalk,
- org.eclipse.jgit.lib,
- org.eclipse.jgit.util",
- org.eclipse.jgit.storage.file;version="4.2.0";
- uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.util",
- org.eclipse.jgit.storage.pack;version="4.2.0";
- uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.submodule;version="4.2.0";
- uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.treewalk.filter,
- org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.revwalk.filter;version="4.2.0";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.file;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.pack;version="4.2.0";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.submodule;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
org.eclipse.jgit.transport;version="4.2.0";
uses:="org.eclipse.jgit.transport.resolver,
org.eclipse.jgit.revwalk,
@@ -133,11 +115,8 @@ Export-Package: org.eclipse.jgit.api;version="4.2.0";
org.eclipse.jgit.transport.http,
org.eclipse.jgit.errors,
org.eclipse.jgit.storage.pack",
- org.eclipse.jgit.transport.http;version="4.2.0";
- uses:="javax.net.ssl",
- org.eclipse.jgit.transport.resolver;version="4.2.0";
- uses:="org.eclipse.jgit.lib,
- org.eclipse.jgit.transport",
+ org.eclipse.jgit.transport.http;version="4.2.0";uses:="javax.net.ssl",
+ org.eclipse.jgit.transport.resolver;version="4.2.0";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
org.eclipse.jgit.treewalk;version="4.2.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
@@ -145,8 +124,7 @@ Export-Package: org.eclipse.jgit.api;version="4.2.0";
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.util,
org.eclipse.jgit.dircache",
- org.eclipse.jgit.treewalk.filter;version="4.2.0";
- uses:="org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.treewalk.filter;version="4.2.0";uses:="org.eclipse.jgit.treewalk",
org.eclipse.jgit.util;version="4.2.0";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.transport.http,
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 51e44fd778..0e9b0b59e6 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -20,6 +20,7 @@ argumentIsNotAValidCommentString=Invalid comment: {0}
atLeastOnePathIsRequired=At least one path is required.
atLeastOnePatternIsRequired=At least one pattern is required.
atLeastTwoFiltersNeeded=At least two filters needed.
+atomicPushNotSupported=Atomic push not supported.
authenticationNotSupported=authentication not supported
badBase64InputCharacterAt=Bad Base64 input character at {0} : {1} (decimal)
badEntryDelimiter=Bad entry delimiter
@@ -45,6 +46,7 @@ cannotBeCombined=Cannot be combined.
cannotBeRecursiveWhenTreesAreIncluded=TreeWalk shouldn't be recursive when tree objects are included.
cannotChangeActionOnComment=Cannot change action on comment line in git-rebase-todo file, old action: {0}, new action: {1}.
cannotChangeToComment=Cannot change a non-comment line to a comment line.
+cannotCheckoutFromUnbornBranch=Cannot checkout from unborn branch
cannotCheckoutOursSwitchBranch=Checking out ours/theirs is only possible when checking out index, not when switching branches.
cannotCombineSquashWithNoff=Cannot combine --squash with --no-ff.
cannotCombineTreeFilterWithRevFilter=Cannot combine TreeFilter {0} with RevFilter {1}.
@@ -87,6 +89,7 @@ cannotReadBlob=Cannot read blob {0}
cannotReadCommit=Cannot read commit {0}
cannotReadFile=Cannot read file {0}
cannotReadHEAD=cannot read HEAD: {0} {1}
+cannotReadIndex=The index file {0} exists but cannot be read
cannotReadObject=Cannot read object
cannotReadObjectsPath=Cannot read {0}/{1}: {2}
cannotReadTree=Cannot read tree {0}
@@ -279,6 +282,8 @@ fileCannotBeDeleted=File cannot be deleted: {0}
fileIsTooBigForThisConvenienceMethod=File is too big for this convenience method ({0} bytes).
fileIsTooLarge=File is too large: {0}
fileModeNotSetForPath=FileMode not set for path {0}
+filterExecutionFailed=Execution of filter command ''{0}'' on file ''{1}'' failed
+filterExecutionFailedRc=Execution of filter command ''{0}'' on file ''{1}'' failed with return code ''{2}'', message on stderr: ''{3}''
findingGarbage=Finding garbage
flagIsDisposed={0} is disposed.
flagNotFromThis={0} not from this.
@@ -344,6 +349,7 @@ invalidPathReservedOnWindows=Invalid path (''{0}'' is reserved on Windows): {1}
invalidReflogRevision=Invalid reflog revision: {0}
invalidRefName=Invalid ref name: {0}
invalidRemote=Invalid remote: {0}
+invalidRepositoryStateNoHead=Invalid repository --- cannot read HEAD
invalidShallowObject=invalid shallow object {0}, expected commit
invalidStageForPath=Invalid stage {0} for path {1}
invalidTagOption=Invalid tag option: {0}
@@ -450,6 +456,7 @@ packfileIsTruncated=Packfile {0} is truncated.
packfileIsTruncatedNoParam=Packfile is truncated.
packHandleIsStale=Pack file {0} handle is stale, removing it from pack list
packHasUnresolvedDeltas=pack has unresolved deltas
+packInaccessible=Pack file {0} now inaccessible; removing it from pack list
packingCancelledDuringObjectsWriting=Packing cancelled during objects writing
packObjectCountMismatch=Pack object count mismatch: pack {0} index {1}: {2}
packRefs=Pack refs
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java
new file mode 100644
index 0000000000..08f81cb9f8
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/NonNull.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2015, Andrey Loskutov <loskutov@gmx.de>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * JGit's replacement for the {@code javax.annotation.Nonnull}.
+ * <p>
+ * Denotes that a local variable, parameter, field, method return value expected
+ * to be non {@code null}.
+ *
+ * @since 4.2
+ */
+@Documented
+@Retention(RetentionPolicy.CLASS)
+@Target({ FIELD, METHOD, PARAMETER, LOCAL_VARIABLE })
+public @interface NonNull {
+ // marker annotation with no members
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/annotations/Nullable.java b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/Nullable.java
index 254920e7a3..7b9156710f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/annotations/Nullable.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/annotations/Nullable.java
@@ -54,13 +54,46 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * JGit's replacement for the {@code javax.annotations.Nullable}.
+ * Marks types that can hold the value {@code null} at run time.
* <p>
- * Denotes that a local variable, parameter, field, method return value can be
- * {@code null}.
+ * Unlike {@code org.eclipse.jdt.annotation.Nullable}, this has run-time
+ * retention, allowing the annotation to be recognized by
+ * <a href="https://github.com/google/guice/wiki/UseNullable">Guice</a>. Unlike
+ * {@code javax.annotation.Nullable}, this does not involve importing new classes
+ * to a standard (Java EE) package, so it can be deployed in an OSGi container
+ * without running into
+ * <a href="http://wiki.osgi.org/wiki/Split_Packages">split-package</a>
+ * <a href="https://gerrit-review.googlesource.com/50112">problems</a>.
+ * <p>
+ * You can use this annotation to qualify a type in a method signature or local
+ * variable declaration. The entity whose type has this annotation is allowed to
+ * hold the value {@code null} at run time. This allows annotation based null
+ * analysis to infer that
+ * <ul>
+ * <li>Binding a {@code null} value to the entity is legal.
+ * <li>Dereferencing the entity is unsafe and can trigger a
+ * {@code NullPointerException}.
+ * </ul>
+ * <p>
+ * To avoid a dependency on Java 8, this annotation does not use
+ * {@link Target @Target} {@code TYPE_USE}. That may change when JGit starts
+ * requiring Java 8.
+ * <p>
+ * <b>Warning:</b> Please do not use this annotation on arrays. Different
+ * annotation processors treat {@code @Nullable Object[]} differently: some
+ * treat it as an array of nullable objects, for consistency with versions of
+ * {@code Nullable} defined with {@code @Target} {@code TYPE_USE}, while others
+ * treat it as a nullable array of objects. JGit therefore avoids using this
+ * annotation on arrays altogether.
+ *
+ * @see <a href=
+ * "http://types.cs.washington.edu/checker-framework/current/checker-framework-manual.html#faq-array-syntax-meaning">
+ * The checker-framework manual</a>
+ *
+ * @since 4.2
*/
@Documented
-@Retention(RetentionPolicy.CLASS)
+@Retention(RetentionPolicy.RUNTIME)
@Target({ FIELD, METHOD, PARAMETER, LOCAL_VARIABLE })
public @interface Nullable {
// marker annotation with no members
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
index de6c32a808..67fb342fe2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
@@ -48,6 +48,7 @@ import java.io.InputStream;
import java.util.Collection;
import java.util.LinkedList;
+import org.eclipse.jgit.api.errors.FilterFailedException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.NoFilepatternException;
@@ -63,6 +64,7 @@ import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
import org.eclipse.jgit.treewalk.WorkingTreeIterator;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
@@ -139,6 +141,7 @@ public class AddCommand extends GitCommand<DirCache> {
try (ObjectInserter inserter = repo.newObjectInserter();
final TreeWalk tw = new TreeWalk(repo)) {
+ tw.setOperationType(OperationType.CHECKIN_OP);
dc = repo.lockDirCache();
DirCacheIterator c;
@@ -146,6 +149,7 @@ public class AddCommand extends GitCommand<DirCache> {
tw.addTree(new DirCacheBuildIterator(builder));
if (workingTreeIterator == null)
workingTreeIterator = new FileTreeIterator(repo);
+ workingTreeIterator.setDirCacheIterator(tw, 0);
tw.addTree(workingTreeIterator);
tw.setRecursive(true);
if (!addAll)
@@ -208,6 +212,9 @@ public class AddCommand extends GitCommand<DirCache> {
builder.commit();
setCallable(false);
} catch (IOException e) {
+ Throwable cause = e.getCause();
+ if (cause != null && cause instanceof FilterFailedException)
+ throw (FilterFailedException) cause;
throw new JGitInternalException(
JGitText.get().exceptionCaughtDuringExecutionOfAddCommand, e);
} finally {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
index 8d85bfcb15..8743ea9ac7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
@@ -222,6 +222,12 @@ public class CheckoutCommand extends GitCommand<Ref> {
}
Ref headRef = repo.getRef(Constants.HEAD);
+ if (headRef == null) {
+ // TODO Git CLI supports checkout from unborn branch, we should
+ // also allow this
+ throw new UnsupportedOperationException(
+ JGitText.get().cannotCheckoutFromUnbornBranch);
+ }
String shortHeadRef = getShortBranchName(headRef);
String refLogMessage = "checkout: moving from " + shortHeadRef; //$NON-NLS-1$
ObjectId branch;
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 6174d48d3a..9466dab74e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
@@ -86,6 +86,7 @@ import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
import org.eclipse.jgit.util.ChangeIdUtil;
/**
@@ -328,9 +329,12 @@ public class CommitCommand extends GitCommand<RevCommit> {
boolean emptyCommit = true;
try (TreeWalk treeWalk = new TreeWalk(repo)) {
+ treeWalk.setOperationType(OperationType.CHECKIN_OP);
int dcIdx = treeWalk
.addTree(new DirCacheBuildIterator(existingBuilder));
- int fIdx = treeWalk.addTree(new FileTreeIterator(repo));
+ FileTreeIterator fti = new FileTreeIterator(repo);
+ fti.setDirCacheIterator(treeWalk, 0);
+ int fIdx = treeWalk.addTree(fti);
int hIdx = -1;
if (headId != null)
hIdx = treeWalk.addTree(rw.parseTree(headId));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
index addca4c469..2cd5f59a71 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
@@ -713,8 +713,48 @@ public class Git implements AutoCloseable {
}
/**
- * @return the git repository this class is interacting with; see {@link
- * #close()} for notes on closing this repository.
+ * Return a command used to list the available remotes.
+ *
+ * @return a {@link RemoteListCommand}
+ * @since 4.2
+ */
+ public RemoteListCommand remoteList() {
+ return new RemoteListCommand(repo);
+ }
+
+ /**
+ * Return a command used to add a new remote.
+ *
+ * @return a {@link RemoteAddCommand}
+ * @since 4.2
+ */
+ public RemoteAddCommand remoteAdd() {
+ return new RemoteAddCommand(repo);
+ }
+
+ /**
+ * Return a command used to remove an existing remote.
+ *
+ * @return a {@link RemoteRemoveCommand}
+ * @since 4.2
+ */
+ public RemoteRemoveCommand remoteRemove() {
+ return new RemoteRemoveCommand(repo);
+ }
+
+ /**
+ * Return a command used to change the URL of an existing remote.
+ *
+ * @return a {@link RemoteSetUrlCommand}
+ * @since 4.2
+ */
+ public RemoteSetUrlCommand remoteSetUrl() {
+ return new RemoteSetUrlCommand(repo);
+ }
+
+ /**
+ * @return the git repository this class is interacting with; see
+ * {@link #close()} for notes on closing this repository.
*/
public Repository getRepository() {
return repo;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java
index 227e32236d..f5b82bdd7d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java
@@ -89,9 +89,8 @@ public class PushCommand extends
private String receivePack = RemoteConfig.DEFAULT_RECEIVE_PACK;
private boolean dryRun;
-
+ private boolean atomic;
private boolean force;
-
private boolean thin = Transport.DEFAULT_PUSH_THIN;
private OutputStream out;
@@ -145,6 +144,7 @@ public class PushCommand extends
transports = Transport.openAll(repo, remote, Transport.Operation.PUSH);
for (final Transport transport : transports) {
transport.setPushThin(thin);
+ transport.setPushAtomic(atomic);
if (receivePack != null)
transport.setOptionReceivePack(receivePack);
transport.setDryRun(dryRun);
@@ -397,6 +397,29 @@ public class PushCommand extends
}
/**
+ * @return true if all-or-nothing behavior is requested.
+ * @since 4.2
+ */
+ public boolean isAtomic() {
+ return atomic;
+ }
+
+ /**
+ * Requests atomic push (all references updated, or no updates).
+ *
+ * Default setting is false.
+ *
+ * @param atomic
+ * @return {@code this}
+ * @since 4.2
+ */
+ public PushCommand setAtomic(boolean atomic) {
+ checkCallable();
+ this.atomic = atomic;
+ return this;
+ }
+
+ /**
* @return the force preference for push operation
*/
public boolean isForce() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
index ff29008420..8582bbb0dc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
@@ -668,12 +668,13 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
}
private void writeRewrittenHashes() throws RevisionSyntaxException,
- IOException {
+ IOException, RefNotFoundException {
File currentCommitFile = rebaseState.getFile(CURRENT_COMMIT);
if (!currentCommitFile.exists())
return;
- String head = repo.resolve(Constants.HEAD).getName();
+ ObjectId headId = getHead().getObjectId();
+ String head = headId.getName();
String currentCommits = rebaseState.readFile(CURRENT_COMMIT);
for (String current : currentCommits.split("\n")) //$NON-NLS-1$
RebaseState
@@ -743,8 +744,8 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
private void resetSoftToParent() throws IOException,
GitAPIException, CheckoutConflictException {
- Ref orig_head = repo.getRef(Constants.ORIG_HEAD);
- ObjectId orig_headId = orig_head.getObjectId();
+ Ref ref = repo.getRef(Constants.ORIG_HEAD);
+ ObjectId orig_head = ref == null ? null : ref.getObjectId();
try {
// we have already commited the cherry-picked commit.
// what we need is to have changes introduced by this
@@ -755,7 +756,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
} finally {
// set ORIG_HEAD back to where we started because soft
// reset moved it
- repo.writeOrigHead(orig_headId);
+ repo.writeOrigHead(orig_head);
}
}
@@ -980,6 +981,9 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
try {
raw = IO.readFully(authorScriptFile);
} catch (FileNotFoundException notFound) {
+ if (authorScriptFile.exists()) {
+ throw notFound;
+ }
return null;
}
return parseAuthor(raw);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteAddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteAddCommand.java
new file mode 100644
index 0000000000..679566903f
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteAddCommand.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r@zend.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.URIish;
+
+/**
+ * Used to add a new remote.
+ *
+ * This class has setters for all supported options and arguments of this
+ * command and a {@link #call()} method to finally execute the command.
+ *
+ * @see <a href=
+ * "http://www.kernel.org/pub/software/scm/git/docs/git-remote.html" > Git
+ * documentation about Remote</a>
+ *
+ * @since 4.2
+ */
+public class RemoteAddCommand extends GitCommand<RemoteConfig> {
+
+ private String name;
+
+ private URIish uri;
+
+ /**
+ * @param repo
+ */
+ protected RemoteAddCommand(Repository repo) {
+ super(repo);
+ }
+
+ /**
+ * The name of the remote to add.
+ *
+ * @param name
+ * a remote name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * The URL of the repository for the new remote.
+ *
+ * @param uri
+ * an URL for the remote
+ */
+ public void setUri(URIish uri) {
+ this.uri = uri;
+ }
+
+ /**
+ * Executes the {@code remote add} command with all the options and
+ * parameters collected by the setter methods of this class.
+ *
+ * @return the {@link RemoteConfig} object of the added remote
+ */
+ @Override
+ public RemoteConfig call() throws GitAPIException {
+ checkCallable();
+
+ try {
+ StoredConfig config = repo.getConfig();
+ RemoteConfig remote = new RemoteConfig(config, name);
+
+ RefSpec refSpec = new RefSpec();
+ refSpec = refSpec.setForceUpdate(true);
+ refSpec = refSpec.setSourceDestination(Constants.R_HEADS + "*", //$NON-NLS-1$
+ Constants.R_REMOTES + name + "/*"); //$NON-NLS-1$
+ remote.addFetchRefSpec(refSpec);
+
+ remote.addURI(uri);
+
+ remote.update(config);
+ config.save();
+ return remote;
+ } catch (IOException | URISyntaxException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+
+ }
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteListCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteListCommand.java
new file mode 100644
index 0000000000..f778eaa28c
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteListCommand.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r@zend.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.RemoteConfig;
+
+/**
+ * Used to obtain the list of remotes.
+ *
+ * This class has setters for all supported options and arguments of this
+ * command and a {@link #call()} method to finally execute the command.
+ *
+ * @see <a href=
+ * "http://www.kernel.org/pub/software/scm/git/docs/git-remote.html" > Git
+ * documentation about Remote</a>
+ *
+ * @since 4.2
+ */
+public class RemoteListCommand extends GitCommand<List<RemoteConfig>> {
+
+ /**
+ * @param repo
+ */
+ protected RemoteListCommand(Repository repo) {
+ super(repo);
+ }
+
+ /**
+ * Executes the {@code remote} command with all the options and parameters
+ * collected by the setter methods of this class.
+ *
+ * @return a list of {@link RemoteConfig} objects.
+ */
+ @Override
+ public List<RemoteConfig> call() throws GitAPIException {
+ checkCallable();
+
+ try {
+ return RemoteConfig.getAllRemoteConfigs(repo.getConfig());
+ } catch (URISyntaxException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+ }
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java
new file mode 100644
index 0000000000..5782bf61b5
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteRemoveCommand.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r@zend.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.transport.RemoteConfig;
+
+/**
+ * Used to remove an existing remote.
+ *
+ * This class has setters for all supported options and arguments of this
+ * command and a {@link #call()} method to finally execute the command.
+ *
+ * @see <a href=
+ * "http://www.kernel.org/pub/software/scm/git/docs/git-remote.html" > Git
+ * documentation about Remote</a>
+ *
+ * @since 4.2
+ */
+public class RemoteRemoveCommand extends GitCommand<RemoteConfig> {
+
+ private String name;
+
+ /**
+ * @param repo
+ */
+ protected RemoteRemoveCommand(Repository repo) {
+ super(repo);
+ }
+
+ /**
+ * The name of the remote to remove.
+ *
+ * @param name
+ * a remote name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Executes the {@code remote} command with all the options and parameters
+ * collected by the setter methods of this class.
+ *
+ * @return the {@link RemoteConfig} object of the removed remote
+ */
+ @Override
+ public RemoteConfig call() throws GitAPIException {
+ checkCallable();
+
+ try {
+ StoredConfig config = repo.getConfig();
+ RemoteConfig remote = new RemoteConfig(config, name);
+ config.unsetSection(ConfigConstants.CONFIG_KEY_REMOTE, name);
+ config.save();
+ return remote;
+ } catch (IOException | URISyntaxException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+
+ }
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java
new file mode 100644
index 0000000000..6bd2ac7993
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RemoteSetUrlCommand.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2015, Kaloyan Raev <kaloyan.r@zend.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.URIish;
+
+/**
+ * Used to to change the URL of a remote.
+ *
+ * This class has setters for all supported options and arguments of this
+ * command and a {@link #call()} method to finally execute the command.
+ *
+ * @see <a href=
+ * "http://www.kernel.org/pub/software/scm/git/docs/git-remote.html" > Git
+ * documentation about Remote</a>
+ *
+ * @since 4.2
+ */
+public class RemoteSetUrlCommand extends GitCommand<RemoteConfig> {
+
+ private String name;
+
+ private URIish uri;
+
+ private boolean push;
+
+ /**
+ * @param repo
+ */
+ protected RemoteSetUrlCommand(Repository repo) {
+ super(repo);
+ }
+
+ /**
+ * The name of the remote to change the URL for.
+ *
+ * @param name
+ * a remote name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * The new URL for the remote.
+ *
+ * @param uri
+ * an URL for the remote
+ */
+ public void setUri(URIish uri) {
+ this.uri = uri;
+ }
+
+ /**
+ * Whether to change the push URL of the remote instead of the fetch URL.
+ *
+ * @param push
+ * <code>true</code> to set the push url, <code>false</code> to
+ * set the fetch url
+ */
+ public void setPush(boolean push) {
+ this.push = push;
+ }
+
+ /**
+ * Executes the {@code remote} command with all the options and parameters
+ * collected by the setter methods of this class.
+ *
+ * @return the {@link RemoteConfig} object of the modified remote
+ */
+ @Override
+ public RemoteConfig call() throws GitAPIException {
+ checkCallable();
+
+ try {
+ StoredConfig config = repo.getConfig();
+ RemoteConfig remote = new RemoteConfig(config, name);
+ if (push) {
+ List<URIish> uris = remote.getPushURIs();
+ if (uris.size() > 1) {
+ throw new JGitInternalException(
+ "remote.newtest.pushurl has multiple values"); //$NON-NLS-1$
+ } else if (uris.size() == 1) {
+ remote.removePushURI(uris.get(0));
+ }
+ remote.addPushURI(uri);
+ } else {
+ List<URIish> uris = remote.getURIs();
+ if (uris.size() > 1) {
+ throw new JGitInternalException(
+ "remote.newtest.url has multiple values"); //$NON-NLS-1$
+ } else if (uris.size() == 1) {
+ remote.removeURI(uris.get(0));
+ }
+ remote.addURI(uri);
+ }
+
+ remote.update(config);
+ config.save();
+ return remote;
+ } catch (IOException | URISyntaxException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+ }
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java
index 607253b76d..0731dd45ec 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RenameBranchCommand.java
@@ -51,6 +51,7 @@ import org.eclipse.jgit.api.errors.DetachedHeadException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.InvalidRefNameException;
import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.api.errors.NoHeadException;
import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.internal.JGitText;
@@ -121,6 +122,10 @@ public class RenameBranchCommand extends GitCommand<Ref> {
fullOldName = ref.getName();
} else {
fullOldName = repo.getFullBranch();
+ if (fullOldName == null) {
+ throw new NoHeadException(
+ JGitText.get().invalidRepositoryStateNoHead);
+ }
if (ObjectId.isId(fullOldName))
throw new DetachedHeadException();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/FilterFailedException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/FilterFailedException.java
new file mode 100644
index 0000000000..fbc30ef162
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/FilterFailedException.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2015, Christian Halstrick <christian.halstrick@sap.com> and
+ * other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v1.0 which accompanies this
+ * distribution, is reproduced below, and is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.api.errors;
+
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.internal.JGitText;
+
+/**
+ * Exception thrown when the execution of a filter command failed
+ *
+ * @since 4.2
+ */
+public class FilterFailedException extends GitAPIException {
+ private static final long serialVersionUID = 1L;
+
+ private String filterCommand;
+
+ private String path;
+
+ private byte[] stdout;
+
+ private String stderr;
+
+ private int rc;
+
+ /**
+ * Thrown if during execution of filter command an exception occurred
+ *
+ * @param cause
+ * the exception
+ * @param filterCommand
+ * the command which failed
+ * @param path
+ * the path processed by the filter
+ */
+ public FilterFailedException(Exception cause, String filterCommand,
+ String path) {
+ super(MessageFormat.format(JGitText.get().filterExecutionFailed,
+ filterCommand, path), cause);
+ this.filterCommand = filterCommand;
+ this.path = path;
+ }
+
+ /**
+ * Thrown if a filter command returns a non-zero return code
+ *
+ * @param rc
+ * the return code
+ * @param filterCommand
+ * the command which failed
+ * @param path
+ * the path processed by the filter
+ * @param stdout
+ * the output the filter generated so far. This should be limited
+ * to reasonable size.
+ * @param stderr
+ * the stderr output of the filter
+ */
+ @SuppressWarnings("boxing")
+ public FilterFailedException(int rc, String filterCommand, String path,
+ byte[] stdout, String stderr) {
+ super(MessageFormat.format(JGitText.get().filterExecutionFailedRc,
+ filterCommand, path, rc, stderr));
+ this.rc = rc;
+ this.filterCommand = filterCommand;
+ this.path = path;
+ this.stdout = stdout;
+ this.stderr = stderr;
+ }
+
+ /**
+ * @return the filterCommand
+ */
+ public String getFilterCommand() {
+ return filterCommand;
+ }
+
+ /**
+ * @return the path of the file processed by the filter command
+ */
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * @return the output generated by the filter command. Might be truncated to
+ * limit memory consumption.
+ */
+ public byte[] getOutput() {
+ return stdout;
+ }
+
+ /**
+ * @return the error output returned by the filter command
+ */
+ public String getError() {
+ return stderr;
+ }
+
+ /**
+ * @return the return code returned by the filter command
+ */
+ public int getReturnCode() {
+ return rc;
+ }
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java
index d3ce685187..905ad76929 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java
@@ -50,8 +50,10 @@ package org.eclipse.jgit.attributes;
* <li>Set - represented by {@link State#SET}</li>
* <li>Unset - represented by {@link State#UNSET}</li>
* <li>Set to a value - represented by {@link State#CUSTOM}</li>
- * <li>Unspecified - <code>null</code> is used instead of an instance of this
- * class</li>
+ * <li>Unspecified - used to revert an attribute . This is crucial in order to
+ * mark an attribute as unspecified in the attributes map and thus preventing
+ * following (with lower priority) nodes from setting the attribute to a value
+ * at all</li>
* </ul>
* </p>
*
@@ -61,6 +63,7 @@ public final class Attribute {
/**
* The attribute value state
+ * see also https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html
*/
public static enum State {
/** the attribute is set */
@@ -69,6 +72,13 @@ public final class Attribute {
/** the attribute is unset */
UNSET,
+ /**
+ * the attribute appears as if it would not be defined at all
+ *
+ * @since 4.2
+ */
+ UNSPECIFIED,
+
/** the attribute is set to a custom value */
CUSTOM
}
@@ -176,6 +186,8 @@ public final class Attribute {
return key;
case UNSET:
return "-" + key; //$NON-NLS-1$
+ case UNSPECIFIED:
+ return "!" + key; //$NON-NLS-1$
case CUSTOM:
default:
return key + "=" + value; //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attributes.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attributes.java
new file mode 100644
index 0000000000..0810e31682
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attributes.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com>
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.attributes;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.eclipse.jgit.attributes.Attribute.State;
+
+/**
+ * Represents a set of attributes for a path
+ * <p>
+ *
+ * @since 4.2
+ */
+public final class Attributes {
+ private final Map<String, Attribute> map = new LinkedHashMap<>();
+
+ /**
+ * Creates a new instance
+ *
+ * @param attributes
+ */
+ public Attributes(Attribute... attributes) {
+ if (attributes != null) {
+ for (Attribute a : attributes) {
+ put(a);
+ }
+ }
+ }
+
+ /**
+ * @return true if the set does not contain any attributes
+ */
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ /**
+ * @param key
+ * @return the attribute or null
+ */
+ public Attribute get(String key) {
+ return map.get(key);
+ }
+
+ /**
+ * @return all attributes
+ */
+ public Collection<Attribute> getAll() {
+ return new ArrayList<>(map.values());
+ }
+
+ /**
+ * @param a
+ */
+ public void put(Attribute a) {
+ map.put(a.getKey(), a);
+ }
+
+ /**
+ * @param key
+ */
+ public void remove(String key) {
+ map.remove(key);
+ }
+
+ /**
+ * @param key
+ * @return true if the {@link Attributes} contains this key
+ */
+ public boolean containsKey(String key) {
+ return map.containsKey(key);
+ }
+
+ /**
+ * Returns the state.
+ *
+ * @param key
+ *
+ * @return the state (never returns <code>null</code>)
+ */
+ public Attribute.State getState(String key) {
+ Attribute a = map.get(key);
+ return a != null ? a.getState() : Attribute.State.UNSPECIFIED;
+ }
+
+ /**
+ * @param key
+ * @return true if the key is {@link State#SET}, false in all other cases
+ */
+ public boolean isSet(String key) {
+ return (getState(key) == State.SET);
+ }
+
+ /**
+ * @param key
+ * @return true if the key is {@link State#UNSET}, false in all other cases
+ */
+ public boolean isUnset(String key) {
+ return (getState(key) == State.UNSET);
+ }
+
+ /**
+ * @param key
+ * @return true if the key is {@link State#UNSPECIFIED}, false in all other
+ * cases
+ */
+ public boolean isUnspecified(String key) {
+ return (getState(key) == State.UNSPECIFIED);
+ }
+
+ /**
+ * @param key
+ * @return true if the key is {@link State#CUSTOM}, false in all other cases
+ * see {@link #getValue(String)} for the value of the key
+ */
+ public boolean isCustom(String key) {
+ return (getState(key) == State.CUSTOM);
+ }
+
+ /**
+ * @param key
+ * @return the attribute value (may be <code>null</code>)
+ */
+ public String getValue(String key) {
+ Attribute a = map.get(key);
+ return a != null ? a.getValue() : null;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append(getClass().getSimpleName());
+ buf.append("["); //$NON-NLS-1$
+ buf.append(" "); //$NON-NLS-1$
+ for (Attribute a : map.values()) {
+ buf.append(a.toString());
+ buf.append(" "); //$NON-NLS-1$
+ }
+ buf.append("]"); //$NON-NLS-1$
+ return buf.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return map.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!(obj instanceof Attributes))
+ return false;
+ Attributes other = (Attributes) obj;
+ return this.map.equals(other.map);
+ }
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java
index 70f56ff964..5c0aba2e0e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java
@@ -50,7 +50,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
-import java.util.Map;
import org.eclipse.jgit.lib.Constants;
@@ -134,11 +133,12 @@ public class AttributesNode {
* true if the target item is a directory.
* @param attributes
* Map that will hold the attributes matching this entry path. If
- * it is not empty, this method will NOT override any
- * existing entry.
+ * it is not empty, this method will NOT override any existing
+ * entry.
+ * @since 4.2
*/
- public void getAttributes(String entryPath, boolean isDirectory,
- Map<String, Attribute> attributes) {
+ public void getAttributes(String entryPath,
+ boolean isDirectory, Attributes attributes) {
// Parse rules in the reverse order that they were read since the last
// entry should be used
ListIterator<AttributesRule> ruleIterator = rules.listIterator(rules
@@ -153,7 +153,7 @@ public class AttributesNode {
while (attributeIte.hasPrevious()) {
Attribute attr = attributeIte.previous();
if (!attributes.containsKey(attr.getKey()))
- attributes.put(attr.getKey(), attr);
+ attributes.put(attr);
}
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java
new file mode 100644
index 0000000000..6f2ebad677
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNodeProvider.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014, Arthur Daussy <arthur.daussy@obeo.fr>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.attributes;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.lib.CoreConfig;
+
+/**
+ * An interface used to retrieve the global and info {@link AttributesNode}s.
+ *
+ * @since 4.2
+ *
+ */
+public interface AttributesNodeProvider {
+
+ /**
+ * Retrieve the {@link AttributesNode} that holds the information located
+ * in $GIT_DIR/info/attributes file.
+ *
+ * @return the {@link AttributesNode} that holds the information located in
+ * $GIT_DIR/info/attributes file.
+ * @throws IOException
+ * if an error is raised while parsing the attributes file
+ */
+ public AttributesNode getInfoAttributesNode() throws IOException;
+
+ /**
+ * Retrieve the {@link AttributesNode} that holds the information located
+ * in the global gitattributes file.
+ *
+ * @return the {@link AttributesNode} that holds the information located in
+ * the global gitattributes file.
+ * @throws IOException
+ * IOException if an error is raised while parsing the
+ * attributes file
+ * @see CoreConfig#getAttributesFile()
+ */
+ public AttributesNode getGlobalAttributesNode() throws IOException;
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java
new file mode 100644
index 0000000000..1037f697d4
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015, Christian Halstrick <christian.halstrick@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.attributes;
+
+/**
+ * Interface for classes which provide git attributes
+ *
+ * @since 4.2
+ */
+public interface AttributesProvider {
+ /**
+ * @return the currently active attributes
+ */
+ public Attributes getAttributes();
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java
index bcac14b5ff..35d18c4b2a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java
@@ -84,6 +84,13 @@ public class AttributesRule {
continue;
}
+ if (attribute.startsWith("!")) {//$NON-NLS-1$
+ if (attribute.length() > 1)
+ result.add(new Attribute(attribute.substring(1),
+ State.UNSPECIFIED));
+ continue;
+ }
+
final int equalsIndex = attribute.indexOf("="); //$NON-NLS-1$
if (equalsIndex == -1)
result.add(new Attribute(attribute, State.SET));
@@ -200,4 +207,16 @@ public class AttributesRule {
boolean match = matcher.matches(relativeTarget, isDirectory);
return match;
}
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(pattern);
+ for (Attribute a : attributes) {
+ sb.append(" "); //$NON-NLS-1$
+ sb.append(a);
+ }
+ return sb.toString();
+
+ }
} \ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java
index 0dc4b05787..444ab1cb83 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java
@@ -132,7 +132,11 @@ public abstract class ContentSource {
@Override
public long size(String path, ObjectId id) throws IOException {
- return reader.getObjectSize(id, Constants.OBJ_BLOB);
+ try {
+ return reader.getObjectSize(id, Constants.OBJ_BLOB);
+ } catch (MissingObjectException ignore) {
+ return 0;
+ }
}
@Override
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
index 6d9a32db92..fa0339544f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
@@ -63,6 +63,7 @@ import java.util.Comparator;
import java.util.List;
import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.IndexReadException;
import org.eclipse.jgit.errors.LockFailedException;
import org.eclipse.jgit.errors.UnmergedPathException;
import org.eclipse.jgit.events.IndexChangedEvent;
@@ -70,12 +71,15 @@ import org.eclipse.jgit.events.IndexChangedListener;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.file.FileSnapshot;
import org.eclipse.jgit.internal.storage.file.LockFile;
+import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.IO;
@@ -145,6 +149,28 @@ public class DirCache {
}
/**
+ * Create a new in memory index read from the contents of a tree.
+ *
+ * @param reader
+ * reader to access the tree objects from a repository.
+ * @param treeId
+ * tree to read. Must identify a tree, not a tree-ish.
+ * @return a new cache which has no backing store file, but contains the
+ * contents of {@code treeId}.
+ * @throws IOException
+ * one or more trees not available from the ObjectReader.
+ * @since 4.2
+ */
+ public static DirCache read(ObjectReader reader, AnyObjectId treeId)
+ throws IOException {
+ DirCache d = newInCore();
+ DirCacheBuilder b = d.builder();
+ b.addTree(null, DirCacheEntry.STAGE_0, reader, treeId);
+ b.finish();
+ return d;
+ }
+
+ /**
* Create a new in-core index representation and read an index from disk.
* <p>
* The new index will be read before it is returned to the caller. Read
@@ -417,6 +443,12 @@ public class DirCache {
}
}
} catch (FileNotFoundException fnfe) {
+ if (liveFile.exists()) {
+ // Panic: the index file exists but we can't read it
+ throw new IndexReadException(
+ MessageFormat.format(JGitText.get().cannotReadIndex,
+ liveFile.getAbsolutePath(), fnfe));
+ }
// Someone must have deleted it between our exists test
// and actually opening the path. That's fine, its empty.
//
@@ -869,8 +901,8 @@ public class DirCache {
*/
public DirCacheEntry[] getEntriesWithin(String path) {
if (path.length() == 0) {
- final DirCacheEntry[] r = new DirCacheEntry[sortedEntries.length];
- System.arraycopy(sortedEntries, 0, r, 0, sortedEntries.length);
+ DirCacheEntry[] r = new DirCacheEntry[entryCnt];
+ System.arraycopy(sortedEntries, 0, r, 0, entryCnt);
return r;
}
if (!path.endsWith("/")) //$NON-NLS-1$
@@ -963,6 +995,7 @@ public class DirCache {
private void updateSmudgedEntries() throws IOException {
List<String> paths = new ArrayList<String>(128);
try (TreeWalk walk = new TreeWalk(repository)) {
+ walk.setOperationType(OperationType.CHECKIN_OP);
for (int i = 0; i < entryCnt; i++)
if (sortedEntries[i].isSmudged())
paths.add(sortedEntries[i].getPathString());
@@ -974,6 +1007,7 @@ public class DirCache {
FileTreeIterator fIter = new FileTreeIterator(repository);
walk.addTree(iIter);
walk.addTree(fIter);
+ fIter.setDirCacheIterator(walk, 0);
walk.setRecursive(true);
while (walk.next()) {
iIter = walk.getTree(0, DirCacheIterator.class);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java
index 6c7a70c52e..cfebe2d073 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheBuilder.java
@@ -44,6 +44,9 @@
package org.eclipse.jgit.dircache;
+import static org.eclipse.jgit.lib.FileMode.TYPE_MASK;
+import static org.eclipse.jgit.lib.FileMode.TYPE_TREE;
+
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Arrays;
@@ -51,9 +54,7 @@ import java.util.Arrays;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
-import org.eclipse.jgit.treewalk.TreeWalk;
/**
* Updates a {@link DirCache} by adding individual {@link DirCacheEntry}s.
@@ -102,8 +103,9 @@ public class DirCacheBuilder extends BaseDirCacheEditor {
*/
public void add(final DirCacheEntry newEntry) {
if (newEntry.getRawMode() == 0)
- throw new IllegalArgumentException(MessageFormat.format(JGitText.get().fileModeNotSetForPath
- , newEntry.getPathString()));
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().fileModeNotSetForPath,
+ newEntry.getPathString()));
beforeAdd(newEntry);
fastAdd(newEntry);
}
@@ -162,27 +164,56 @@ public class DirCacheBuilder extends BaseDirCacheEditor {
* @throws IOException
* a tree cannot be read to iterate through its entries.
*/
- public void addTree(final byte[] pathPrefix, final int stage,
- final ObjectReader reader, final AnyObjectId tree) throws IOException {
- final TreeWalk tw = new TreeWalk(reader);
- tw.addTree(new CanonicalTreeParser(pathPrefix, reader, tree
- .toObjectId()));
- tw.setRecursive(true);
- if (tw.next()) {
- final DirCacheEntry newEntry = toEntry(stage, tw);
- beforeAdd(newEntry);
- fastAdd(newEntry);
- while (tw.next())
- fastAdd(toEntry(stage, tw));
+ public void addTree(byte[] pathPrefix, int stage, ObjectReader reader,
+ AnyObjectId tree) throws IOException {
+ CanonicalTreeParser p = createTreeParser(pathPrefix, reader, tree);
+ while (!p.eof()) {
+ if (isTree(p)) {
+ p = enterTree(p, reader);
+ continue;
+ }
+
+ DirCacheEntry first = toEntry(stage, p);
+ beforeAdd(first);
+ fastAdd(first);
+ p = p.next();
+ break;
+ }
+
+ // Rest of tree entries are correctly sorted; use fastAdd().
+ while (!p.eof()) {
+ if (isTree(p)) {
+ p = enterTree(p, reader);
+ } else {
+ fastAdd(toEntry(stage, p));
+ p = p.next();
+ }
}
}
- private DirCacheEntry toEntry(final int stage, final TreeWalk tw) {
- final DirCacheEntry e = new DirCacheEntry(tw.getRawPath(), stage);
- final AbstractTreeIterator i;
+ private static CanonicalTreeParser createTreeParser(byte[] pathPrefix,
+ ObjectReader reader, AnyObjectId tree) throws IOException {
+ return new CanonicalTreeParser(pathPrefix, reader, tree);
+ }
+
+ private static boolean isTree(CanonicalTreeParser p) {
+ return (p.getEntryRawMode() & TYPE_MASK) == TYPE_TREE;
+ }
+
+ private static CanonicalTreeParser enterTree(CanonicalTreeParser p,
+ ObjectReader reader) throws IOException {
+ p = p.createSubtreeIterator(reader);
+ return p.eof() ? p.next() : p;
+ }
+
+ private static DirCacheEntry toEntry(int stage, CanonicalTreeParser i) {
+ byte[] buf = i.getEntryPathBuffer();
+ int len = i.getEntryPathLength();
+ byte[] path = new byte[len];
+ System.arraycopy(buf, 0, path, 0, len);
- i = tw.getTree(0, AbstractTreeIterator.class);
- e.setFileMode(tw.getFileMode(0));
+ DirCacheEntry e = new DirCacheEntry(path, stage);
+ e.setFileMode(i.getEntryRawMode());
e.setObjectIdFromRaw(i.idBuffer(), i.idOffset());
return e;
}
@@ -242,9 +273,9 @@ public class DirCacheBuilder extends BaseDirCacheEditor {
sorted = true;
}
- private static IllegalStateException bad(final DirCacheEntry a,
- final String msg) {
- return new IllegalStateException(msg + ": " + a.getStage() + " " //$NON-NLS-1$ //$NON-NLS-2$
- + a.getPathString());
+ private static IllegalStateException bad(DirCacheEntry a, String msg) {
+ return new IllegalStateException(String.format(
+ "%s: %d %s", //$NON-NLS-1$
+ msg, Integer.valueOf(a.getStage()), a.getPathString()));
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
index 0036ab5089..4eb688170c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -52,15 +52,18 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.eclipse.jgit.api.errors.FilterFailedException;
import org.eclipse.jgit.errors.CheckoutConflictException;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.IndexWriteException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
import org.eclipse.jgit.lib.CoreConfig.SymLinks;
import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectChecker;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
@@ -76,6 +79,7 @@ import org.eclipse.jgit.treewalk.WorkingTreeIterator;
import org.eclipse.jgit.treewalk.WorkingTreeOptions;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.FS.ExecutionResult;
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.RawParseUtils;
import org.eclipse.jgit.util.SystemReader;
@@ -85,9 +89,10 @@ import org.eclipse.jgit.util.io.AutoCRLFOutputStream;
* This class handles checking out one or two trees merging with the index.
*/
public class DirCacheCheckout {
+ private static final int MAX_EXCEPTION_TEXT_SIZE = 10 * 1024;
private Repository repo;
- private HashMap<String, ObjectId> updated = new HashMap<String, ObjectId>();
+ private HashMap<String, String> updated = new HashMap<String, String>();
private ArrayList<String> conflicts = new ArrayList<String>();
@@ -112,9 +117,9 @@ public class DirCacheCheckout {
private boolean emptyDirCache;
/**
- * @return a list of updated paths and objectIds
+ * @return a list of updated paths and smudgeFilterCommands
*/
- public Map<String, ObjectId> getUpdated() {
+ public Map<String, String> getUpdated() {
return updated;
}
@@ -447,7 +452,8 @@ public class DirCacheCheckout {
for (String path : updated.keySet()) {
DirCacheEntry entry = dc.getEntry(path);
if (!FileMode.GITLINK.equals(entry.getRawMode()))
- checkoutEntry(repo, entry, objectReader, false);
+ checkoutEntry(repo, entry, objectReader, false,
+ updated.get(path));
}
// commit the index builder - a new index is persisted
@@ -996,9 +1002,12 @@ public class DirCacheCheckout {
removed.add(path);
}
- private void update(String path, ObjectId mId, FileMode mode) {
+ private void update(String path, ObjectId mId, FileMode mode)
+ throws IOException {
if (!FileMode.TREE.equals(mode)) {
- updated.put(path, mId);
+ updated.put(path,
+ walk.getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE));
+
DirCacheEntry entry = new DirCacheEntry(path, DirCacheEntry.STAGE_0);
entry.setObjectId(mId);
entry.setFileMode(mode);
@@ -1150,7 +1159,7 @@ public class DirCacheCheckout {
*/
public static void checkoutEntry(Repository repo, DirCacheEntry entry,
ObjectReader or) throws IOException {
- checkoutEntry(repo, entry, or, false);
+ checkoutEntry(repo, entry, or, false, null);
}
/**
@@ -1186,6 +1195,46 @@ public class DirCacheCheckout {
*/
public static void checkoutEntry(Repository repo, DirCacheEntry entry,
ObjectReader or, boolean deleteRecursive) throws IOException {
+ checkoutEntry(repo, entry, or, deleteRecursive, null);
+ }
+
+ /**
+ * Updates the file in the working tree with content and mode from an entry
+ * in the index. The new content is first written to a new temporary file in
+ * the same directory as the real file. Then that new file is renamed to the
+ * final filename.
+ *
+ * <p>
+ * <b>Note:</b> if the entry path on local file system exists as a file, it
+ * will be deleted and if it exists as a directory, it will be deleted
+ * recursively, independently if has any content.
+ * </p>
+ *
+ * <p>
+ * TODO: this method works directly on File IO, we may need another
+ * abstraction (like WorkingTreeIterator). This way we could tell e.g.
+ * Eclipse that Files in the workspace got changed
+ * </p>
+ *
+ * @param repo
+ * repository managing the destination work tree.
+ * @param entry
+ * the entry containing new mode and content
+ * @param or
+ * object reader to use for checkout
+ * @param deleteRecursive
+ * true to recursively delete final path if it exists on the file
+ * system
+ * @param smudgeFilterCommand
+ * the filter command to be run for smudging the entry to be
+ * checked out
+ *
+ * @throws IOException
+ * @since 4.2
+ */
+ public static void checkoutEntry(Repository repo, DirCacheEntry entry,
+ ObjectReader or, boolean deleteRecursive,
+ String smudgeFilterCommand) throws IOException {
ObjectLoader ol = or.open(entry.getObjectId());
File f = new File(repo.getWorkTree(), entry.getPathString());
File parentDir = f.getParentFile();
@@ -1210,14 +1259,52 @@ public class DirCacheCheckout {
OutputStream channel = new FileOutputStream(tmpFile);
if (opt.getAutoCRLF() == AutoCRLF.TRUE)
channel = new AutoCRLFOutputStream(channel);
- try {
- ol.copyTo(channel);
- } finally {
- channel.close();
+ if (smudgeFilterCommand != null) {
+ ProcessBuilder filterProcessBuilder = fs
+ .runInShell(smudgeFilterCommand, new String[0]);
+ filterProcessBuilder.directory(repo.getWorkTree());
+ filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY,
+ repo.getDirectory().getAbsolutePath());
+ ExecutionResult result;
+ int rc;
+ try {
+ // TODO: wire correctly with AUTOCRLF
+ result = fs.execute(filterProcessBuilder, ol.openStream());
+ rc = result.getRc();
+ if (rc == 0) {
+ result.getStdout().writeTo(channel,
+ NullProgressMonitor.INSTANCE);
+ }
+ } catch (IOException | InterruptedException e) {
+ throw new IOException(new FilterFailedException(e,
+ smudgeFilterCommand, entry.getPathString()));
+
+ } finally {
+ channel.close();
+ }
+ if (rc != 0) {
+ throw new IOException(new FilterFailedException(rc,
+ smudgeFilterCommand, entry.getPathString(),
+ result.getStdout().toByteArray(MAX_EXCEPTION_TEXT_SIZE),
+ RawParseUtils.decode(result.getStderr()
+ .toByteArray(MAX_EXCEPTION_TEXT_SIZE))));
+ }
+ } else {
+ try {
+ ol.copyTo(channel);
+ } finally {
+ channel.close();
+ }
+ }
+ // The entry needs to correspond to the on-disk filesize. If the content
+ // was filtered (either by autocrlf handling or smudge filters) ask the
+ // filesystem again for the length. Otherwise the objectloader knows the
+ // size
+ if (opt.getAutoCRLF() == AutoCRLF.TRUE || smudgeFilterCommand != null) {
+ entry.setLength(tmpFile.length());
+ } else {
+ entry.setLength(ol.getSize());
}
- entry.setLength(opt.getAutoCRLF() == AutoCRLF.TRUE ? //
- tmpFile.length() // AutoCRLF wants on-disk-size
- : (int) ol.getSize());
if (opt.isFileMode() && fs.supportsExecute()) {
if (FileMode.EXECUTABLE_FILE.equals(entry.getRawMode())) {
@@ -1255,24 +1342,6 @@ public class DirCacheCheckout {
checkValidPathSegment(chk, i);
}
- /**
- * Check if path is a valid path for a checked out file name or ref name.
- *
- * @param path
- * @throws InvalidPathException
- * if the path is invalid
- * @since 3.3
- */
- static void checkValidPath(String path) throws InvalidPathException {
- try {
- SystemReader.getInstance().checkPath(path);
- } catch (CorruptObjectException e) {
- InvalidPathException p = new InvalidPathException(path);
- p.initCause(e);
- throw p;
- }
- }
-
private static void checkValidPathSegment(ObjectChecker chk,
CanonicalTreeParser t) throws InvalidPathException {
try {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
index eef2e6d3c3..c8bc0960f4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
@@ -65,6 +65,7 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.MutableInteger;
import org.eclipse.jgit.util.NB;
+import org.eclipse.jgit.util.SystemReader;
/**
* A single file (or stage of a file) in a {@link DirCache}.
@@ -191,7 +192,7 @@ public class DirCacheEntry {
}
try {
- DirCacheCheckout.checkValidPath(toString(path));
+ checkPath(path);
} catch (InvalidPathException e) {
CorruptObjectException p =
new CorruptObjectException(e.getMessage());
@@ -263,7 +264,7 @@ public class DirCacheEntry {
/**
* Create an empty entry at the specified stage.
*
- * @param newPath
+ * @param path
* name of the cache entry, in the standard encoding.
* @param stage
* the stage index of the new entry.
@@ -274,16 +275,16 @@ public class DirCacheEntry {
* range 0..3, inclusive.
*/
@SuppressWarnings("boxing")
- public DirCacheEntry(final byte[] newPath, final int stage) {
- DirCacheCheckout.checkValidPath(toString(newPath));
+ public DirCacheEntry(byte[] path, final int stage) {
+ checkPath(path);
if (stage < 0 || 3 < stage)
throw new IllegalArgumentException(MessageFormat.format(
JGitText.get().invalidStageForPath,
- stage, toString(newPath)));
+ stage, toString(path)));
info = new byte[INFO_LEN];
infoOffset = 0;
- path = newPath;
+ this.path = path;
int flags = ((stage & 0x3) << 12);
if (path.length < NAME_MASK)
@@ -498,12 +499,16 @@ public class DirCacheEntry {
switch (mode.getBits() & FileMode.TYPE_MASK) {
case FileMode.TYPE_MISSING:
case FileMode.TYPE_TREE:
- throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidModeForPath
- , mode, getPathString()));
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().invalidModeForPath, mode, getPathString()));
}
NB.encodeInt32(info, infoOffset + P_MODE, mode.getBits());
}
+ void setFileMode(int mode) {
+ NB.encodeInt32(info, infoOffset + P_MODE, mode);
+ }
+
/**
* Get the cached creation time of this file, in milliseconds.
*
@@ -730,6 +735,16 @@ public class DirCacheEntry {
return 0;
}
+ private static void checkPath(byte[] path) {
+ try {
+ SystemReader.getInstance().checkPath(path);
+ } catch (CorruptObjectException e) {
+ InvalidPathException p = new InvalidPathException(toString(path));
+ p.initCause(e);
+ throw p;
+ }
+ }
+
private static String toString(final byte[] path) {
return Constants.CHARSET.decode(ByteBuffer.wrap(path)).toString();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java
index 354a07439a..ad93f7213f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java
@@ -103,9 +103,6 @@ public class DirCacheIterator extends AbstractTreeIterator {
/** The subtree containing {@link #currentEntry} if this is first entry. */
protected DirCacheTree currentSubtree;
- /** Holds an {@link AttributesNode} for the current entry */
- private AttributesNode attributesNode;
-
/**
* Create a new iterator for an already loaded DirCache instance.
* <p>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/IndexReadException.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/IndexReadException.java
new file mode 100644
index 0000000000..70f650dde6
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/IndexReadException.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015, Christian Halstrick <christian.halstrick@sap.com> and
+ * other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v1.0 which accompanies this
+ * distribution, is reproduced below, and is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.errors;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.internal.JGitText;
+
+/**
+ * Cannot read the index. This is a serious error that users need to be made
+ * aware of.
+ *
+ * @since 4.2
+ */
+public class IndexReadException extends IOException {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructs an IndexReadException with the default message.
+ */
+ public IndexReadException() {
+ super(JGitText.get().indexWriteException);
+ }
+
+ /**
+ * Constructs an IndexReadException with the specified detail message.
+ *
+ * @param s
+ * message
+ */
+ public IndexReadException(final String s) {
+ super(s);
+ }
+
+ /**
+ * Constructs an IndexReadException with the specified detail message.
+ *
+ * @param s
+ * message
+ * @param cause
+ * root cause exception
+ */
+ public IndexReadException(final String s, final Throwable cause) {
+ super(s);
+ initCause(cause);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
index 1d2d3bfaaf..ff9f233aa5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
@@ -42,6 +42,9 @@
*/
package org.eclipse.jgit.gitrepo;
+import static org.eclipse.jgit.lib.Constants.DEFAULT_REMOTE_NAME;
+import static org.eclipse.jgit.lib.Constants.R_REMOTES;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -574,7 +577,7 @@ public class RepoCommand extends GitCommand<RevCommit> {
private static String findRef(String ref, Repository repo)
throws IOException {
if (!ObjectId.isId(ref)) {
- Ref r = repo.getRef(Constants.DEFAULT_REMOTE_NAME + "/" + ref); //$NON-NLS-1$
+ Ref r = repo.exactRef(R_REMOTES + DEFAULT_REMOTE_NAME + "/" + ref); //$NON-NLS-1$
if (r != null)
return r.getName();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java
index 2e6582819f..a501fee901 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PrePushHook.java
@@ -113,6 +113,9 @@ public class PrePushHook extends GitHook<String> {
*/
@Override
protected String[] getParameters() {
+ if (remoteName == null) {
+ remoteName = remoteLocation;
+ }
return new String[] { remoteName, remoteLocation };
}
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 e39469bd8c..796eaaebf5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -79,6 +79,7 @@ public class JGitText extends TranslationBundle {
/***/ public String atLeastOnePathIsRequired;
/***/ public String atLeastOnePatternIsRequired;
/***/ public String atLeastTwoFiltersNeeded;
+ /***/ public String atomicPushNotSupported;
/***/ public String authenticationNotSupported;
/***/ public String badBase64InputCharacterAt;
/***/ public String badEntryDelimiter;
@@ -104,6 +105,7 @@ public class JGitText extends TranslationBundle {
/***/ public String cannotBeRecursiveWhenTreesAreIncluded;
/***/ public String cannotChangeActionOnComment;
/***/ public String cannotChangeToComment;
+ /***/ public String cannotCheckoutFromUnbornBranch;
/***/ public String cannotCheckoutOursSwitchBranch;
/***/ public String cannotCombineSquashWithNoff;
/***/ public String cannotCombineTreeFilterWithRevFilter;
@@ -146,6 +148,7 @@ public class JGitText extends TranslationBundle {
/***/ public String cannotReadCommit;
/***/ public String cannotReadFile;
/***/ public String cannotReadHEAD;
+ /***/ public String cannotReadIndex;
/***/ public String cannotReadObject;
/***/ public String cannotReadObjectsPath;
/***/ public String cannotReadTree;
@@ -338,6 +341,8 @@ public class JGitText extends TranslationBundle {
/***/ public String fileIsTooBigForThisConvenienceMethod;
/***/ public String fileIsTooLarge;
/***/ public String fileModeNotSetForPath;
+ /***/ public String filterExecutionFailed;
+ /***/ public String filterExecutionFailedRc;
/***/ public String findingGarbage;
/***/ public String flagIsDisposed;
/***/ public String flagNotFromThis;
@@ -410,6 +415,7 @@ public class JGitText extends TranslationBundle {
/***/ public String invalidURL;
/***/ public String invalidWildcards;
/***/ public String invalidRefSpec;
+ /***/ public String invalidRepositoryStateNoHead;
/***/ public String invalidWindowSize;
/***/ public String isAStaticFlagAndHasNorevWalkInstance;
/***/ public String JRELacksMD5Implementation;
@@ -509,6 +515,7 @@ public class JGitText extends TranslationBundle {
/***/ public String packfileIsTruncatedNoParam;
/***/ public String packHandleIsStale;
/***/ public String packHasUnresolvedDeltas;
+ /***/ public String packInaccessible;
/***/ public String packingCancelledDuringObjectsWriting;
/***/ public String packObjectCountMismatch;
/***/ public String packRefs;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java
index 122f6d3d19..0d5fd0f859 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java
@@ -44,8 +44,13 @@
package org.eclipse.jgit.internal.storage.dfs;
import java.io.IOException;
+import java.io.InputStream;
import java.text.MessageFormat;
+import java.util.Collections;
+import org.eclipse.jgit.attributes.AttributesNode;
+import org.eclipse.jgit.attributes.AttributesNodeProvider;
+import org.eclipse.jgit.attributes.AttributesRule;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.RefUpdate;
@@ -126,4 +131,36 @@ public abstract class DfsRepository extends Repository {
public ReflogReader getReflogReader(String refName) throws IOException {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public AttributesNodeProvider createAttributesNodeProvider() {
+ // TODO Check if the implementation used in FileRepository can be used
+ // for this kind of repository
+ return new EmptyAttributesNodeProvider();
+ }
+
+ private static class EmptyAttributesNodeProvider implements
+ AttributesNodeProvider {
+ private EmptyAttributesNode emptyAttributesNode = new EmptyAttributesNode();
+
+ public AttributesNode getInfoAttributesNode() throws IOException {
+ return emptyAttributesNode;
+ }
+
+ public AttributesNode getGlobalAttributesNode() throws IOException {
+ return emptyAttributesNode;
+ }
+
+ private static class EmptyAttributesNode extends AttributesNode {
+
+ public EmptyAttributesNode() {
+ super(Collections.<AttributesRule> emptyList());
+ }
+
+ @Override
+ public void parse(InputStream in) throws IOException {
+ // Do nothing
+ }
+ }
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java
index 832e4fb6a8..1c664b4097 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java
@@ -13,14 +13,22 @@ import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.pack.PackExt;
+import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Ref.Storage;
import org.eclipse.jgit.lib.SymbolicRef;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.ReceiveCommand;
import org.eclipse.jgit.util.RefList;
/**
@@ -46,8 +54,8 @@ public class InMemoryRepository extends DfsRepository {
static final AtomicInteger packId = new AtomicInteger();
private final DfsObjDatabase objdb;
-
private final DfsRefDatabase refdb;
+ private boolean performsAtomicTransactions = true;
/**
* Initialize a new in-memory repository.
@@ -76,6 +84,17 @@ public class InMemoryRepository extends DfsRepository {
return refdb;
}
+ /**
+ * Enable (or disable) the atomic reference transaction support.
+ * <p>
+ * Useful for testing atomic support enabled or disabled.
+ *
+ * @param atomic
+ */
+ public void setPerformsAtomicTransactions(boolean atomic) {
+ performsAtomicTransactions = atomic;
+ }
+
private class MemObjDatabase extends DfsObjDatabase {
private List<DfsPackDescription> packs = new ArrayList<DfsPackDescription>();
@@ -235,41 +254,143 @@ public class InMemoryRepository extends DfsRepository {
private class MemRefDatabase extends DfsRefDatabase {
private final ConcurrentMap<String, Ref> refs = new ConcurrentHashMap<String, Ref>();
+ private final ReadWriteLock lock = new ReentrantReadWriteLock(true /* fair */);
MemRefDatabase() {
super(InMemoryRepository.this);
}
@Override
+ public boolean performsAtomicTransactions() {
+ return performsAtomicTransactions;
+ }
+
+ @Override
+ public BatchRefUpdate newBatchUpdate() {
+ return new BatchRefUpdate(this) {
+ @Override
+ public void execute(RevWalk walk, ProgressMonitor monitor)
+ throws IOException {
+ if (performsAtomicTransactions()) {
+ try {
+ lock.writeLock().lock();
+ batch(walk, getCommands());
+ } finally {
+ lock.writeLock().unlock();
+ }
+ } else {
+ super.execute(walk, monitor);
+ }
+ }
+ };
+ }
+
+ @Override
protected RefCache scanAllRefs() throws IOException {
RefList.Builder<Ref> ids = new RefList.Builder<Ref>();
RefList.Builder<Ref> sym = new RefList.Builder<Ref>();
- for (Ref ref : refs.values()) {
- if (ref.isSymbolic())
- sym.add(ref);
- ids.add(ref);
+ try {
+ lock.readLock().lock();
+ for (Ref ref : refs.values()) {
+ if (ref.isSymbolic())
+ sym.add(ref);
+ ids.add(ref);
+ }
+ } finally {
+ lock.readLock().unlock();
}
ids.sort();
sym.sort();
return new RefCache(ids.toRefList(), sym.toRefList());
}
+ private void batch(RevWalk walk, List<ReceiveCommand> cmds) {
+ // Validate that the target exists in a new RevWalk, as the RevWalk
+ // from the RefUpdate might be reading back unflushed objects.
+ Map<ObjectId, ObjectId> peeled = new HashMap<>();
+ try (RevWalk rw = new RevWalk(getRepository())) {
+ for (ReceiveCommand c : cmds) {
+ if (!ObjectId.zeroId().equals(c.getNewId())) {
+ try {
+ RevObject o = rw.parseAny(c.getNewId());
+ if (o instanceof RevTag) {
+ peeled.put(o, rw.peel(o).copy());
+ }
+ } catch (IOException e) {
+ c.setResult(ReceiveCommand.Result.REJECTED_MISSING_OBJECT);
+ reject(cmds);
+ return;
+ }
+ }
+ }
+ }
+
+ // Check all references conform to expected old value.
+ for (ReceiveCommand c : cmds) {
+ Ref r = refs.get(c.getRefName());
+ if (r == null) {
+ if (c.getType() != ReceiveCommand.Type.CREATE) {
+ c.setResult(ReceiveCommand.Result.LOCK_FAILURE);
+ reject(cmds);
+ return;
+ }
+ } else if (r.isSymbolic() || r.getObjectId() == null
+ || !r.getObjectId().equals(c.getOldId())) {
+ c.setResult(ReceiveCommand.Result.LOCK_FAILURE);
+ reject(cmds);
+ return;
+ }
+ }
+
+ // Write references.
+ for (ReceiveCommand c : cmds) {
+ if (c.getType() == ReceiveCommand.Type.DELETE) {
+ refs.remove(c.getRefName());
+ c.setResult(ReceiveCommand.Result.OK);
+ continue;
+ }
+
+ ObjectId p = peeled.get(c.getNewId());
+ Ref r;
+ if (p != null) {
+ r = new ObjectIdRef.PeeledTag(Storage.PACKED,
+ c.getRefName(), c.getNewId(), p);
+ } else {
+ r = new ObjectIdRef.PeeledNonTag(Storage.PACKED,
+ c.getRefName(), c.getNewId());
+ }
+ refs.put(r.getName(), r);
+ c.setResult(ReceiveCommand.Result.OK);
+ }
+ clearCache();
+ }
+
+ private void reject(List<ReceiveCommand> cmds) {
+ for (ReceiveCommand c : cmds) {
+ if (c.getResult() == ReceiveCommand.Result.NOT_ATTEMPTED) {
+ c.setResult(ReceiveCommand.Result.REJECTED_OTHER_REASON,
+ JGitText.get().transactionAborted);
+ }
+ }
+ }
+
@Override
protected boolean compareAndPut(Ref oldRef, Ref newRef)
throws IOException {
- ObjectId id = newRef.getObjectId();
- if (id != null) {
- try (RevWalk rw = new RevWalk(getRepository())) {
- // Validate that the target exists in a new RevWalk, as the RevWalk
- // from the RefUpdate might be reading back unflushed objects.
- rw.parseAny(id);
+ try {
+ lock.writeLock().lock();
+ ObjectId id = newRef.getObjectId();
+ if (id != null) {
+ try (RevWalk rw = new RevWalk(getRepository())) {
+ // Validate that the target exists in a new RevWalk, as the RevWalk
+ // from the RefUpdate might be reading back unflushed objects.
+ rw.parseAny(id);
+ }
}
- }
- String name = newRef.getName();
- if (oldRef == null)
- return refs.putIfAbsent(name, newRef) == null;
+ String name = newRef.getName();
+ if (oldRef == null)
+ return refs.putIfAbsent(name, newRef) == null;
- synchronized (refs) {
Ref cur = refs.get(name);
Ref toCompare = cur;
if (toCompare != null) {
@@ -294,22 +415,29 @@ public class InMemoryRepository extends DfsRepository {
if (eq(toCompare, oldRef))
return refs.replace(name, cur, newRef);
}
- }
- if (oldRef.getStorage() == Storage.NEW)
- return refs.putIfAbsent(name, newRef) == null;
+ if (oldRef.getStorage() == Storage.NEW)
+ return refs.putIfAbsent(name, newRef) == null;
- return false;
+ return false;
+ } finally {
+ lock.writeLock().unlock();
+ }
}
@Override
protected boolean compareAndRemove(Ref oldRef) throws IOException {
- String name = oldRef.getName();
- Ref cur = refs.get(name);
- if (cur != null && eq(cur, oldRef))
- return refs.remove(name, cur);
- else
- return false;
+ try {
+ lock.writeLock().lock();
+ String name = oldRef.getName();
+ Ref cur = refs.get(name);
+ if (cur != null && eq(cur, oldRef))
+ return refs.remove(name, cur);
+ else
+ return false;
+ } finally {
+ lock.writeLock().unlock();
+ }
}
private boolean eq(Ref a, Ref b) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
index 995621ee3e..490cbcaa81 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
@@ -49,11 +49,15 @@ package org.eclipse.jgit.internal.storage.file;
import static org.eclipse.jgit.lib.RefDatabase.ALL;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.HashSet;
import java.util.Set;
+import org.eclipse.jgit.attributes.AttributesNode;
+import org.eclipse.jgit.attributes.AttributesNodeProvider;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.events.ConfigChangedEvent;
import org.eclipse.jgit.events.ConfigChangedListener;
@@ -479,4 +483,63 @@ public class FileRepository extends Repository {
return new ReflogReaderImpl(this, ref.getName());
return null;
}
+
+ @Override
+ public AttributesNodeProvider createAttributesNodeProvider() {
+ return new AttributesNodeProviderImpl(this);
+ }
+
+ /**
+ * Implementation a {@link AttributesNodeProvider} for a
+ * {@link FileRepository}.
+ *
+ * @author <a href="mailto:arthur.daussy@obeo.fr">Arthur Daussy</a>
+ *
+ */
+ static class AttributesNodeProviderImpl implements
+ AttributesNodeProvider {
+
+ private AttributesNode infoAttributesNode;
+
+ private AttributesNode globalAttributesNode;
+
+ /**
+ * Constructor.
+ *
+ * @param repo
+ * {@link Repository} that will provide the attribute nodes.
+ */
+ protected AttributesNodeProviderImpl(Repository repo) {
+ infoAttributesNode = new InfoAttributesNode(repo);
+ globalAttributesNode = new GlobalAttributesNode(repo);
+ }
+
+ public AttributesNode getInfoAttributesNode() throws IOException {
+ if (infoAttributesNode instanceof InfoAttributesNode)
+ infoAttributesNode = ((InfoAttributesNode) infoAttributesNode)
+ .load();
+ return infoAttributesNode;
+ }
+
+ public AttributesNode getGlobalAttributesNode() throws IOException {
+ if (globalAttributesNode instanceof GlobalAttributesNode)
+ globalAttributesNode = ((GlobalAttributesNode) globalAttributesNode)
+ .load();
+ return globalAttributesNode;
+ }
+
+ static void loadRulesFromFile(AttributesNode r, File attrs)
+ throws FileNotFoundException, IOException {
+ if (attrs.exists()) {
+ FileInputStream in = new FileInputStream(attrs);
+ try {
+ r.parse(in);
+ } finally {
+ in.close();
+ }
+ }
+ }
+
+ }
+
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
index e7005c247e..4c40538b6a 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
@@ -90,6 +90,7 @@ import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Ref.Storage;
import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.ReflogEntry;
+import org.eclipse.jgit.lib.ReflogReader;
import org.eclipse.jgit.revwalk.ObjectWalk;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -592,7 +593,11 @@ public class GC {
* @throws IOException
*/
private Set<ObjectId> listRefLogObjects(Ref ref, long minTime) throws IOException {
- List<ReflogEntry> rlEntries = repo.getReflogReader(ref.getName())
+ ReflogReader reflogReader = repo.getReflogReader(ref.getName());
+ if (reflogReader == null) {
+ return Collections.emptySet();
+ }
+ List<ReflogEntry> rlEntries = reflogReader
.getReverseEntries();
if (rlEntries == null || rlEntries.isEmpty())
return Collections.<ObjectId> emptySet();
@@ -635,10 +640,7 @@ public class GC {
*/
private Set<ObjectId> listNonHEADIndexObjects()
throws CorruptObjectException, IOException {
- try {
- if (repo.getIndexFile() == null)
- return Collections.emptySet();
- } catch (NoWorkTreeException e) {
+ if (repo.isBare()) {
return Collections.emptySet();
}
try (TreeWalk treeWalk = new TreeWalk(repo)) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GlobalAttributesNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GlobalAttributesNode.java
new file mode 100644
index 0000000000..454d3bff69
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GlobalAttributesNode.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014, Arthur Daussy <arthur.daussy@obeo.fr>
+ * Copyright (C) 2015, Christian Halstrick <christian.halstrick@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.storage.file;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.jgit.attributes.AttributesNode;
+import org.eclipse.jgit.lib.CoreConfig;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.util.FS;
+
+/** Attribute node loaded from global system-wide file. */
+public class GlobalAttributesNode extends AttributesNode {
+ final Repository repository;
+
+ /**
+ * @param repository
+ */
+ public GlobalAttributesNode(Repository repository) {
+ this.repository = repository;
+ }
+
+ /**
+ * @return the attributes node
+ * @throws IOException
+ */
+ public AttributesNode load() throws IOException {
+ AttributesNode r = new AttributesNode();
+
+ FS fs = repository.getFS();
+ String path = repository.getConfig().get(CoreConfig.KEY)
+ .getAttributesFile();
+ if (path != null) {
+ File attributesFile;
+ if (path.startsWith("~/")) { //$NON-NLS-1$
+ attributesFile = fs.resolve(fs.userHome(),
+ path.substring(2));
+ } else {
+ attributesFile = fs.resolve(null, path);
+ }
+ FileRepository.AttributesNodeProviderImpl.loadRulesFromFile(r, attributesFile);
+ }
+ return r.getRules().isEmpty() ? null : r;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java
new file mode 100644
index 0000000000..bda5cbeba4
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014, Arthur Daussy <arthur.daussy@obeo.fr>
+ * Copyright (C) 2015, Christian Halstrick <christian.halstrick@sap.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.storage.file;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.jgit.attributes.AttributesNode;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.util.FS;
+
+/** Attribute node loaded from the $GIT_DIR/info/attributes file. */
+public class InfoAttributesNode extends AttributesNode {
+ final Repository repository;
+
+ /**
+ * @param repository
+ */
+ public InfoAttributesNode(Repository repository) {
+ this.repository = repository;
+ }
+
+ /**
+ * @return the attributes node
+ * @throws IOException
+ */
+ public AttributesNode load() throws IOException {
+ AttributesNode r = new AttributesNode();
+
+ FS fs = repository.getFS();
+
+ File attributes = fs.resolve(repository.getDirectory(),
+ Constants.INFO_ATTRIBUTES);
+ FileRepository.AttributesNodeProviderImpl.loadRulesFromFile(r, attributes);
+
+ return r.getRules().isEmpty() ? null : r;
+ }
+
+} \ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
index 50297a97a0..e23ca741b8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LockFile.java
@@ -227,6 +227,10 @@ public class LockFile {
fis.close();
}
} catch (FileNotFoundException fnfe) {
+ if (ref.exists()) {
+ unlock();
+ throw fnfe;
+ }
// Don't worry about a file that doesn't exist yet, it
// conceptually has no current content to copy.
//
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
index e7ef127dd7..bd1d488d94 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
@@ -433,16 +433,14 @@ public class ObjectDirectory extends FileObjectDatabase {
ObjectLoader openLooseObject(WindowCursor curs, AnyObjectId id)
throws IOException {
- try {
- File path = fileFor(id);
- FileInputStream in = new FileInputStream(path);
- try {
- unpackedObjectCache.add(id);
- return UnpackedObject.open(in, path, id, curs);
- } finally {
- in.close();
- }
+ File path = fileFor(id);
+ try (FileInputStream in = new FileInputStream(path)) {
+ unpackedObjectCache.add(id);
+ return UnpackedObject.open(in, path, id, curs);
} catch (FileNotFoundException noFile) {
+ if (path.exists()) {
+ throw noFile;
+ }
unpackedObjectCache.remove(id);
return null;
}
@@ -513,15 +511,14 @@ public class ObjectDirectory extends FileObjectDatabase {
private long getLooseObjectSize(WindowCursor curs, AnyObjectId id)
throws IOException {
- try {
- FileInputStream in = new FileInputStream(fileFor(id));
- try {
- unpackedObjectCache.add(id);
- return UnpackedObject.getSize(in, id, curs);
- } finally {
- in.close();
- }
+ File f = fileFor(id);
+ try (FileInputStream in = new FileInputStream(f)) {
+ unpackedObjectCache.add(id);
+ return UnpackedObject.getSize(in, id, curs);
} catch (FileNotFoundException noFile) {
+ if (f.exists()) {
+ throw noFile;
+ }
unpackedObjectCache.remove(id);
return -1;
}
@@ -561,7 +558,11 @@ public class ObjectDirectory extends FileObjectDatabase {
// Assume the pack is corrupted, and remove it from the list.
removePack(p);
} else if (e instanceof FileNotFoundException) {
- warnTmpl = JGitText.get().packWasDeleted;
+ if (p.getPackFile().exists()) {
+ warnTmpl = JGitText.get().packInaccessible;
+ } else {
+ warnTmpl = JGitText.get().packWasDeleted;
+ }
removePack(p);
} else if (FileUtils.isStaleFileHandle(e)) {
warnTmpl = JGitText.get().packHandleIsStale;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
index 731bbd3839..69f7e97071 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
@@ -262,6 +262,30 @@ public class RefDirectory extends RefDatabase {
}
@Override
+ public Ref exactRef(String name) throws IOException {
+ RefList<Ref> packed = getPackedRefs();
+ Ref ref;
+ try {
+ ref = readRef(name, packed);
+ if (ref != null) {
+ ref = resolve(ref, 0, null, null, packed);
+ }
+ } catch (IOException e) {
+ if (name.contains("/") //$NON-NLS-1$
+ || !(e.getCause() instanceof InvalidObjectIdException)) {
+ throw e;
+ }
+
+ // While looking for a ref outside of refs/ (e.g., 'config'), we
+ // found a non-ref file (e.g., a config file) instead. Treat this
+ // as a ref-not-found condition.
+ ref = null;
+ }
+ fireRefsChanged();
+ return ref;
+ }
+
+ @Override
public Ref getRef(final String needle) throws IOException {
final RefList<Ref> packed = getPackedRefs();
Ref ref = null;
@@ -270,6 +294,8 @@ public class RefDirectory extends RefDatabase {
ref = readRef(prefix + needle, packed);
if (ref != null) {
ref = resolve(ref, 0, null, null, packed);
+ }
+ if (ref != null) {
break;
}
} catch (IOException e) {
@@ -762,6 +788,9 @@ public class RefDirectory extends RefDatabase {
new DigestInputStream(new FileInputStream(packedRefsFile),
digest), CHARSET));
} catch (FileNotFoundException noPackedRefs) {
+ if (packedRefsFile.exists()) {
+ throw noPackedRefs;
+ }
// Ignore it and leave the new list empty.
return PackedRefList.NO_PACKED_REFS;
}
@@ -918,7 +947,10 @@ public class RefDirectory extends RefDatabase {
try {
buf = IO.readSome(path, limit);
} catch (FileNotFoundException noFile) {
- return null; // doesn't exist; not a reference.
+ if (path.exists() && path.isFile()) {
+ throw noFile;
+ }
+ return null; // doesn't exist or no file; not a reference.
}
int n = buf.length;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java
index dadc631194..2f583b275a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ReflogReaderImpl.java
@@ -96,6 +96,9 @@ class ReflogReaderImpl implements ReflogReader {
try {
log = IO.readFully(logName);
} catch (FileNotFoundException e) {
+ if (logName.exists()) {
+ throw e;
+ }
return null;
}
@@ -118,6 +121,9 @@ class ReflogReaderImpl implements ReflogReader {
try {
log = IO.readFully(logName);
} catch (FileNotFoundException e) {
+ if (logName.exists()) {
+ throw e;
+ }
return Collections.emptyList();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java
index 2cc2563962..a02743720d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/UnpackedObject.java
@@ -399,6 +399,9 @@ public class UnpackedObject {
try {
in = buffer(new FileInputStream(path));
} catch (FileNotFoundException gone) {
+ if (path.exists()) {
+ throw gone;
+ }
// If the loose file no longer exists, it may have been
// moved into a pack file in the mean time. Try again
// to locate the object.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
index 535a6ee175..d30edaf41b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
@@ -273,6 +273,13 @@ public final class Constants {
public static final String INFO_EXCLUDE = "info/exclude";
/**
+ * Attributes-override-file
+ *
+ * @since 4.2
+ */
+ public static final String INFO_ATTRIBUTES = "info/attributes";
+
+ /**
* The system property that contains the system user name
*
* @since 3.6
@@ -363,6 +370,27 @@ public final class Constants {
*/
public static final String DOT_GIT_ATTRIBUTES = ".gitattributes";
+ /**
+ * Key for filters in .gitattributes
+ *
+ * @since 4.2
+ */
+ public static final String ATTR_FILTER = "filter";
+
+ /**
+ * clean command name, used to call filter driver
+ *
+ * @since 4.2
+ */
+ public static final String ATTR_FILTER_TYPE_CLEAN = "clean";
+
+ /**
+ * smudge command name, used to call filter driver
+ *
+ * @since 4.2
+ */
+ public static final String ATTR_FILTER_TYPE_SMUDGE = "smudge";
+
/** Name of the ignore file */
public static final String DOT_GIT_IGNORE = ".gitignore";
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
index 3fde2f919b..9e474f86a8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/IndexDiff.java
@@ -74,6 +74,7 @@ import org.eclipse.jgit.treewalk.EmptyTreeIterator;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.WorkingTreeIterator;
+import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
import org.eclipse.jgit.treewalk.filter.IndexDiffFilter;
import org.eclipse.jgit.treewalk.filter.SkipWorkTreeFilter;
@@ -403,6 +404,7 @@ public class IndexDiff {
dirCache = repository.readDirCache();
try (TreeWalk treeWalk = new TreeWalk(repository)) {
+ treeWalk.setOperationType(OperationType.CHECKIN_OP);
treeWalk.setRecursive(true);
// add the trees (tree, dirchache, workdir)
if (tree != null)
@@ -411,6 +413,7 @@ public class IndexDiff {
treeWalk.addTree(new EmptyTreeIterator());
treeWalk.addTree(new DirCacheIterator(dirCache));
treeWalk.addTree(initialWorkingTreeIterator);
+ initialWorkingTreeIterator.setDirCacheIterator(treeWalk, 1);
Collection<TreeFilter> filters = new ArrayList<TreeFilter>(4);
if (monitor != null) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
index ef22fb90fe..986666f2f4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
@@ -51,6 +51,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+
/**
* Abstraction of name to {@link ObjectId} mapping.
* <p>
@@ -132,6 +135,7 @@ public abstract class RefDatabase {
* @since 2.3
* @see #isNameConflicting(String)
*/
+ @NonNull
public Collection<String> getConflictingNames(String name)
throws IOException {
Map<String, Ref> allRefs = getRefs(ALL);
@@ -169,6 +173,7 @@ public abstract class RefDatabase {
* @throws IOException
* the reference space cannot be accessed.
*/
+ @NonNull
public abstract RefUpdate newUpdate(String name, boolean detach)
throws IOException;
@@ -183,6 +188,7 @@ public abstract class RefDatabase {
* @throws IOException
* the reference space cannot be accessed.
*/
+ @NonNull
public abstract RefRename newRename(String fromName, String toName)
throws IOException;
@@ -193,6 +199,7 @@ public abstract class RefDatabase {
*
* @return a new batch update object.
*/
+ @NonNull
public BatchRefUpdate newBatchUpdate() {
return new BatchRefUpdate(this);
}
@@ -223,6 +230,7 @@ public abstract class RefDatabase {
* @throws IOException
* the reference space cannot be accessed.
*/
+ @Nullable
public abstract Ref getRef(String name) throws IOException;
/**
@@ -238,21 +246,13 @@ public abstract class RefDatabase {
* the reference space cannot be accessed.
* @since 4.1
*/
+ @Nullable
public Ref exactRef(String name) throws IOException {
- int slash = name.lastIndexOf('/');
- String prefix = name.substring(0, slash + 1);
- String rest = name.substring(slash + 1);
- Ref result = getRefs(prefix).get(rest);
- if (result != null || slash != -1) {
- return result;
- }
-
- for (Ref ref : getAdditionalRefs()) {
- if (name.equals(ref.getName())) {
- return ref;
- }
+ Ref ref = getRef(name);
+ if (ref == null || !name.equals(ref.getName())) {
+ return null;
}
- return null;
+ return ref;
}
/**
@@ -270,6 +270,7 @@ public abstract class RefDatabase {
* the reference space cannot be accessed.
* @since 4.1
*/
+ @NonNull
public Map<String, Ref> exactRef(String... refs) throws IOException {
Map<String, Ref> result = new HashMap<>(refs.length);
for (String name : refs) {
@@ -294,6 +295,7 @@ public abstract class RefDatabase {
* the reference space cannot be accessed.
* @since 4.1
*/
+ @Nullable
public Ref firstExactRef(String... refs) throws IOException {
for (String name : refs) {
Ref ref = exactRef(name);
@@ -317,6 +319,7 @@ public abstract class RefDatabase {
* @throws IOException
* the reference space cannot be accessed.
*/
+ @NonNull
public abstract Map<String, Ref> getRefs(String prefix) throws IOException;
/**
@@ -331,6 +334,7 @@ public abstract class RefDatabase {
* @throws IOException
* the reference space cannot be accessed.
*/
+ @NonNull
public abstract List<Ref> getAdditionalRefs() throws IOException;
/**
@@ -347,10 +351,11 @@ public abstract class RefDatabase {
* @return {@code ref} if {@code ref.isPeeled()} is true; otherwise a new
* Ref object representing the same data as Ref, but isPeeled() will
* be true and getPeeledObjectId() will contain the peeled object
- * (or null).
+ * (or {@code null}).
* @throws IOException
* the reference space or object space cannot be accessed.
*/
+ @NonNull
public abstract Ref peel(Ref ref) throws IOException;
/**
@@ -374,9 +379,10 @@ public abstract class RefDatabase {
* @param name
* short name of ref to find, e.g. "master" to find
* "refs/heads/master" in map.
- * @return The first ref matching the name, or null if not found.
+ * @return The first ref matching the name, or {@code null} if not found.
* @since 3.4
*/
+ @Nullable
public static Ref findRef(Map<String, Ref> map, String name) {
for (String prefix : SEARCH_PATH) {
String fullname = prefix + name;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefRename.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefRename.java
index 05eb31183d..59f852b8c7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefRename.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefRename.java
@@ -170,7 +170,7 @@ public abstract class RefRename {
*/
protected boolean needToUpdateHEAD() throws IOException {
Ref head = source.getRefDatabase().getRef(Constants.HEAD);
- if (head.isSymbolic()) {
+ if (head != null && head.isSymbolic()) {
head = head.getTarget();
return head.getName().equals(source.getName());
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
index d4c72cb9cb..49a970d03a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -64,6 +64,9 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.attributes.AttributesNodeProvider;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.errors.AmbiguousObjectException;
import org.eclipse.jgit.errors.CorruptObjectException;
@@ -137,6 +140,7 @@ public abstract class Repository implements AutoCloseable {
}
/** @return listeners observing only events on this repository. */
+ @NonNull
public ListenerList getListenerList() {
return myListeners;
}
@@ -181,7 +185,16 @@ public abstract class Repository implements AutoCloseable {
*/
public abstract void create(boolean bare) throws IOException;
- /** @return local metadata directory; null if repository isn't local. */
+ /**
+ * @return local metadata directory; {@code null} if repository isn't local.
+ */
+ /*
+ * TODO This method should be annotated as Nullable, because in some
+ * specific configurations metadata is not located in the local file system
+ * (for example in memory databases). In "usual" repositories this
+ * annotation would only cause compiler errors at places where the actual
+ * directory can never be null.
+ */
public File getDirectory() {
return gitDir;
}
@@ -189,28 +202,52 @@ public abstract class Repository implements AutoCloseable {
/**
* @return the object database which stores this repository's data.
*/
+ @NonNull
public abstract ObjectDatabase getObjectDatabase();
/** @return a new inserter to create objects in {@link #getObjectDatabase()} */
+ @NonNull
public ObjectInserter newObjectInserter() {
return getObjectDatabase().newInserter();
}
/** @return a new reader to read objects from {@link #getObjectDatabase()} */
+ @NonNull
public ObjectReader newObjectReader() {
return getObjectDatabase().newReader();
}
/** @return the reference database which stores the reference namespace. */
+ @NonNull
public abstract RefDatabase getRefDatabase();
/**
* @return the configuration of this repository
*/
+ @NonNull
public abstract StoredConfig getConfig();
/**
- * @return the used file system abstraction
+ * @return a new {@link AttributesNodeProvider}. This
+ * {@link AttributesNodeProvider} is lazy loaded only once. It means
+ * that it will not be updated after loading. Prefer creating new
+ * instance for each use.
+ * @since 4.2
+ */
+ @NonNull
+ public abstract AttributesNodeProvider createAttributesNodeProvider();
+
+
+ /**
+ * @return the used file system abstraction, or or {@code null} if
+ * repository isn't local.
+ */
+ /*
+ * TODO This method should be annotated as Nullable, because in some
+ * specific configurations metadata is not located in the local file system
+ * (for example in memory databases). In "usual" repositories this
+ * annotation would only cause compiler errors at places where the actual
+ * directory can never be null.
*/
public FS getFS() {
return fs;
@@ -244,6 +281,7 @@ public abstract class Repository implements AutoCloseable {
* @throws IOException
* the object store cannot be accessed.
*/
+ @NonNull
public ObjectLoader open(final AnyObjectId objectId)
throws MissingObjectException, IOException {
return getObjectDatabase().open(objectId);
@@ -271,6 +309,7 @@ public abstract class Repository implements AutoCloseable {
* @throws IOException
* the object store cannot be accessed.
*/
+ @NonNull
public ObjectLoader open(AnyObjectId objectId, int typeHint)
throws MissingObjectException, IncorrectObjectTypeException,
IOException {
@@ -289,6 +328,7 @@ public abstract class Repository implements AutoCloseable {
* a symbolic ref was passed in and could not be resolved back
* to the base ref, as the symbolic ref could not be read.
*/
+ @NonNull
public RefUpdate updateRef(final String ref) throws IOException {
return updateRef(ref, false);
}
@@ -307,6 +347,7 @@ public abstract class Repository implements AutoCloseable {
* a symbolic ref was passed in and could not be resolved back
* to the base ref, as the symbolic ref could not be read.
*/
+ @NonNull
public RefUpdate updateRef(final String ref, final boolean detach) throws IOException {
return getRefDatabase().newUpdate(ref, detach);
}
@@ -323,6 +364,7 @@ public abstract class Repository implements AutoCloseable {
* the rename could not be performed.
*
*/
+ @NonNull
public RefRename renameRef(final String fromRef, final String toRef) throws IOException {
return getRefDatabase().newRename(fromRef, toRef);
}
@@ -362,7 +404,8 @@ public abstract class Repository implements AutoCloseable {
*
* @param revstr
* A git object references expression
- * @return an ObjectId or null if revstr can't be resolved to any ObjectId
+ * @return an ObjectId or {@code null} if revstr can't be resolved to any
+ * ObjectId
* @throws AmbiguousObjectException
* {@code revstr} contains an abbreviated ObjectId and this
* repository contains more than one object which match to the
@@ -376,6 +419,7 @@ public abstract class Repository implements AutoCloseable {
* @throws IOException
* on serious errors
*/
+ @Nullable
public ObjectId resolve(final String revstr)
throws AmbiguousObjectException, IncorrectObjectTypeException,
RevisionSyntaxException, IOException {
@@ -397,10 +441,12 @@ public abstract class Repository implements AutoCloseable {
* expects a branch or revision id.
*
* @param revstr
- * @return object id or ref name from resolved expression
+ * @return object id or ref name from resolved expression or {@code null} if
+ * given expression cannot be resolved
* @throws AmbiguousObjectException
* @throws IOException
*/
+ @Nullable
public String simplify(final String revstr)
throws AmbiguousObjectException, IOException {
try (RevWalk rw = new RevWalk(this)) {
@@ -414,6 +460,7 @@ public abstract class Repository implements AutoCloseable {
}
}
+ @Nullable
private Object resolve(final RevWalk rw, final String revstr)
throws IOException {
char[] revChars = revstr.toCharArray();
@@ -717,11 +764,13 @@ public abstract class Repository implements AutoCloseable {
return true;
}
+ @Nullable
private RevObject parseSimple(RevWalk rw, String revstr) throws IOException {
ObjectId id = resolveSimple(revstr);
return id != null ? rw.parseAny(id) : null;
}
+ @Nullable
private ObjectId resolveSimple(final String revstr) throws IOException {
if (ObjectId.isId(revstr))
return ObjectId.fromString(revstr);
@@ -749,6 +798,7 @@ public abstract class Repository implements AutoCloseable {
return null;
}
+ @Nullable
private String resolveReflogCheckout(int checkoutNo)
throws IOException {
ReflogReader reader = getReflogReader(Constants.HEAD);
@@ -790,6 +840,7 @@ public abstract class Repository implements AutoCloseable {
return rw.parseCommit(entry.getNewId());
}
+ @Nullable
private ObjectId resolveAbbreviation(final String revstr) throws IOException,
AmbiguousObjectException {
AbbreviatedObjectId id = AbbreviatedObjectId.fromString(revstr);
@@ -826,11 +877,13 @@ public abstract class Repository implements AutoCloseable {
getRefDatabase().close();
}
+ @NonNull
@SuppressWarnings("nls")
public String toString() {
String desc;
- if (getDirectory() != null)
- desc = getDirectory().getPath();
+ File directory = getDirectory();
+ if (directory != null)
+ desc = directory.getPath();
else
desc = getClass().getSimpleName() + "-" //$NON-NLS-1$
+ System.identityHashCode(this);
@@ -850,10 +903,12 @@ public abstract class Repository implements AutoCloseable {
* current ObjectId in hexadecimal string format.
*
* @return name of current branch (for example {@code refs/heads/master}),
- * an ObjectId in hex format if the current branch is detached,
- * or null if the repository is corrupt and has no HEAD reference.
+ * an ObjectId in hex format if the current branch is detached, or
+ * {@code null} if the repository is corrupt and has no HEAD
+ * reference.
* @throws IOException
*/
+ @Nullable
public String getFullBranch() throws IOException {
Ref head = getRef(Constants.HEAD);
if (head == null)
@@ -872,16 +927,17 @@ public abstract class Repository implements AutoCloseable {
* leading prefix {@code refs/heads/} is removed from the reference before
* it is returned to the caller.
*
- * @return name of current branch (for example {@code master}), an
- * ObjectId in hex format if the current branch is detached,
- * or null if the repository is corrupt and has no HEAD reference.
+ * @return name of current branch (for example {@code master}), an ObjectId
+ * in hex format if the current branch is detached, or {@code null}
+ * if the repository is corrupt and has no HEAD reference.
* @throws IOException
*/
+ @Nullable
public String getBranch() throws IOException {
String name = getFullBranch();
if (name != null)
return shortenRefName(name);
- return name;
+ return null;
}
/**
@@ -894,6 +950,7 @@ public abstract class Repository implements AutoCloseable {
*
* @return unmodifiable collection of other known objects.
*/
+ @NonNull
public Set<ObjectId> getAdditionalHaves() {
return Collections.emptySet();
}
@@ -905,16 +962,53 @@ public abstract class Repository implements AutoCloseable {
* the name of the ref to lookup. May be a short-hand form, e.g.
* "master" which is is automatically expanded to
* "refs/heads/master" if "refs/heads/master" already exists.
- * @return the Ref with the given name, or null if it does not exist
+ * @return the Ref with the given name, or {@code null} if it does not exist
* @throws IOException
+ * @deprecated Use {@link #exactRef(String)} or {@link #findRef(String)}
+ * instead.
*/
+ @Deprecated
+ @Nullable
public Ref getRef(final String name) throws IOException {
+ return findRef(name);
+ }
+
+ /**
+ * Get a ref by name.
+ *
+ * @param name
+ * the name of the ref to lookup. Must not be a short-hand
+ * form; e.g., "master" is not automatically expanded to
+ * "refs/heads/master".
+ * @return the Ref with the given name, or {@code null} if it does not exist
+ * @throws IOException
+ * @since 4.2
+ */
+ @Nullable
+ public Ref exactRef(String name) throws IOException {
+ return getRefDatabase().exactRef(name);
+ }
+
+ /**
+ * Search for a ref by (possibly abbreviated) name.
+ *
+ * @param name
+ * the name of the ref to lookup. May be a short-hand form, e.g.
+ * "master" which is is automatically expanded to
+ * "refs/heads/master" if "refs/heads/master" already exists.
+ * @return the Ref with the given name, or {@code null} if it does not exist
+ * @throws IOException
+ * @since 4.2
+ */
+ @Nullable
+ public Ref findRef(String name) throws IOException {
return getRefDatabase().getRef(name);
}
/**
* @return mutable map of all known refs (heads, tags, remotes).
*/
+ @NonNull
public Map<String, Ref> getAllRefs() {
try {
return getRefDatabase().getRefs(RefDatabase.ALL);
@@ -928,6 +1022,7 @@ public abstract class Repository implements AutoCloseable {
* of the entry contains the ref with the full tag name
* ("refs/tags/v1.0").
*/
+ @NonNull
public Map<String, Ref> getTags() {
try {
return getRefDatabase().getRefs(Constants.R_TAGS);
@@ -949,6 +1044,7 @@ public abstract class Repository implements AutoCloseable {
* will be true and getPeeledObjectId will contain the peeled object
* (or null).
*/
+ @NonNull
public Ref peel(final Ref ref) {
try {
return getRefDatabase().peel(ref);
@@ -963,6 +1059,7 @@ public abstract class Repository implements AutoCloseable {
/**
* @return a map with all objects referenced by a peeled ref.
*/
+ @NonNull
public Map<AnyObjectId, Set<Ref>> getAllRefsByPeeledObjectId() {
Map<String, Ref> allRefs = getAllRefs();
Map<AnyObjectId, Set<Ref>> ret = new HashMap<AnyObjectId, Set<Ref>>(allRefs.size());
@@ -987,11 +1084,13 @@ public abstract class Repository implements AutoCloseable {
}
/**
- * @return the index file location
+ * @return the index file location or {@code null} if repository isn't
+ * local.
* @throws NoWorkTreeException
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @NonNull
public File getIndexFile() throws NoWorkTreeException {
if (isBare())
throw new NoWorkTreeException();
@@ -1016,6 +1115,7 @@ public abstract class Repository implements AutoCloseable {
* the index file is using a format or extension that this
* library does not support.
*/
+ @NonNull
public DirCache readDirCache() throws NoWorkTreeException,
CorruptObjectException, IOException {
return DirCache.read(this);
@@ -1040,6 +1140,7 @@ public abstract class Repository implements AutoCloseable {
* the index file is using a format or extension that this
* library does not support.
*/
+ @NonNull
public DirCache lockDirCache() throws NoWorkTreeException,
CorruptObjectException, IOException {
// we want DirCache to inform us so that we can inform registered
@@ -1065,6 +1166,7 @@ public abstract class Repository implements AutoCloseable {
/**
* @return an important state
*/
+ @NonNull
public RepositoryState getRepositoryState() {
if (isBare() || getDirectory() == null)
return RepositoryState.BARE;
@@ -1207,6 +1309,7 @@ public abstract class Repository implements AutoCloseable {
* @return normalized repository relative path or the empty
* string if the file is not relative to the work directory.
*/
+ @NonNull
public static String stripWorkDir(File workDir, File file) {
final String filePath = file.getPath();
final String workDirPath = workDir.getPath();
@@ -1241,6 +1344,7 @@ public abstract class Repository implements AutoCloseable {
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @NonNull
public File getWorkTree() throws NoWorkTreeException {
if (isBare())
throw new NoWorkTreeException();
@@ -1264,6 +1368,7 @@ public abstract class Repository implements AutoCloseable {
*
* @return a more user friendly ref name
*/
+ @NonNull
public static String shortenRefName(String refName) {
if (refName.startsWith(Constants.R_HEADS))
return refName.substring(Constants.R_HEADS.length());
@@ -1279,9 +1384,10 @@ public abstract class Repository implements AutoCloseable {
* @return the remote branch name part of <code>refName</code>, i.e. without
* the <code>refs/remotes/&lt;remote&gt;</code> prefix, if
* <code>refName</code> represents a remote tracking branch;
- * otherwise null.
+ * otherwise {@code null}.
* @since 3.4
*/
+ @Nullable
public String shortenRemoteBranchName(String refName) {
for (String remote : getRemoteNames()) {
String remotePrefix = Constants.R_REMOTES + remote + "/"; //$NON-NLS-1$
@@ -1296,9 +1402,10 @@ public abstract class Repository implements AutoCloseable {
* @return the remote name part of <code>refName</code>, i.e. without the
* <code>refs/remotes/&lt;remote&gt;</code> prefix, if
* <code>refName</code> represents a remote tracking branch;
- * otherwise null.
+ * otherwise {@code null}.
* @since 3.4
*/
+ @Nullable
public String getRemoteName(String refName) {
for (String remote : getRemoteNames()) {
String remotePrefix = Constants.R_REMOTES + remote + "/"; //$NON-NLS-1$
@@ -1310,12 +1417,13 @@ public abstract class Repository implements AutoCloseable {
/**
* @param refName
- * @return a {@link ReflogReader} for the supplied refname, or null if the
- * named ref does not exist.
+ * @return a {@link ReflogReader} for the supplied refname, or {@code null}
+ * if the named ref does not exist.
* @throws IOException
* the ref could not be accessed.
* @since 3.0
*/
+ @Nullable
public abstract ReflogReader getReflogReader(String refName)
throws IOException;
@@ -1331,6 +1439,7 @@ public abstract class Repository implements AutoCloseable {
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @Nullable
public String readMergeCommitMsg() throws IOException, NoWorkTreeException {
return readCommitMsgFile(Constants.MERGE_MSG);
}
@@ -1365,6 +1474,7 @@ public abstract class Repository implements AutoCloseable {
* See {@link #isBare()}.
* @since 4.0
*/
+ @Nullable
public String readCommitEditMsg() throws IOException, NoWorkTreeException {
return readCommitMsgFile(Constants.COMMIT_EDITMSG);
}
@@ -1399,6 +1509,7 @@ public abstract class Repository implements AutoCloseable {
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @Nullable
public List<ObjectId> readMergeHeads() throws IOException, NoWorkTreeException {
if (isBare() || getDirectory() == null)
throw new NoWorkTreeException();
@@ -1442,6 +1553,7 @@ public abstract class Repository implements AutoCloseable {
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @Nullable
public ObjectId readCherryPickHead() throws IOException,
NoWorkTreeException {
if (isBare() || getDirectory() == null)
@@ -1465,6 +1577,7 @@ public abstract class Repository implements AutoCloseable {
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @Nullable
public ObjectId readRevertHead() throws IOException, NoWorkTreeException {
if (isBare() || getDirectory() == null)
throw new NoWorkTreeException();
@@ -1530,6 +1643,7 @@ public abstract class Repository implements AutoCloseable {
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @Nullable
public ObjectId readOrigHead() throws IOException, NoWorkTreeException {
if (isBare() || getDirectory() == null)
throw new NoWorkTreeException();
@@ -1550,6 +1664,7 @@ public abstract class Repository implements AutoCloseable {
* if this is bare, which implies it has no working directory.
* See {@link #isBare()}.
*/
+ @Nullable
public String readSquashCommitMsg() throws IOException {
return readCommitMsgFile(Constants.SQUASH_MSG);
}
@@ -1571,6 +1686,7 @@ public abstract class Repository implements AutoCloseable {
writeCommitMsg(squashMsgFile, msg);
}
+ @Nullable
private String readCommitMsgFile(String msgFilename) throws IOException {
if (isBare() || getDirectory() == null)
throw new NoWorkTreeException();
@@ -1579,6 +1695,9 @@ public abstract class Repository implements AutoCloseable {
try {
return RawParseUtils.decode(IO.readFully(mergeMsgFile));
} catch (FileNotFoundException e) {
+ if (mergeMsgFile.exists()) {
+ throw e;
+ }
// the file has disappeared in the meantime ignore it
return null;
}
@@ -1601,15 +1720,20 @@ public abstract class Repository implements AutoCloseable {
* Read a file from the git directory.
*
* @param filename
- * @return the raw contents or null if the file doesn't exist or is empty
+ * @return the raw contents or {@code null} if the file doesn't exist or is
+ * empty
* @throws IOException
*/
+ @Nullable
private byte[] readGitDirectoryFile(String filename) throws IOException {
File file = new File(getDirectory(), filename);
try {
byte[] raw = IO.readFully(file);
return raw.length > 0 ? raw : null;
} catch (FileNotFoundException notFound) {
+ if (file.exists()) {
+ throw notFound;
+ }
return null;
}
}
@@ -1657,6 +1781,7 @@ public abstract class Repository implements AutoCloseable {
* @throws IOException
* @since 3.2
*/
+ @NonNull
public List<RebaseTodoLine> readRebaseTodo(String path,
boolean includeComments)
throws IOException {
@@ -1686,6 +1811,7 @@ public abstract class Repository implements AutoCloseable {
* @return the names of all known remotes
* @since 3.4
*/
+ @NonNull
public Set<String> getRemoteNames() {
return getConfig()
.getSubsections(ConfigConstants.CONFIG_REMOTE_SECTION);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
index aef47c58cf..e0556447ce 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
@@ -57,8 +57,6 @@ import java.util.List;
import java.util.TimeZone;
import org.eclipse.jgit.dircache.DirCache;
-import org.eclipse.jgit.dircache.DirCacheBuilder;
-import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.NoMergeBaseException;
import org.eclipse.jgit.internal.JGitText;
@@ -70,7 +68,6 @@ import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.filter.RevFilter;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.EmptyTreeIterator;
-import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.WorkingTreeIterator;
/**
@@ -181,7 +178,7 @@ public class RecursiveMerger extends ResolveMerger {
WorkingTreeIterator oldWTreeIt = workingTreeIterator;
workingTreeIterator = null;
try {
- dircache = dircacheFromTree(currentBase.getTree());
+ dircache = DirCache.read(reader, currentBase.getTree());
inCore = true;
List<RevCommit> parents = new ArrayList<RevCommit>();
@@ -256,30 +253,4 @@ public class RecursiveMerger extends ResolveMerger {
new Date((time + 1) * 1000L),
TimeZone.getTimeZone("GMT+0000")); //$NON-NLS-1$
}
-
- /**
- * Create a new in memory dircache which has the same content as a given
- * tree.
- *
- * @param treeId
- * the tree which should be used to fill the dircache
- * @return a new in memory dircache
- * @throws IOException
- */
- private DirCache dircacheFromTree(ObjectId treeId) throws IOException {
- DirCache ret = DirCache.newInCore();
- DirCacheBuilder aBuilder = ret.builder();
- try (TreeWalk atw = new TreeWalk(reader)) {
- atw.addTree(treeId);
- atw.setRecursive(true);
- while (atw.next()) {
- DirCacheEntry e = new DirCacheEntry(atw.getRawPath());
- e.setFileMode(atw.getFileMode(0));
- e.setObjectId(atw.getObjectId(0));
- aBuilder.add(e);
- }
- }
- aBuilder.finish();
- return ret;
- }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
index 8a6343c3cc..de08e4b6a0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -786,11 +786,6 @@ public class ResolveMerger extends ThreeWayMerger {
private File writeMergedFile(MergeResult<RawText> result)
throws FileNotFoundException, IOException {
File workTree = db.getWorkTree();
- if (workTree == null)
- // TODO: This should be handled by WorkingTreeIterators which
- // support write operations
- throw new UnsupportedOperationException();
-
FS fs = db.getFS();
File of = new File(workTree, tw.getPathString());
File parentFolder = of.getParentFile();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
index 5509fc6a70..29c7f98411 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
@@ -163,6 +163,9 @@ public class FileBasedConfig extends StoredConfig {
hash = newHash;
}
} catch (FileNotFoundException noFile) {
+ if (configFile.exists()) {
+ throw noFile;
+ }
clear();
snapshot = newSnapshot;
} catch (IOException e) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
index 24fb3be64c..0834c359aa 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
@@ -44,6 +44,8 @@
package org.eclipse.jgit.transport;
+import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_ATOMIC;
+
import java.io.IOException;
import java.io.OutputStream;
import java.text.MessageFormat;
@@ -110,17 +112,15 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
public static final String CAPABILITY_SIDE_BAND_64K = GitProtocolConstants.CAPABILITY_SIDE_BAND_64K;
private final boolean thinPack;
+ private final boolean atomic;
+ private boolean capableAtomic;
private boolean capableDeleteRefs;
-
private boolean capableReport;
-
private boolean capableSideBand;
-
private boolean capableOfsDelta;
private boolean sentCommand;
-
private boolean writePack;
/** Time in milliseconds spent transferring the pack data. */
@@ -135,6 +135,7 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
public BasePackPushConnection(final PackTransport packTransport) {
super(packTransport);
thinPack = transport.isPushThin();
+ atomic = transport.isPushAtomic();
}
public void push(final ProgressMonitor monitor,
@@ -224,6 +225,11 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
private void writeCommands(final Collection<RemoteRefUpdate> refUpdates,
final ProgressMonitor monitor, OutputStream outputStream) throws IOException {
final String capabilities = enableCapabilities(monitor, outputStream);
+ if (atomic && !capableAtomic) {
+ throw new TransportException(uri,
+ JGitText.get().atomicPushNotSupported);
+ }
+
for (final RemoteRefUpdate rru : refUpdates) {
if (!capableDeleteRefs && rru.isDelete()) {
rru.setStatus(Status.REJECTED_NODELETE);
@@ -231,9 +237,11 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
}
final StringBuilder sb = new StringBuilder();
- final Ref advertisedRef = getRef(rru.getRemoteName());
- final ObjectId oldId = (advertisedRef == null ? ObjectId.zeroId()
- : advertisedRef.getObjectId());
+ ObjectId oldId = rru.getExpectedOldObjectId();
+ if (oldId == null) {
+ Ref adv = getRef(rru.getRemoteName());
+ oldId = adv != null ? adv.getObjectId() : ObjectId.zeroId();
+ }
sb.append(oldId.name());
sb.append(' ');
sb.append(rru.getNewObjectId().name());
@@ -259,6 +267,8 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
private String enableCapabilities(final ProgressMonitor monitor,
OutputStream outputStream) {
final StringBuilder line = new StringBuilder();
+ if (atomic)
+ capableAtomic = wantCapability(line, CAPABILITY_ATOMIC);
capableReport = wantCapability(line, CAPABILITY_REPORT_STATUS);
capableDeleteRefs = wantCapability(line, CAPABILITY_DELETE_REFS);
capableOfsDelta = wantCapability(line, CAPABILITY_OFS_DELTA);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java
index 7e9434a0f0..622680a27f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java
@@ -42,6 +42,7 @@
*/
package org.eclipse.jgit.transport;
+import java.io.File;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
@@ -85,12 +86,16 @@ public class HMACSHA1NonceGenerator implements NonceGenerator {
public synchronized String createNonce(Repository repo, long timestamp)
throws IllegalStateException {
String path;
- if (repo instanceof DfsRepository)
+ if (repo instanceof DfsRepository) {
path = ((DfsRepository) repo).getDescription().getRepositoryName();
- else if (repo.getDirectory() != null)
- path = repo.getDirectory().getPath();
- else
- throw new IllegalStateException();
+ } else {
+ File directory = repo.getDirectory();
+ if (directory != null) {
+ path = directory.getPath();
+ } else {
+ throw new IllegalStateException();
+ }
+ }
String input = path + ":" + String.valueOf(timestamp); //$NON-NLS-1$
byte[] rawHmac;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java
index 3594ea91b4..998f280014 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java
@@ -219,7 +219,8 @@ abstract class HttpAuthMethod {
if (credentialsProvider.supports(u, p)
&& credentialsProvider.get(uri, u, p)) {
username = u.getValue();
- password = new String(p.getValue());
+ char[] v = p.getValue();
+ password = (v == null) ? null : new String(p.getValue());
p.clear();
} else
return false;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
index 8947f2779d..d436e08df1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushCertificateStore.java
@@ -65,7 +65,6 @@ import java.util.Map;
import java.util.NoSuchElementException;
import org.eclipse.jgit.dircache.DirCache;
-import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEditor;
import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
import org.eclipse.jgit.dircache.DirCacheEntry;
@@ -448,13 +447,10 @@ public class PushCertificateStore implements AutoCloseable {
}
private DirCache newDirCache() throws IOException {
- DirCache dc = DirCache.newInCore();
if (commit != null) {
- DirCacheBuilder b = dc.builder();
- b.addTree(new byte[0], DirCacheEntry.STAGE_0, reader, commit.getTree());
- b.finish();
+ return DirCache.read(reader, commit.getTree());
}
- return dc;
+ return DirCache.newInCore();
}
private ObjectId saveCert(ObjectInserter inserter, DirCache dc,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java
index 9721ee9eb0..4fd192dbb2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PushProcess.java
@@ -47,6 +47,7 @@ import java.io.IOException;
import java.io.OutputStream;
import java.text.MessageFormat;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -183,6 +184,7 @@ class PushProcess {
private Map<String, RemoteRefUpdate> prepareRemoteUpdates()
throws TransportException {
+ boolean atomic = transport.isPushAtomic();
final Map<String, RemoteRefUpdate> result = new HashMap<String, RemoteRefUpdate>();
for (final RemoteRefUpdate rru : toPush.values()) {
final Ref advertisedRef = connection.getRef(rru.getRemoteName());
@@ -205,8 +207,14 @@ class PushProcess {
if (rru.isExpectingOldObjectId()
&& !rru.getExpectedOldObjectId().equals(advertisedOld)) {
rru.setStatus(Status.REJECTED_REMOTE_CHANGED);
+ if (atomic) {
+ return rejectAll();
+ }
continue;
}
+ if (!rru.isExpectingOldObjectId()) {
+ rru.setExpectedOldObjectId(advertisedOld);
+ }
// create ref (hasn't existed on remote side) and delete ref
// are always fast-forward commands, feasible at this level
@@ -236,14 +244,28 @@ class PushProcess {
JGitText.get().readingObjectsFromLocalRepositoryFailed, x.getMessage()), x);
}
rru.setFastForward(fastForward);
- if (!fastForward && !rru.isForceUpdate())
+ if (!fastForward && !rru.isForceUpdate()) {
rru.setStatus(Status.REJECTED_NONFASTFORWARD);
- else
+ if (atomic) {
+ return rejectAll();
+ }
+ } else {
result.put(rru.getRemoteName(), rru);
+ }
}
return result;
}
+ private Map<String, RemoteRefUpdate> rejectAll() {
+ for (RemoteRefUpdate rru : toPush.values()) {
+ if (rru.getStatus() == Status.NOT_ATTEMPTED) {
+ rru.setStatus(RemoteRefUpdate.Status.REJECTED_OTHER_REASON);
+ rru.setMessage(JGitText.get().transactionAborted);
+ }
+ }
+ return Collections.emptyMap();
+ }
+
private void modifyUpdatesForDryRun() {
for (final RemoteRefUpdate rru : toPush.values())
if (rru.getStatus() == Status.NOT_ATTEMPTED)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java
index 7c44dba4a2..5702b6d7b9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java
@@ -46,6 +46,7 @@ package org.eclipse.jgit.transport;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import org.eclipse.jgit.internal.JGitText;
@@ -127,26 +128,46 @@ public class ReceiveCommand {
}
/**
- * Filter a list of commands according to result.
+ * Filter a collection of commands according to result.
*
- * @param commands
+ * @param in
* commands to filter.
* @param want
* desired status to filter by.
* @return a copy of the command list containing only those commands with
* the desired status.
- * @since 2.0
+ * @since 4.2
*/
- public static List<ReceiveCommand> filter(List<ReceiveCommand> commands,
- final Result want) {
- List<ReceiveCommand> r = new ArrayList<ReceiveCommand>(commands.size());
- for (final ReceiveCommand cmd : commands) {
+ public static List<ReceiveCommand> filter(Iterable<ReceiveCommand> in,
+ Result want) {
+ List<ReceiveCommand> r;
+ if (in instanceof Collection)
+ r = new ArrayList<>(((Collection<?>) in).size());
+ else
+ r = new ArrayList<>();
+ for (ReceiveCommand cmd : in) {
if (cmd.getResult() == want)
r.add(cmd);
}
return r;
}
+ /**
+ * Filter a list of commands according to result.
+ *
+ * @param commands
+ * commands to filter.
+ * @param want
+ * desired status to filter by.
+ * @return a copy of the command list containing only those commands with
+ * the desired status.
+ * @since 2.0
+ */
+ public static List<ReceiveCommand> filter(List<ReceiveCommand> commands,
+ Result want) {
+ return filter((Iterable<ReceiveCommand>) commands, want);
+ }
+
private final ObjectId oldId;
private final ObjectId newId;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java
index 1b82a36105..5c58346189 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java
@@ -125,7 +125,7 @@ public class RemoteRefUpdate {
OK;
}
- private final ObjectId expectedOldObjectId;
+ private ObjectId expectedOldObjectId;
private final ObjectId newObjectId;
@@ -440,6 +440,10 @@ public class RemoteRefUpdate {
return message;
}
+ void setExpectedOldObjectId(ObjectId id) {
+ expectedOldObjectId = id;
+ }
+
void setStatus(final Status status) {
this.status = status;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
index cc7db47df5..6af153cbc9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
@@ -752,6 +752,9 @@ public abstract class Transport {
/** Should push produce thin-pack when sending objects to remote repository. */
private boolean pushThin = DEFAULT_PUSH_THIN;
+ /** Should push be all-or-nothing atomic behavior? */
+ private boolean pushAtomic;
+
/** Should push just check for operation result, not really push. */
private boolean dryRun;
@@ -970,6 +973,31 @@ public abstract class Transport {
}
/**
+ * Default setting is false.
+ *
+ * @return true if push requires all-or-nothing atomic behavior.
+ * @since 4.2
+ */
+ public boolean isPushAtomic() {
+ return pushAtomic;
+ }
+
+ /**
+ * Request atomic push (all references succeed, or none do).
+ * <p>
+ * Server must also support atomic push. If the server does not support the
+ * feature the push will abort without making changes.
+ *
+ * @param atomic
+ * true when push should be an all-or-nothing operation.
+ * @see PackTransport
+ * @since 4.2
+ */
+ public void setPushAtomic(final boolean atomic) {
+ this.pushAtomic = atomic;
+ }
+
+ /**
* @return true if destination refs should be removed if they no longer
* exist at the source repository.
*/
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
index 7729c11ff9..23c506b128 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
@@ -148,8 +148,9 @@ public class TransportAmazonS3 extends HttpTransport implements WalkTransport {
super(local, uri);
Properties props = loadProperties();
- if (!props.containsKey("tmpdir") && local.getDirectory() != null) //$NON-NLS-1$
- props.put("tmpdir", local.getDirectory().getPath()); //$NON-NLS-1$
+ File directory = local.getDirectory();
+ if (!props.containsKey("tmpdir") && directory != null) //$NON-NLS-1$
+ props.put("tmpdir", directory.getPath()); //$NON-NLS-1$
s3 = new AmazonS3(props);
bucket = uri.getHost();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
index b27fa0d6b2..52f0f04562 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportGitSsh.java
@@ -46,6 +46,7 @@
package org.eclipse.jgit.transport;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
@@ -235,9 +236,10 @@ public class TransportGitSsh extends SshTransport implements PackTransport {
ProcessBuilder pb = new ProcessBuilder();
pb.command(args);
- if (local.getDirectory() != null)
+ File directory = local.getDirectory();
+ if (directory != null)
pb.environment().put(Constants.GIT_DIR_KEY,
- local.getDirectory().getPath());
+ directory.getPath());
try {
return pb.start();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
index 3700b49555..9aeb840ebe 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/URIish.java
@@ -137,7 +137,11 @@ public class URIish implements Serializable {
+ OPT_PORT_P //
+ "(" // open a group capturing the user-home-dir-part //$NON-NLS-1$
+ (USER_HOME_P + "?") //$NON-NLS-1$
- + "[\\\\/])" //$NON-NLS-1$
+ + "(?:" // start non capturing group for host //$NON-NLS-1$
+ // separator or end of line
+ + "[\\\\/])|$" //$NON-NLS-1$
+ + ")" // close non capturing group for the host//$NON-NLS-1$
+ // separator or end of line
+ ")?" // close the optional group containing hostname //$NON-NLS-1$
+ "(.+)?" //$NON-NLS-1$
+ "$"); //$NON-NLS-1$
@@ -593,6 +597,8 @@ public class URIish implements Serializable {
private static boolean eq(final String a, final String b) {
if (a == b)
return true;
+ if (StringUtils.isEmptyOrNull(a) && StringUtils.isEmptyOrNull(b))
+ return true;
if (a == null || b == null)
return false;
return a.equals(b);
@@ -638,7 +644,7 @@ public class URIish implements Serializable {
if (getPath() != null) {
if (getScheme() != null) {
- if (!getPath().startsWith("/")) //$NON-NLS-1$
+ if (!getPath().startsWith("/") && !getPath().isEmpty()) //$NON-NLS-1$
r.append('/');
} else if (getHost() != null)
r.append(':');
@@ -709,9 +715,9 @@ public class URIish implements Serializable {
*/
public String getHumanishName() throws IllegalArgumentException {
String s = getPath();
- if ("/".equals(s)) //$NON-NLS-1$
+ if ("/".equals(s) || "".equals(s)) //$NON-NLS-1$
s = getHost();
- if ("".equals(s) || s == null) //$NON-NLS-1$
+ if (s == null) // $NON-NLS-1$
throw new IllegalArgumentException();
String[] elements;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java
index 41e593eaa2..5e71889574 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/AbstractTreeIterator.java
@@ -49,6 +49,7 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
+import org.eclipse.jgit.attributes.AttributesNode;
import org.eclipse.jgit.dircache.DirCacheCheckout;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -93,6 +94,13 @@ public abstract class AbstractTreeIterator {
AbstractTreeIterator matches;
/**
+ * Parsed rules of .gitattributes file if it exists.
+ *
+ * @since 4.2
+ */
+ protected AttributesNode attributesNode;
+
+ /**
* Number of entries we moved forward to force a D/F conflict match.
*
* @see NameConflictTreeWalk
@@ -320,6 +328,42 @@ public abstract class AbstractTreeIterator {
}
/**
+ * Seek the iterator on a file, if present.
+ *
+ * @param name
+ * file name to find (will not find a directory).
+ * @return true if the file exists in this tree; false otherwise.
+ * @throws CorruptObjectException
+ * tree is invalid.
+ * @since 4.2
+ */
+ public boolean findFile(String name) throws CorruptObjectException {
+ return findFile(Constants.encode(name));
+ }
+
+ /**
+ * Seek the iterator on a file, if present.
+ *
+ * @param name
+ * file name to find (will not find a directory).
+ * @return true if the file exists in this tree; false otherwise.
+ * @throws CorruptObjectException
+ * tree is invalid.
+ * @since 4.2
+ */
+ public boolean findFile(byte[] name) throws CorruptObjectException {
+ for (; !eof(); next(1)) {
+ int cmp = pathCompare(name, 0, name.length, 0, pathOffset);
+ if (cmp == 0) {
+ return true;
+ } else if (cmp > 0) {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ /**
* Compare the path of this current entry to a raw buffer.
*
* @param buf
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java
index 0805e5068c..c038f07725 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/CanonicalTreeParser.java
@@ -44,13 +44,23 @@
package org.eclipse.jgit.treewalk;
+import static org.eclipse.jgit.lib.Constants.DOT_GIT_ATTRIBUTES;
+import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
+import static org.eclipse.jgit.lib.Constants.TYPE_TREE;
+import static org.eclipse.jgit.lib.Constants.encode;
+
import java.io.IOException;
+import java.io.InputStream;
import java.util.Arrays;
+import java.util.Collections;
+import org.eclipse.jgit.attributes.AttributesNode;
+import org.eclipse.jgit.attributes.AttributesRule;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectId;
@@ -59,6 +69,7 @@ import org.eclipse.jgit.lib.ObjectReader;
/** Parses raw Git trees from the canonical semi-text/semi-binary format. */
public class CanonicalTreeParser extends AbstractTreeIterator {
private static final byte[] EMPTY = {};
+ private static final byte[] ATTRS = encode(DOT_GIT_ATTRIBUTES);
private byte[] raw;
@@ -124,6 +135,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
* the raw tree content.
*/
public void reset(final byte[] treeData) {
+ attributesNode = null;
raw = treeData;
prevPtr = -1;
currPtr = 0;
@@ -199,7 +211,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
*/
public void reset(final ObjectReader reader, final AnyObjectId id)
throws IncorrectObjectTypeException, IOException {
- reset(reader.open(id, Constants.OBJ_TREE).getCachedBytes());
+ reset(reader.open(id, OBJ_TREE).getCachedBytes());
}
@Override
@@ -209,7 +221,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
idBuffer.fromRaw(idBuffer(), idOffset());
if (!FileMode.TREE.equals(mode)) {
final ObjectId me = idBuffer.toObjectId();
- throw new IncorrectObjectTypeException(me, Constants.TYPE_TREE);
+ throw new IncorrectObjectTypeException(me, TYPE_TREE);
}
return createSubtreeIterator0(reader, idBuffer);
}
@@ -254,7 +266,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
@Override
public int idOffset() {
- return nextPtr - Constants.OBJECT_ID_LENGTH;
+ return nextPtr - OBJECT_ID_LENGTH;
}
@Override
@@ -292,7 +304,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
prevPtr = ptr;
while (raw[ptr] != 0)
ptr++;
- ptr += Constants.OBJECT_ID_LENGTH + 1;
+ ptr += OBJECT_ID_LENGTH + 1;
}
if (delta != 0)
throw new ArrayIndexOutOfBoundsException(delta);
@@ -328,7 +340,7 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
trace[delta] = ptr;
while (raw[ptr] != 0)
ptr++;
- ptr += Constants.OBJECT_ID_LENGTH + 1;
+ ptr += OBJECT_ID_LENGTH + 1;
}
if (trace[1] == -1)
throw new ArrayIndexOutOfBoundsException(delta);
@@ -363,6 +375,46 @@ public class CanonicalTreeParser extends AbstractTreeIterator {
}
}
pathLen = tmp;
- nextPtr = ptr + Constants.OBJECT_ID_LENGTH;
+ nextPtr = ptr + OBJECT_ID_LENGTH;
+ }
+
+ /**
+ * Retrieve the {@link AttributesNode} for the current entry.
+ *
+ * @param reader
+ * {@link ObjectReader} used to parse the .gitattributes entry.
+ * @return {@link AttributesNode} for the current entry.
+ * @throws IOException
+ * @since 4.2
+ */
+ public AttributesNode getEntryAttributesNode(ObjectReader reader)
+ throws IOException {
+ if (attributesNode == null) {
+ attributesNode = findAttributes(reader);
+ }
+ return attributesNode.getRules().isEmpty() ? null : attributesNode;
+ }
+
+ private AttributesNode findAttributes(ObjectReader reader)
+ throws IOException {
+ CanonicalTreeParser itr = new CanonicalTreeParser();
+ itr.reset(raw);
+ if (itr.findFile(ATTRS)) {
+ return loadAttributes(reader, itr.getEntryObjectId());
+ }
+ return noAttributes();
+ }
+
+ private static AttributesNode loadAttributes(ObjectReader reader,
+ AnyObjectId id) throws IOException {
+ AttributesNode r = new AttributesNode();
+ try (InputStream in = reader.open(id, OBJ_BLOB).openStream()) {
+ r.parse(in);
+ }
+ return r.getRules().isEmpty() ? noAttributes() : r;
+ }
+
+ private static AttributesNode noAttributes() {
+ return new AttributesNode(Collections.<AttributesRule> emptyList());
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
index 8d2cb1d8cd..accf4956f6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
@@ -67,8 +67,8 @@ import org.eclipse.jgit.util.FS;
*/
public class FileTreeIterator extends WorkingTreeIterator {
/**
- * the starting directory. This directory should correspond to the root of
- * the repository.
+ * the starting directory of this Iterator. All entries are located directly
+ * in this directory.
*/
protected final File directory;
@@ -238,8 +238,6 @@ public class FileTreeIterator extends WorkingTreeIterator {
@Override
protected byte[] idSubmodule(final Entry e) {
- if (repository == null)
- return idSubmodule(getDirectory(), e);
- return super.idSubmodule(e);
+ return idSubmodule(getDirectory(), e);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
index 2d6acbddf0..350f563964 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
@@ -96,7 +96,7 @@ public class NameConflictTreeWalk extends TreeWalk {
* the repository the walker will obtain data from.
*/
public NameConflictTreeWalk(final Repository repo) {
- this(repo.newObjectReader());
+ super(repo);
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
index 06e828419d..06dc0bf6d0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
@@ -45,12 +45,25 @@
package org.eclipse.jgit.treewalk;
import java.io.IOException;
-
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.attributes.Attribute;
+import org.eclipse.jgit.attributes.Attribute.State;
+import org.eclipse.jgit.attributes.Attributes;
+import org.eclipse.jgit.attributes.AttributesNode;
+import org.eclipse.jgit.attributes.AttributesNodeProvider;
+import org.eclipse.jgit.attributes.AttributesProvider;
+import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.StopWalkException;
import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.MutableObjectId;
@@ -60,6 +73,7 @@ import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
+import org.eclipse.jgit.util.QuotedString;
import org.eclipse.jgit.util.RawParseUtils;
/**
@@ -82,10 +96,45 @@ import org.eclipse.jgit.util.RawParseUtils;
* Multiple simultaneous TreeWalk instances per {@link Repository} are
* permitted, even from concurrent threads.
*/
-public class TreeWalk implements AutoCloseable {
+public class TreeWalk implements AutoCloseable, AttributesProvider {
private static final AbstractTreeIterator[] NO_TREES = {};
/**
+ * @since 4.2
+ */
+ public static enum OperationType {
+ /**
+ * Represents a checkout operation (for example a checkout or reset
+ * operation).
+ */
+ CHECKOUT_OP,
+
+ /**
+ * Represents a checkin operation (for example an add operation)
+ */
+ CHECKIN_OP
+ }
+
+ /**
+ * Type of operation you want to retrieve the git attributes for.
+ */
+ private OperationType operationType = OperationType.CHECKOUT_OP;
+
+ /**
+ * The filter command as defined in gitattributes. The keys are
+ * filterName+"."+filterCommandType. E.g. "lfs.clean"
+ */
+ private Map<String, String> filterCommandsByNameDotType = new HashMap<String, String>();
+
+ /**
+ * @param operationType
+ * @since 4.2
+ */
+ public void setOperationType(OperationType operationType) {
+ this.operationType = operationType;
+ }
+
+ /**
* Open a tree walk and filter to exactly one path.
* <p>
* The returned tree walk is already positioned on the requested path, so
@@ -213,8 +262,15 @@ public class TreeWalk implements AutoCloseable {
private boolean postChildren;
+ private AttributesNodeProvider attributesNodeProvider;
+
AbstractTreeIterator currentHead;
+ /** Cached attribute for the current entry */
+ private Attributes attrs = null;
+
+ private Config config;
+
/**
* Create a new tree walker for a given repository.
*
@@ -225,6 +281,8 @@ public class TreeWalk implements AutoCloseable {
*/
public TreeWalk(final Repository repo) {
this(repo.newObjectReader(), true);
+ config = repo.getConfig();
+ attributesNodeProvider = repo.createAttributesNodeProvider();
}
/**
@@ -356,8 +414,29 @@ public class TreeWalk implements AutoCloseable {
postOrderTraversal = b;
}
+ /**
+ * Sets the {@link AttributesNodeProvider} for this {@link TreeWalk}.
+ * <p>
+ * This is a requirement for a correct computation of the git attributes.
+ * If this {@link TreeWalk} has been built using
+ * {@link #TreeWalk(Repository)} constructor, the
+ * {@link AttributesNodeProvider} has already been set. Indeed,the
+ * {@link Repository} can provide an {@link AttributesNodeProvider} using
+ * {@link Repository#createAttributesNodeProvider()} method. Otherwise you
+ * should provide one.
+ * </p>
+ *
+ * @see Repository#createAttributesNodeProvider()
+ * @param provider
+ * @since 4.2
+ */
+ public void setAttributesNodeProvider(AttributesNodeProvider provider) {
+ attributesNodeProvider = provider;
+ }
+
/** Reset this walker so new tree iterators can be added to it. */
public void reset() {
+ attrs = null;
trees = NO_TREES;
advance = false;
depth = 0;
@@ -401,6 +480,7 @@ public class TreeWalk implements AutoCloseable {
advance = false;
depth = 0;
+ attrs = null;
}
/**
@@ -450,6 +530,7 @@ public class TreeWalk implements AutoCloseable {
trees = r;
advance = false;
depth = 0;
+ attrs = null;
}
/**
@@ -546,6 +627,7 @@ public class TreeWalk implements AutoCloseable {
public boolean next() throws MissingObjectException,
IncorrectObjectTypeException, CorruptObjectException, IOException {
try {
+ attrs = null;
if (advance) {
advance = false;
postChildren = false;
@@ -915,6 +997,7 @@ public class TreeWalk implements AutoCloseable {
*/
public void enterSubtree() throws MissingObjectException,
IncorrectObjectTypeException, CorruptObjectException, IOException {
+ attrs = null;
final AbstractTreeIterator ch = currentHead;
final AbstractTreeIterator[] tmp = new AbstractTreeIterator[trees.length];
for (int i = 0; i < trees.length; i++) {
@@ -1008,4 +1091,296 @@ public class TreeWalk implements AutoCloseable {
static String pathOf(final byte[] buf, int pos, int end) {
return RawParseUtils.decode(Constants.CHARSET, buf, pos, end);
}
+
+ /**
+ * Retrieve the git attributes for the current entry.
+ *
+ * <h4>Git attribute computation</h4>
+ *
+ * <ul>
+ * <li>Get the attributes matching the current path entry from the info file
+ * (see {@link AttributesNodeProvider#getInfoAttributesNode()}).</li>
+ * <li>Completes the list of attributes using the .gitattributes files
+ * located on the current path (the further the directory that contains
+ * .gitattributes is from the path in question, the lower its precedence).
+ * For a checkin operation, it will look first on the working tree (if any).
+ * If there is no attributes file, it will fallback on the index. For a
+ * checkout operation, it will first use the index entry and then fallback
+ * on the working tree if none.</li>
+ * <li>In the end, completes the list of matching attributes using the
+ * global attribute file define in the configuration (see
+ * {@link AttributesNodeProvider#getGlobalAttributesNode()})</li>
+ *
+ * </ul>
+ *
+ *
+ * <h4>Iterator constraints</h4>
+ *
+ * <p>
+ * In order to have a correct list of attributes for the current entry, this
+ * {@link TreeWalk} requires to have at least one
+ * {@link AttributesNodeProvider} and a {@link DirCacheIterator} set up. An
+ * {@link AttributesNodeProvider} is used to retrieve the attributes from
+ * the info attributes file and the global attributes file. The
+ * {@link DirCacheIterator} is used to retrieve the .gitattributes files
+ * stored in the index. A {@link WorkingTreeIterator} can also be provided
+ * to access the local version of the .gitattributes files. If none is
+ * provided it will fallback on the {@link DirCacheIterator}.
+ * </p>
+ *
+ * @return a {@link Set} of {@link Attribute}s that match the current entry.
+ * @since 4.2
+ */
+ public Attributes getAttributes() {
+ if (attrs != null)
+ return attrs;
+
+ if (attributesNodeProvider == null) {
+ // The work tree should have a AttributesNodeProvider to be able to
+ // retrieve the info and global attributes node
+ throw new IllegalStateException(
+ "The tree walk should have one AttributesNodeProvider set in order to compute the git attributes."); //$NON-NLS-1$
+ }
+
+ WorkingTreeIterator workingTreeIterator = getTree(WorkingTreeIterator.class);
+ DirCacheIterator dirCacheIterator = getTree(DirCacheIterator.class);
+ CanonicalTreeParser other = getTree(CanonicalTreeParser.class);
+
+ if (workingTreeIterator == null && dirCacheIterator == null
+ && other == null) {
+ // Can not retrieve the attributes without at least one of the above
+ // iterators.
+ return new Attributes();
+ }
+
+ String path = currentHead.getEntryPathString();
+ final boolean isDir = FileMode.TREE.equals(currentHead.mode);
+ Attributes attributes = new Attributes();
+ try {
+ // Gets the global attributes node
+ AttributesNode globalNodeAttr = attributesNodeProvider
+ .getGlobalAttributesNode();
+ // Gets the info attributes node
+ AttributesNode infoNodeAttr = attributesNodeProvider
+ .getInfoAttributesNode();
+
+ // Gets the info attributes
+ if (infoNodeAttr != null) {
+ infoNodeAttr.getAttributes(path, isDir, attributes);
+ }
+
+ // Gets the attributes located on the current entry path
+ getPerDirectoryEntryAttributes(path, isDir, operationType,
+ workingTreeIterator, dirCacheIterator, other, attributes);
+
+ // Gets the attributes located in the global attribute file
+ if (globalNodeAttr != null) {
+ globalNodeAttr.getAttributes(path, isDir, attributes);
+ }
+ } catch (IOException e) {
+ throw new JGitInternalException("Error while parsing attributes", e); //$NON-NLS-1$
+ }
+ // now after all attributes are collected - in the correct hierarchy
+ // order - remove all unspecified entries (the ! marker)
+ for (Attribute a : attributes.getAll()) {
+ if (a.getState() == State.UNSPECIFIED)
+ attributes.remove(a.getKey());
+ }
+ return attributes;
+ }
+
+ /**
+ * Get the attributes located on the current entry path.
+ *
+ * @param path
+ * current entry path
+ * @param isDir
+ * holds true if the current entry is a directory
+ * @param opType
+ * type of operation
+ * @param workingTreeIterator
+ * a {@link WorkingTreeIterator} matching the current entry
+ * @param dirCacheIterator
+ * a {@link DirCacheIterator} matching the current entry
+ * @param other
+ * a {@link CanonicalTreeParser} matching the current entry
+ * @param attributes
+ * Non null map holding the existing attributes. This map will be
+ * augmented with new entry. None entry will be overrided.
+ * @throws IOException
+ * It raises an {@link IOException} if a problem appears while
+ * parsing one on the attributes file.
+ */
+ private void getPerDirectoryEntryAttributes(String path, boolean isDir,
+ OperationType opType, WorkingTreeIterator workingTreeIterator,
+ DirCacheIterator dirCacheIterator, CanonicalTreeParser other,
+ Attributes attributes)
+ throws IOException {
+ // Prevents infinite recurrence
+ if (workingTreeIterator != null || dirCacheIterator != null
+ || other != null) {
+ AttributesNode currentAttributesNode = getCurrentAttributesNode(
+ opType, workingTreeIterator, dirCacheIterator, other);
+ if (currentAttributesNode != null) {
+ currentAttributesNode.getAttributes(path, isDir, attributes);
+ }
+ getPerDirectoryEntryAttributes(path, isDir, opType,
+ getParent(workingTreeIterator, WorkingTreeIterator.class),
+ getParent(dirCacheIterator, DirCacheIterator.class),
+ getParent(other, CanonicalTreeParser.class), attributes);
+ }
+ }
+
+ private static <T extends AbstractTreeIterator> T getParent(T current,
+ Class<T> type) {
+ if (current != null) {
+ AbstractTreeIterator parent = current.parent;
+ if (type.isInstance(parent)) {
+ return type.cast(parent);
+ }
+ }
+ return null;
+ }
+
+ private <T extends AbstractTreeIterator> T getTree(Class<T> type) {
+ for (int i = 0; i < trees.length; i++) {
+ AbstractTreeIterator tree = trees[i];
+ if (type.isInstance(tree)) {
+ return type.cast(tree);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the {@link AttributesNode} for the current entry.
+ * <p>
+ * This method implements the fallback mechanism between the index and the
+ * working tree depending on the operation type
+ * </p>
+ *
+ * @param opType
+ * @param workingTreeIterator
+ * @param dirCacheIterator
+ * @param other
+ * @return a {@link AttributesNode} of the current entry,
+ * {@link NullPointerException} otherwise.
+ * @throws IOException
+ * It raises an {@link IOException} if a problem appears while
+ * parsing one on the attributes file.
+ */
+ private AttributesNode getCurrentAttributesNode(OperationType opType,
+ @Nullable WorkingTreeIterator workingTreeIterator,
+ @Nullable DirCacheIterator dirCacheIterator,
+ @Nullable CanonicalTreeParser other)
+ throws IOException {
+ AttributesNode attributesNode = null;
+ switch (opType) {
+ case CHECKIN_OP:
+ if (workingTreeIterator != null) {
+ attributesNode = workingTreeIterator.getEntryAttributesNode();
+ }
+ if (attributesNode == null && dirCacheIterator != null) {
+ attributesNode = getAttributesNode(dirCacheIterator
+ .getEntryAttributesNode(getObjectReader()),
+ attributesNode);
+ }
+ if (attributesNode == null && other != null) {
+ attributesNode = getAttributesNode(
+ other.getEntryAttributesNode(getObjectReader()),
+ attributesNode);
+ }
+ break;
+ case CHECKOUT_OP:
+ if (other != null) {
+ attributesNode = other
+ .getEntryAttributesNode(getObjectReader());
+ }
+ if (dirCacheIterator != null) {
+ attributesNode = getAttributesNode(dirCacheIterator
+ .getEntryAttributesNode(getObjectReader()),
+ attributesNode);
+ }
+ if (attributesNode == null && workingTreeIterator != null) {
+ attributesNode = getAttributesNode(
+ workingTreeIterator.getEntryAttributesNode(),
+ attributesNode);
+ }
+ break;
+ default:
+ throw new IllegalStateException(
+ "The only supported operation types are:" //$NON-NLS-1$
+ + OperationType.CHECKIN_OP + "," //$NON-NLS-1$
+ + OperationType.CHECKOUT_OP);
+ }
+
+ return attributesNode;
+ }
+
+ private static AttributesNode getAttributesNode(AttributesNode value,
+ AttributesNode defaultValue) {
+ return (value == null) ? defaultValue : value;
+ }
+
+ /**
+ * Inspect config and attributes to return a filtercommand applicable for
+ * the current path
+ *
+ * @param filterCommandType
+ * which type of filterCommand should be executed. E.g. "clean",
+ * "smudge"
+ * @return a filter command
+ * @throws IOException
+ * @since 4.2
+ */
+ public String getFilterCommand(String filterCommandType)
+ throws IOException {
+ Attributes attributes = getAttributes();
+
+ Attribute f = attributes.get(Constants.ATTR_FILTER);
+ if (f == null) {
+ return null;
+ }
+ String filterValue = f.getValue();
+ if (filterValue == null) {
+ return null;
+ }
+
+ String filterCommand = getFilterCommandDefinition(filterValue,
+ filterCommandType);
+ if (filterCommand == null) {
+ return null;
+ }
+ return filterCommand.replaceAll("%f", //$NON-NLS-1$
+ QuotedString.BOURNE.quote((getPathString())));
+ }
+
+ /**
+ * Get the filter command how it is defined in gitconfig. The returned
+ * string may contain "%f" which needs to be replaced by the current path
+ * before executing the filter command. These filter definitions are cached
+ * for better performance.
+ *
+ * @param filterDriverName
+ * The name of the filter driver as it is referenced in the
+ * gitattributes file. E.g. "lfs". For each filter driver there
+ * may be many commands defined in the .gitconfig
+ * @param filterCommandType
+ * The type of the filter command for a specific filter driver.
+ * May be "clean" or "smudge".
+ * @return the definition of the command to be executed for this filter
+ * driver and filter command
+ */
+ private String getFilterCommandDefinition(String filterDriverName,
+ String filterCommandType) {
+ String key = filterDriverName + "." + filterCommandType; //$NON-NLS-1$
+ String filterCommand = filterCommandsByNameDotType.get(key);
+ if (filterCommand != null)
+ return filterCommand;
+ filterCommand = config.getString(Constants.ATTR_FILTER,
+ filterDriverName, filterCommandType);
+ if (filterCommand != null)
+ filterCommandsByNameDotType.put(key, filterCommand);
+ return filterCommand;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
index 73ab04f9cb..94beeeb56f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -62,6 +62,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import org.eclipse.jgit.api.errors.FilterFailedException;
import org.eclipse.jgit.attributes.AttributesNode;
import org.eclipse.jgit.attributes.AttributesRule;
import org.eclipse.jgit.diff.RawText;
@@ -76,6 +77,7 @@ import org.eclipse.jgit.ignore.IgnoreNode;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.CoreConfig;
+import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
import org.eclipse.jgit.lib.CoreConfig.CheckStat;
import org.eclipse.jgit.lib.CoreConfig.SymLinks;
import org.eclipse.jgit.lib.FileMode;
@@ -85,6 +87,7 @@ import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.submodule.SubmoduleWalk;
import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.FS.ExecutionResult;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
import org.eclipse.jgit.util.io.EolCanonicalizingInputStream;
@@ -101,6 +104,8 @@ import org.eclipse.jgit.util.io.EolCanonicalizingInputStream;
* @see FileTreeIterator
*/
public abstract class WorkingTreeIterator extends AbstractTreeIterator {
+ private static final int MAX_EXCEPTION_TEXT_SIZE = 10 * 1024;
+
/** An empty entry array, suitable for {@link #init(Entry[])}. */
protected static final Entry[] EOF = {};
@@ -134,8 +139,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
/** If there is a .gitignore file present, the parsed rules from it. */
private IgnoreNode ignoreNode;
- /** If there is a .gitattributes file present, the parsed rules from it. */
- private AttributesNode attributesNode;
+ private String cleanFilterCommand;
/** Repository that is the root level being iterated over */
protected Repository repository;
@@ -147,19 +151,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
private int contentIdOffset;
/**
- * Holds the {@link AttributesNode} that is stored in
- * $GIT_DIR/info/attributes file.
- */
- private AttributesNode infoAttributeNode;
-
- /**
- * Holds the {@link AttributesNode} that is stored in global attribute file.
- *
- * @see CoreConfig#getAttributesFile()
- */
- private AttributesNode globalAttributeNode;
-
- /**
* Create a new iterator with no parent.
*
* @param options
@@ -202,8 +193,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
protected WorkingTreeIterator(final WorkingTreeIterator p) {
super(p);
state = p.state;
- infoAttributeNode = p.infoAttributeNode;
- globalAttributeNode = p.globalAttributeNode;
+ repository = p.repository;
}
/**
@@ -223,10 +213,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
else
entry = null;
ignoreNode = new RootIgnoreNode(entry, repo);
-
- infoAttributeNode = new InfoAttributesNode(repo);
-
- globalAttributeNode = new GlobalAttributesNode(repo);
}
/**
@@ -370,7 +356,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
private InputStream possiblyFilteredInputStream(final Entry e,
final InputStream is, final long len) throws IOException {
- if (!mightNeedCleaning()) {
+ boolean mightNeedCleaning = mightNeedCleaning();
+ if (!mightNeedCleaning) {
canonLen = len;
return is;
}
@@ -388,7 +375,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
return new ByteArrayInputStream(raw, 0, n);
}
- if (isBinary(e)) {
+ // TODO: fix autocrlf causing mightneedcleaning
+ if (!mightNeedCleaning && isBinary(e)) {
canonLen = len;
return is;
}
@@ -412,10 +400,12 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
}
}
- private boolean mightNeedCleaning() {
+ private boolean mightNeedCleaning() throws IOException {
switch (getOptions().getAutoCRLF()) {
case FALSE:
default:
+ if (getCleanFilterCommand() != null)
+ return true;
return false;
case TRUE:
@@ -437,8 +427,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
}
}
- private static ByteBuffer filterClean(byte[] src, int n)
- throws IOException {
+ private ByteBuffer filterClean(byte[] src, int n) throws IOException {
InputStream in = new ByteArrayInputStream(src);
try {
return IO.readWholeStream(filterClean(in), n);
@@ -447,8 +436,42 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
}
}
- private static InputStream filterClean(InputStream in) {
- return new EolCanonicalizingInputStream(in, true);
+ private InputStream filterClean(InputStream in) throws IOException {
+ in = handleAutoCRLF(in);
+ String filterCommand = getCleanFilterCommand();
+ if (filterCommand != null) {
+ FS fs = repository.getFS();
+ ProcessBuilder filterProcessBuilder = fs.runInShell(filterCommand,
+ new String[0]);
+ filterProcessBuilder.directory(repository.getWorkTree());
+ filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY,
+ repository.getDirectory().getAbsolutePath());
+ ExecutionResult result;
+ try {
+ result = fs.execute(filterProcessBuilder, in);
+ } catch (IOException | InterruptedException e) {
+ throw new IOException(new FilterFailedException(e,
+ filterCommand, getEntryPathString()));
+ }
+ int rc = result.getRc();
+ if (rc != 0) {
+ throw new IOException(new FilterFailedException(rc,
+ filterCommand, getEntryPathString(),
+ result.getStdout().toByteArray(MAX_EXCEPTION_TEXT_SIZE),
+ RawParseUtils.decode(result.getStderr()
+ .toByteArray(MAX_EXCEPTION_TEXT_SIZE))));
+ }
+ return result.getStdout().openInputStream();
+ }
+ return in;
+ }
+
+ private InputStream handleAutoCRLF(InputStream in) {
+ AutoCRLF autoCRLF = getOptions().getAutoCRLF();
+ if (autoCRLF == AutoCRLF.TRUE || autoCRLF == AutoCRLF.INPUT) {
+ in = new EolCanonicalizingInputStream(in, true);
+ }
+ return in;
}
/**
@@ -507,6 +530,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
System.arraycopy(e.encodedName, 0, path, pathOffset, nameLen);
pathLen = pathOffset + nameLen;
canonLen = -1;
+ cleanFilterCommand = null;
}
/**
@@ -667,41 +691,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
return attributesNode;
}
- /**
- * Retrieves the {@link AttributesNode} that holds the information located
- * in $GIT_DIR/info/attributes file.
- *
- * @return the {@link AttributesNode} that holds the information located in
- * $GIT_DIR/info/attributes file.
- * @throws IOException
- * if an error is raised while parsing the attributes file
- * @since 3.7
- */
- public AttributesNode getInfoAttributesNode() throws IOException {
- if (infoAttributeNode instanceof InfoAttributesNode)
- infoAttributeNode = ((InfoAttributesNode) infoAttributeNode).load();
- return infoAttributeNode;
- }
-
- /**
- * Retrieves the {@link AttributesNode} that holds the information located
- * in system-wide file.
- *
- * @return the {@link AttributesNode} that holds the information located in
- * system-wide file.
- * @throws IOException
- * IOException if an error is raised while parsing the
- * attributes file
- * @see CoreConfig#getAttributesFile()
- * @since 3.7
- */
- public AttributesNode getGlobalAttributesNode() throws IOException {
- if (globalAttributeNode instanceof GlobalAttributesNode)
- globalAttributeNode = ((GlobalAttributesNode) globalAttributeNode)
- .load();
- return globalAttributeNode;
- }
-
private static final Comparator<Entry> ENTRY_CMP = new Comparator<Entry>() {
public int compare(final Entry o1, final Entry o2) {
final byte[] a = o1.encodedName;
@@ -1296,68 +1285,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
}
}
- /**
- * Attributes node loaded from global system-wide file.
- */
- private static class GlobalAttributesNode extends AttributesNode {
- final Repository repository;
-
- GlobalAttributesNode(Repository repository) {
- this.repository = repository;
- }
-
- AttributesNode load() throws IOException {
- AttributesNode r = new AttributesNode();
-
- FS fs = repository.getFS();
- String path = repository.getConfig().get(CoreConfig.KEY)
- .getAttributesFile();
- if (path != null) {
- File attributesFile;
- if (path.startsWith("~/")) //$NON-NLS-1$
- attributesFile = fs.resolve(fs.userHome(),
- path.substring(2));
- else
- attributesFile = fs.resolve(null, path);
- loadRulesFromFile(r, attributesFile);
- }
- return r.getRules().isEmpty() ? null : r;
- }
- }
-
- /** Magic type indicating there may be rules for the top level. */
- private static class InfoAttributesNode extends AttributesNode {
- final Repository repository;
-
- InfoAttributesNode(Repository repository) {
- this.repository = repository;
- }
-
- AttributesNode load() throws IOException {
- AttributesNode r = new AttributesNode();
-
- FS fs = repository.getFS();
-
- File attributes = fs.resolve(repository.getDirectory(),
- "info/attributes"); //$NON-NLS-1$
- loadRulesFromFile(r, attributes);
-
- return r.getRules().isEmpty() ? null : r;
- }
-
- }
-
- private static void loadRulesFromFile(AttributesNode r, File attrs)
- throws FileNotFoundException, IOException {
- if (attrs.exists()) {
- FileInputStream in = new FileInputStream(attrs);
- try {
- r.parse(in);
- } finally {
- in.close();
- }
- }
- }
private static final class IteratorState {
/** Options used to process the working tree. */
@@ -1390,4 +1317,18 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
}
}
}
+
+ /**
+ * @return the clean filter command for the current entry or
+ * <code>null</code> if no such command is defined
+ * @throws IOException
+ * @since 4.2
+ */
+ public String getCleanFilterCommand() throws IOException {
+ if (cleanFilterCommand == null && state.walk != null) {
+ cleanFilterCommand = state.walk
+ .getFilterCommand(Constants.ATTR_FILTER_TYPE_CLEAN);
+ }
+ return cleanFilterCommand;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
index e407dd8be9..253acbb76a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -110,7 +110,54 @@ public abstract class FS {
}
}
- final static Logger LOG = LoggerFactory.getLogger(FS.class);
+ /**
+ * Result of an executed process. The caller is responsible to close the
+ * contained {@link TemporaryBuffer}s
+ *
+ * @since 4.2
+ */
+ public static class ExecutionResult {
+ private TemporaryBuffer stdout;
+
+ private TemporaryBuffer stderr;
+
+ private int rc;
+
+ /**
+ * @param stdout
+ * @param stderr
+ * @param rc
+ */
+ public ExecutionResult(TemporaryBuffer stdout, TemporaryBuffer stderr,
+ int rc) {
+ this.stdout = stdout;
+ this.stderr = stderr;
+ this.rc = rc;
+ }
+
+ /**
+ * @return buffered standard output stream
+ */
+ public TemporaryBuffer getStdout() {
+ return stdout;
+ }
+
+ /**
+ * @return buffered standard error stream
+ */
+ public TemporaryBuffer getStderr() {
+ return stderr;
+ }
+
+ /**
+ * @return the return code of the process
+ */
+ public int getRc() {
+ return rc;
+ }
+ }
+
+ private final static Logger LOG = LoggerFactory.getLogger(FS.class);
/** The auto-detected implementation selected for this operating system and JRE. */
public static final FS DETECTED = detect();
@@ -902,9 +949,7 @@ public abstract class FS {
* @param outRedirect
* An OutputStream on which to redirect the processes stdout. Can
* be <code>null</code>, in which case the processes standard
- * output will be lost. If binary is set to <code>false</code>
- * then it is expected that the process emits text data which
- * should be processed line by line.
+ * output will be lost.
* @param errRedirect
* An OutputStream on which to redirect the processes stderr. Can
* be <code>null</code>, in which case the processes standard
@@ -912,9 +957,9 @@ public abstract class FS {
* @param inRedirect
* An InputStream from which to redirect the processes stdin. Can
* be <code>null</code>, in which case the process doesn't get
- * any data over stdin. If binary is set to
- * <code>false</code> then it is expected that the process
- * expects text data which should be processed line by line.
+ * any data over stdin. It is assumed that the whole InputStream
+ * will be consumed by the process. The method will close the
+ * inputstream after all bytes are read.
* @return the return code of this process.
* @throws IOException
* if an I/O error occurs while executing this process.
@@ -964,6 +1009,9 @@ public abstract class FS {
// A process doesn't clean its own resources even when destroyed
// Explicitly try and close all three streams, preserving the
// outer I/O exception if any.
+ if (inRedirect != null) {
+ inRedirect.close();
+ }
try {
process.getErrorStream().close();
} catch (IOException e) {
@@ -1004,10 +1052,10 @@ public abstract class FS {
pool.shutdown(); // Disable new tasks from being submitted
try {
// Wait a while for existing tasks to terminate
- if (!pool.awaitTermination(5, TimeUnit.SECONDS)) {
+ if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
pool.shutdownNow(); // Cancel currently executing tasks
// Wait a while for tasks to respond to being canceled
- if (!pool.awaitTermination(5, TimeUnit.SECONDS))
+ if (!pool.awaitTermination(60, TimeUnit.SECONDS))
hasShutdown = false;
}
} catch (InterruptedException ie) {
@@ -1034,6 +1082,31 @@ public abstract class FS {
*/
public abstract ProcessBuilder runInShell(String cmd, String[] args);
+ /**
+ * Execute a command defined by a {@link ProcessBuilder}.
+ *
+ * @param pb
+ * The command to be executed
+ * @param in
+ * The standard input stream passed to the process
+ * @return The result of the executed command
+ * @throws InterruptedException
+ * @throws IOException
+ * @since 4.2
+ */
+ public ExecutionResult execute(ProcessBuilder pb, InputStream in)
+ throws IOException, InterruptedException {
+ TemporaryBuffer stdout = new TemporaryBuffer.LocalFile(null);
+ TemporaryBuffer stderr = new TemporaryBuffer.Heap(1024, 1024 * 1024);
+ try {
+ int rc = runProcess(pb, stdout, stderr, in);
+ return new ExecutionResult(stdout, stderr, rc);
+ } finally {
+ stdout.close();
+ stderr.close();
+ }
+ }
+
private static class Holder<V> {
final V value;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
index f0a2e721a6..defe14f0ed 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
@@ -171,7 +171,8 @@ public class FS_Win32 extends FS {
createSymLink(linkName, tempFile.getPath());
supportSymlinks = Boolean.TRUE;
linkName.delete();
- } catch (IOException | UnsupportedOperationException e) {
+ } catch (IOException | UnsupportedOperationException
+ | InternalError e) {
supportSymlinks = Boolean.FALSE;
} finally {
if (tempFile != null)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
index 2450be4c17..ec581b397a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
@@ -162,17 +162,15 @@ public class FS_Win32_Cygwin extends FS_Win32 {
errRedirect, stdinArgs);
}
- @Override
- public boolean supportsSymlinks() {
- return true;
- }
-
/**
* @since 3.7
*/
@Override
public File findHook(Repository repository, String hookName) {
final File gitdir = repository.getDirectory();
+ if (gitdir == null) {
+ return null;
+ }
final Path hookPath = gitdir.toPath().resolve(Constants.HOOKS)
.resolve(hookName);
if (Files.isExecutable(hookPath))
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
index 6d0318c029..727ea79cc9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
@@ -398,8 +398,10 @@ public class FileUtils {
* Create a symbolic link
*
* @param path
+ * the path of the symbolic link to create
* @param target
- * @return path to the created link
+ * the target of the symbolic link
+ * @return the path to the symbolic link
* @throws IOException
* @since 4.2
*/
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
index 4795c89e73..9860ef070f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
@@ -339,4 +339,19 @@ public abstract class SystemReader {
public void checkPath(String path) throws CorruptObjectException {
platformChecker.checkPath(path);
}
+
+ /**
+ * Check tree path entry for validity.
+ * <p>
+ * Scans a multi-directory path string such as {@code "src/main.c"}.
+ *
+ * @param path
+ * path string to scan.
+ * @throws CorruptObjectException
+ * path is invalid.
+ * @since 4.2
+ */
+ public void checkPath(byte[] path) throws CorruptObjectException {
+ platformChecker.checkPath(path, 0, path.length);
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
index ca47f50fd9..3cd5929c7f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/TemporaryBuffer.java
@@ -247,6 +247,37 @@ public abstract class TemporaryBuffer extends OutputStream {
}
/**
+ * Convert this buffer's contents into a contiguous byte array. If this size
+ * of the buffer exceeds the limit only return the first {@code limit} bytes
+ * <p>
+ * The buffer is only complete after {@link #close()} has been invoked.
+ *
+ * @param limit
+ * the maximum number of bytes to be returned
+ *
+ * @return the byte array limited to {@code limit} bytes.
+ * @throws IOException
+ * an error occurred reading from a local temporary file
+ * @throws OutOfMemoryError
+ * the buffer cannot fit in memory
+ *
+ * @since 4.2
+ */
+ public byte[] toByteArray(int limit) throws IOException {
+ final long len = Math.min(length(), limit);
+ if (Integer.MAX_VALUE < len)
+ throw new OutOfMemoryError(
+ JGitText.get().lengthExceedsMaximumArraySize);
+ final byte[] out = new byte[(int) len];
+ int outPtr = 0;
+ for (final Block b : blocks) {
+ System.arraycopy(b.buffer, 0, out, outPtr, b.count);
+ outPtr += b.count;
+ }
+ return out;
+ }
+
+ /**
* Send this buffer to an output stream.
* <p>
* This method may only be invoked after {@link #close()} has completed