summaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jgit')
-rw-r--r--org.eclipse.jgit/.settings/.api_filters73
-rw-r--r--org.eclipse.jgit/BUILD23
-rw-r--r--org.eclipse.jgit/META-INF/MANIFEST.MF104
-rw-r--r--org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF4
-rw-r--r--org.eclipse.jgit/pom.xml2
-rw-r--r--org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties11
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java17
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java135
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java176
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java9
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBundleWriter.java52
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BasePackBitmapIndex.java17
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java22
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java33
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/MergedReftable.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java20
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java37
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java124
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java49
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/FilterSpec.java108
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteSession.java26
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java53
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java28
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java13
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java188
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java38
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/SshSupport.java59
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java22
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java125
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFOutputStream.java63
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java35
35 files changed, 1224 insertions, 469 deletions
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
deleted file mode 100644
index e2565bd6b3..0000000000
--- a/org.eclipse.jgit/.settings/.api_filters
+++ /dev/null
@@ -1,73 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<component id="org.eclipse.jgit" version="2">
- <resource path="META-INF/MANIFEST.MF" type="org.eclipse.jgit.transport.CredentialsProviderUserInfo">
- <filter id="305324134">
- <message_arguments>
- <message_argument value="org.eclipse.jgit.transport.CredentialsProviderUserInfo"/>
- <message_argument value="org.eclipse.jgit_5.8.0"/>
- </message_arguments>
- </filter>
- </resource>
- <resource path="META-INF/MANIFEST.MF" type="org.eclipse.jgit.transport.DefaultSshSessionFactory">
- <filter id="305324134">
- <message_arguments>
- <message_argument value="org.eclipse.jgit.transport.DefaultSshSessionFactory"/>
- <message_argument value="org.eclipse.jgit_5.8.0"/>
- </message_arguments>
- </filter>
- </resource>
- <resource path="META-INF/MANIFEST.MF" type="org.eclipse.jgit.transport.JschConfigSessionFactory">
- <filter id="305324134">
- <message_arguments>
- <message_argument value="org.eclipse.jgit.transport.JschConfigSessionFactory"/>
- <message_argument value="org.eclipse.jgit_5.8.0"/>
- </message_arguments>
- </filter>
- </resource>
- <resource path="META-INF/MANIFEST.MF" type="org.eclipse.jgit.transport.JschSession">
- <filter id="305324134">
- <message_arguments>
- <message_argument value="org.eclipse.jgit.transport.JschSession"/>
- <message_argument value="org.eclipse.jgit_5.8.0"/>
- </message_arguments>
- </filter>
- </resource>
- <resource path="META-INF/MANIFEST.MF" type="org.eclipse.jgit.transport.OpenSshConfig">
- <filter id="305324134">
- <message_arguments>
- <message_argument value="org.eclipse.jgit.transport.OpenSshConfig"/>
- <message_argument value="org.eclipse.jgit_5.8.0"/>
- </message_arguments>
- </filter>
- </resource>
- <resource path="src/org/eclipse/jgit/lib/BitmapIndex.java" type="org.eclipse.jgit.lib.BitmapIndex$Bitmap">
- <filter id="403804204">
- <message_arguments>
- <message_argument value="org.eclipse.jgit.lib.BitmapIndex.Bitmap"/>
- <message_argument value="retrieveCompressed()"/>
- </message_arguments>
- </filter>
- </resource>
- <resource path="src/org/eclipse/jgit/transport/SshSessionFactory.java" type="org.eclipse.jgit.transport.SshSessionFactory">
- <filter id="336695337">
- <message_arguments>
- <message_argument value="org.eclipse.jgit.transport.SshSessionFactory"/>
- <message_argument value="getType()"/>
- </message_arguments>
- </filter>
- </resource>
- <resource path="src/org/eclipse/jgit/transport/http/HttpConnection.java" type="org.eclipse.jgit.transport.http.HttpConnection">
- <filter id="403767336">
- <message_arguments>
- <message_argument value="org.eclipse.jgit.transport.http.HttpConnection"/>
- <message_argument value="HTTP_11_MOVED_PERM"/>
- </message_arguments>
- </filter>
- <filter id="403767336">
- <message_arguments>
- <message_argument value="org.eclipse.jgit.transport.http.HttpConnection"/>
- <message_argument value="HTTP_NOT_AUTHORITATIVE"/>
- </message_arguments>
- </filter>
- </resource>
-</component>
diff --git a/org.eclipse.jgit/BUILD b/org.eclipse.jgit/BUILD
index f7970976b0..2083372248 100644
--- a/org.eclipse.jgit/BUILD
+++ b/org.eclipse.jgit/BUILD
@@ -14,7 +14,7 @@ SRCS = glob(
RESOURCES = glob(["resources/**"])
java_library(
- name = "jgit",
+ name = "jgit_non_stamped",
srcs = SRCS,
resource_strip_prefix = "org.eclipse.jgit/resources",
resources = RESOURCES,
@@ -25,6 +25,27 @@ java_library(
],
)
+genrule(
+ name = "jgit",
+ srcs = [":jgit_non_stamped"],
+ outs = ["jgit.jar"],
+ cmd = " && ".join([
+ "ROOT=$$PWD",
+ "TMP=$$(mktemp -d || mktemp -d -t bazel-tmp)",
+ "TZ=UTC",
+ "export TZ",
+ "GEN_VERSION=$$(cat bazel-out/stable-status.txt | grep -w STABLE_BUILD_JGIT_LABEL | cut -d ' ' -f 2)",
+ "cd $$TMP",
+ "unzip -q $$ROOT/$<",
+ "echo \"Implementation-Version: $$GEN_VERSION\n$$(cat META-INF/MANIFEST.MF)\" > META-INF/MANIFEST.MF",
+ "find . -exec touch '{}' ';'",
+ "zip -Xqr $$ROOT/$@ .",
+ "rm -rf $$TMP",
+ ]),
+ stamp = 1,
+ visibility = ["//visibility:public"],
+)
+
java_library(
name = "insecure_cipher_factory",
srcs = INSECURE_CIPHER_FACTORY,
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index e83d320811..1348bde29e 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -3,12 +3,12 @@ Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
Automatic-Module-Name: org.eclipse.jgit
Bundle-SymbolicName: org.eclipse.jgit
-Bundle-Version: 5.8.2.qualifier
+Bundle-Version: 5.9.1.qualifier
Bundle-Localization: plugin
Bundle-Vendor: %Bundle-Vendor
Eclipse-ExtensibleAPI: true
-Export-Package: org.eclipse.jgit.annotations;version="5.8.2",
- org.eclipse.jgit.api;version="5.8.2";
+Export-Package: org.eclipse.jgit.annotations;version="5.9.1",
+ org.eclipse.jgit.api;version="5.9.1";
uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.notes,
org.eclipse.jgit.dircache,
@@ -23,18 +23,18 @@ Export-Package: org.eclipse.jgit.annotations;version="5.8.2",
org.eclipse.jgit.revwalk.filter,
org.eclipse.jgit.blame,
org.eclipse.jgit.merge",
- org.eclipse.jgit.api.errors;version="5.8.2";
+ org.eclipse.jgit.api.errors;version="5.9.1";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.errors",
- org.eclipse.jgit.attributes;version="5.8.2";
+ org.eclipse.jgit.attributes;version="5.9.1";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk",
- org.eclipse.jgit.blame;version="5.8.2";
+ org.eclipse.jgit.blame;version="5.9.1";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.diff",
- org.eclipse.jgit.diff;version="5.8.2";
+ org.eclipse.jgit.diff;version="5.9.1";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.attributes,
org.eclipse.jgit.revwalk,
@@ -42,47 +42,47 @@ Export-Package: org.eclipse.jgit.annotations;version="5.8.2",
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.util",
- org.eclipse.jgit.dircache;version="5.8.2";
+ org.eclipse.jgit.dircache;version="5.9.1";
uses:="org.eclipse.jgit.events,
org.eclipse.jgit.lib,
org.eclipse.jgit.attributes,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.util",
- org.eclipse.jgit.errors;version="5.8.2";
+ org.eclipse.jgit.errors;version="5.9.1";
uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.dircache,
org.eclipse.jgit.lib,
org.eclipse.jgit.internal.storage.pack",
- org.eclipse.jgit.events;version="5.8.2";
+ org.eclipse.jgit.events;version="5.9.1";
uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.fnmatch;version="5.8.2",
- org.eclipse.jgit.gitrepo;version="5.8.2";
+ org.eclipse.jgit.fnmatch;version="5.9.1",
+ org.eclipse.jgit.gitrepo;version="5.9.1";
uses:="org.xml.sax.helpers,
org.eclipse.jgit.api,
org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
org.xml.sax",
- org.eclipse.jgit.gitrepo.internal;version="5.8.2";x-internal:=true,
- org.eclipse.jgit.hooks;version="5.8.2";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.ignore;version="5.8.2",
- org.eclipse.jgit.ignore.internal;version="5.8.2";
+ org.eclipse.jgit.gitrepo.internal;version="5.9.1";x-internal:=true,
+ org.eclipse.jgit.hooks;version="5.9.1";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.ignore;version="5.9.1",
+ org.eclipse.jgit.ignore.internal;version="5.9.1";
x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal;version="5.8.2";
+ org.eclipse.jgit.internal;version="5.9.1";
x-friends:="org.eclipse.jgit.test,
org.eclipse.jgit.http.test",
- org.eclipse.jgit.internal.fsck;version="5.8.2";
+ org.eclipse.jgit.internal.fsck;version="5.9.1";
x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.ketch;version="5.8.2";
+ org.eclipse.jgit.internal.ketch;version="5.9.1";
x-friends:="org.eclipse.jgit.junit,
org.eclipse.jgit.test,
org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.revwalk;version="5.8.2";x-internal:=true,
- org.eclipse.jgit.internal.storage.dfs;version="5.8.2";
+ org.eclipse.jgit.internal.revwalk;version="5.9.1";x-internal:=true,
+ org.eclipse.jgit.internal.storage.dfs;version="5.9.1";
x-friends:="org.eclipse.jgit.test,
org.eclipse.jgit.http.server,
org.eclipse.jgit.http.test,
org.eclipse.jgit.lfs.test",
- org.eclipse.jgit.internal.storage.file;version="5.8.2";
+ org.eclipse.jgit.internal.storage.file;version="5.9.1";
x-friends:="org.eclipse.jgit.test,
org.eclipse.jgit.junit,
org.eclipse.jgit.junit.http,
@@ -91,35 +91,35 @@ Export-Package: org.eclipse.jgit.annotations;version="5.8.2",
org.eclipse.jgit.pgm,
org.eclipse.jgit.pgm.test,
org.eclipse.jgit.ssh.apache",
- org.eclipse.jgit.internal.storage.io;version="5.8.2";
+ org.eclipse.jgit.internal.storage.io;version="5.9.1";
x-friends:="org.eclipse.jgit.junit,
org.eclipse.jgit.test,
org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.pack;version="5.8.2";
+ org.eclipse.jgit.internal.storage.pack;version="5.9.1";
x-friends:="org.eclipse.jgit.junit,
org.eclipse.jgit.test,
org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftable;version="5.8.2";
+ org.eclipse.jgit.internal.storage.reftable;version="5.9.1";
x-friends:="org.eclipse.jgit.http.test,
org.eclipse.jgit.junit,
org.eclipse.jgit.test,
org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftree;version="5.8.2";
+ org.eclipse.jgit.internal.storage.reftree;version="5.9.1";
x-friends:="org.eclipse.jgit.junit,
org.eclipse.jgit.test,
org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.submodule;version="5.8.2";x-internal:=true,
- org.eclipse.jgit.internal.transport.connectivity;version="5.8.2";
+ org.eclipse.jgit.internal.submodule;version="5.9.1";x-internal:=true,
+ org.eclipse.jgit.internal.transport.connectivity;version="5.9.1";
x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.http;version="5.8.2";
+ org.eclipse.jgit.internal.transport.http;version="5.9.1";
x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.parser;version="5.8.2";
+ org.eclipse.jgit.internal.transport.parser;version="5.9.1";
x-friends:="org.eclipse.jgit.http.server,
org.eclipse.jgit.test",
- org.eclipse.jgit.internal.transport.ssh;version="5.8.2";
+ org.eclipse.jgit.internal.transport.ssh;version="5.9.1";
x-friends:="org.eclipse.jgit.ssh.apache,
org.eclipse.jgit.ssh.jsch",
- org.eclipse.jgit.lib;version="5.8.2";
+ org.eclipse.jgit.lib;version="5.9.1";
uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.util.sha1,
org.eclipse.jgit.dircache,
@@ -133,9 +133,9 @@ Export-Package: org.eclipse.jgit.annotations;version="5.8.2",
org.eclipse.jgit.util,
org.eclipse.jgit.submodule,
org.eclipse.jgit.util.time",
- org.eclipse.jgit.lib.internal;version="5.8.2";
+ org.eclipse.jgit.lib.internal;version="5.9.1";
x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.merge;version="5.8.2";
+ org.eclipse.jgit.merge;version="5.9.1";
uses:="org.eclipse.jgit.dircache,
org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
@@ -144,40 +144,40 @@ Export-Package: org.eclipse.jgit.annotations;version="5.8.2",
org.eclipse.jgit.util,
org.eclipse.jgit.api,
org.eclipse.jgit.attributes",
- org.eclipse.jgit.nls;version="5.8.2",
- org.eclipse.jgit.notes;version="5.8.2";
+ org.eclipse.jgit.nls;version="5.9.1",
+ org.eclipse.jgit.notes;version="5.9.1";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.merge",
- org.eclipse.jgit.patch;version="5.8.2";
+ org.eclipse.jgit.patch;version="5.9.1";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.diff",
- org.eclipse.jgit.revplot;version="5.8.2";
+ org.eclipse.jgit.revplot;version="5.9.1";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk",
- org.eclipse.jgit.revwalk;version="5.8.2";
+ org.eclipse.jgit.revwalk;version="5.9.1";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.diff,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.revwalk.filter,
org.eclipse.jgit.treewalk",
- org.eclipse.jgit.revwalk.filter;version="5.8.2";
+ org.eclipse.jgit.revwalk.filter;version="5.9.1";
uses:="org.eclipse.jgit.revwalk,
org.eclipse.jgit.lib,
org.eclipse.jgit.util",
- org.eclipse.jgit.storage.file;version="5.8.2";
+ org.eclipse.jgit.storage.file;version="5.9.1";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.util",
- org.eclipse.jgit.storage.pack;version="5.8.2";
+ org.eclipse.jgit.storage.pack;version="5.9.1";
uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.submodule;version="5.8.2";
+ org.eclipse.jgit.submodule;version="5.9.1";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.diff,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.util",
- org.eclipse.jgit.transport;version="5.8.2";
+ org.eclipse.jgit.transport;version="5.9.1";
uses:="javax.crypto,
org.eclipse.jgit.util.io,
org.eclipse.jgit.lib,
@@ -190,21 +190,21 @@ Export-Package: org.eclipse.jgit.annotations;version="5.8.2",
org.eclipse.jgit.transport.resolver,
org.eclipse.jgit.storage.pack,
org.eclipse.jgit.errors",
- org.eclipse.jgit.transport.http;version="5.8.2";
+ org.eclipse.jgit.transport.http;version="5.9.1";
uses:="javax.net.ssl",
- org.eclipse.jgit.transport.resolver;version="5.8.2";
+ org.eclipse.jgit.transport.resolver;version="5.9.1";
uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.lib",
- org.eclipse.jgit.treewalk;version="5.8.2";
+ org.eclipse.jgit.treewalk;version="5.9.1";
uses:="org.eclipse.jgit.dircache,
org.eclipse.jgit.lib,
org.eclipse.jgit.attributes,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.util",
- org.eclipse.jgit.treewalk.filter;version="5.8.2";
+ org.eclipse.jgit.treewalk.filter;version="5.9.1";
uses:="org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util;version="5.8.2";
+ org.eclipse.jgit.util;version="5.9.1";
uses:="org.eclipse.jgit.transport,
org.eclipse.jgit.hooks,
org.eclipse.jgit.revwalk,
@@ -217,12 +217,12 @@ Export-Package: org.eclipse.jgit.annotations;version="5.8.2",
org.eclipse.jgit.treewalk,
javax.net.ssl,
org.eclipse.jgit.util.time",
- org.eclipse.jgit.util.io;version="5.8.2";
+ org.eclipse.jgit.util.io;version="5.9.1";
uses:="org.eclipse.jgit.attributes,
org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util.sha1;version="5.8.2",
- org.eclipse.jgit.util.time;version="5.8.2"
+ org.eclipse.jgit.util.sha1;version="5.9.1",
+ org.eclipse.jgit.util.time;version="5.9.1"
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
javax.crypto,
diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
index 9202c879ed..c561b7ede1 100644
--- a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
@@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2
Bundle-Name: org.eclipse.jgit - Sources
Bundle-SymbolicName: org.eclipse.jgit.source
Bundle-Vendor: Eclipse.org - JGit
-Bundle-Version: 5.8.2.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit;version="5.8.2.qualifier";roots="."
+Bundle-Version: 5.9.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit;version="5.9.1.qualifier";roots="."
diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml
index baa74cba15..c899dfd293 100644
--- a/org.eclipse.jgit/pom.xml
+++ b/org.eclipse.jgit/pom.xml
@@ -20,7 +20,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>5.8.2-SNAPSHOT</version>
+ <version>5.9.1-SNAPSHOT</version>
</parent>
<artifactId>org.eclipse.jgit</artifactId>
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 bf19fd9ba0..0e51eb45cb 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -202,10 +202,8 @@ countingObjects=Counting objects
corruptPack=Pack file {0} is corrupt, removing it from pack list
createBranchFailedUnknownReason=Create branch failed for unknown reason
createBranchUnexpectedResult=Create branch returned unexpected result {0}
-createJGitConfigFailed=Creating JGit config directory {} failed
createNewFileFailed=Could not create new file {0}
createRequiresZeroOldId=Create requires old ID to be zero
-createXDGConfigHomeFailed=Creating XDG_CONFIG_HOME directory {} failed
credentialPassword=Password
credentialPassphrase=Passphrase
credentialUsername=Username
@@ -225,6 +223,8 @@ dirCacheDoesNotHaveABackingFile=DirCache does not have a backing file
dirCacheFileIsNotLocked=DirCache {0} not locked
dirCacheIsNotLocked=DirCache is not locked
DIRCChecksumMismatch=DIRC checksum mismatch
+DIRCCorruptLength=DIRC variable int {0} invalid after entry for {1}
+DIRCCorruptLengthFirst=DIRC variable int {0} invalid in first entry
DIRCExtensionIsTooLargeAt=DIRC extension {0} is too large at {1} bytes.
DIRCExtensionNotSupportedByThisVersion=DIRC extension {0} not supported by this version.
DIRCHasTooManyEntries=DIRC has too many entries.
@@ -409,6 +409,10 @@ lockError=lock error: {0}
lockFailedRetry=locking {0} failed after {1} retries
lockOnNotClosed=Lock on {0} not closed.
lockOnNotHeld=Lock on {0} not held.
+logInconsistentFiletimeDiff={}: inconsistent duration from file timestamps on {}, {}: {} > {}, but diff = {}. Aborting measurement at resolution {}.
+logLargerFiletimeDiff={}: inconsistent duration from file timestamps on {}, {}: diff = {} > {} (last good value). Aborting measurement.
+logSmallerFiletime={}: got smaller file timestamp on {}, {}: {} < {}. Aborting measurement at resolution {}.
+logXDGConfigHomeInvalid=Environment variable XDG_CONFIG_HOME contains an invalid path {}
maxCountMustBeNonNegative=max count must be >= 0
mergeConflictOnNonNoteEntries=Merge conflict on non-note entries: base = {0}, ours = {1}, theirs = {2}
mergeConflictOnNotes=Merge conflict on note {0}. base = {1}, ours = {2}, theirs = {2}
@@ -622,6 +626,7 @@ sourceRefDoesntResolveToAnyObject=Source ref {0} doesn''t resolve to any object.
sourceRefNotSpecifiedForRefspec=Source ref not specified for refspec: {0}
squashCommitNotUpdatingHEAD=Squash commit -- not updating HEAD
sshCommandFailed=Execution of ssh command ''{0}'' failed with error ''{1}''
+sshCommandTimeout=Execution of ssh command ''{0}'' timed out after {1} seconds
sslFailureExceptionMessage=Secure connection to {0} could not be established because of SSL problems
sslFailureInfo=A secure connection to {0} could not be established because the server''s certificate could not be validated.
sslFailureCause=SSL reported: {0}
@@ -662,7 +667,7 @@ threadInterruptedWhileRunning="Current thread interrupted while running {0}"
timeIsUncertain=Time is uncertain
timerAlreadyTerminated=Timer already terminated
timeoutMeasureFsTimestampResolution=measuring filesystem timestamp resolution for ''{0}'' timed out, fall back to resolution of 2 seconds
-tooManyCommands=Too many commands
+tooManyCommands=Commands size exceeds limit defined in receive.maxCommandBytes
tooManyFilters=Too many "filter" lines in request
tooManyIncludeRecursions=Too many recursions; circular includes in config file(s)?
topologicalSortRequired=Topological sort required.
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 7ae005ada8..1a41df3d0a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/ContentSource.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, Google Inc. and others
+ * Copyright (C) 2010, 2020 Google Inc. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -123,7 +123,8 @@ public abstract class ContentSource {
WorkingTreeIterator ptr;
WorkingTreeSource(WorkingTreeIterator iterator) {
- this.tw = new TreeWalk((ObjectReader) null);
+ this.tw = new TreeWalk(iterator.getRepository(),
+ (ObjectReader) null);
this.tw.setRecursive(true);
this.iterator = iterator;
}
@@ -173,6 +174,15 @@ public abstract class ContentSource {
private void seek(String path) throws IOException {
if (!path.equals(current)) {
iterator.reset();
+ // Possibly this iterator had an associated DirCacheIterator,
+ // but we have no access to it and thus don't know about it.
+ // We have to reset this iterator here to work without
+ // DirCacheIterator and to descend always into ignored
+ // directories. Otherwise we might not find tracked files below
+ // ignored folders. Since we're looking only for a single
+ // specific path this is not a performance problem.
+ iterator.setWalkIgnoredDirectories(true);
+ iterator.setDirCacheIterator(null, -1);
tw.reset();
tw.addTree(iterator);
tw.setFilter(PathFilter.create(path));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
index 81367eaa08..ec21954aa2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2009, Google Inc.
- * Copyright (C) 2008-2009, Johannes E. Schindelin <johannes.schindelin@gmx.de> and others
+ * Copyright (C) 2008-2020, Johannes E. Schindelin <johannes.schindelin@gmx.de> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -502,9 +502,18 @@ public class DiffFormatter implements AutoCloseable {
throws IOException {
assertHaveReader();
- TreeWalk walk = new TreeWalk(reader);
- walk.addTree(a);
- walk.addTree(b);
+ TreeWalk walk = new TreeWalk(repository, reader);
+ int aIndex = walk.addTree(a);
+ int bIndex = walk.addTree(b);
+ if (repository != null) {
+ if (a instanceof WorkingTreeIterator
+ && b instanceof DirCacheIterator) {
+ ((WorkingTreeIterator) a).setDirCacheIterator(walk, bIndex);
+ } else if (b instanceof WorkingTreeIterator
+ && a instanceof DirCacheIterator) {
+ ((WorkingTreeIterator) b).setDirCacheIterator(walk, aIndex);
+ }
+ }
walk.setRecursive(true);
TreeFilter filter = getDiffTreeFilterFor(a, b);
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 b2764d7804..03da61583d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java
@@ -1,7 +1,7 @@
/*
- * Copyright (C) 2008-2010, Google Inc.
+ * Copyright (C) 2008, 2010, Google Inc.
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
- * Copyright (C) 2011, Matthias Sohn <matthias.sohn@sap.com> and others
+ * Copyright (C) 2011, 2020, Matthias Sohn <matthias.sohn@sap.com> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -41,6 +41,9 @@ 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.Config;
+import org.eclipse.jgit.lib.Config.ConfigEnum;
+import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
@@ -321,6 +324,9 @@ public class DirCache {
/** Repository containing this index */
private Repository repository;
+ /** If we read this index from disk, the original format. */
+ private DirCacheVersion version;
+
/**
* Create a new in-core index representation.
* <p>
@@ -364,6 +370,10 @@ public class DirCache {
return new DirCacheEditor(this, entryCnt + 16);
}
+ DirCacheVersion getVersion() {
+ return version;
+ }
+
void replace(DirCacheEntry[] e, int cnt) {
sortedEntries = e;
entryCnt = cnt;
@@ -445,13 +455,26 @@ public class DirCache {
md.update(hdr, 0, 12);
if (!is_DIRC(hdr))
throw new CorruptObjectException(JGitText.get().notADIRCFile);
- final int ver = NB.decodeInt32(hdr, 4);
+ int versionCode = NB.decodeInt32(hdr, 4);
+ DirCacheVersion ver = DirCacheVersion.fromInt(versionCode);
+ if (ver == null) {
+ throw new CorruptObjectException(
+ MessageFormat.format(JGitText.get().unknownDIRCVersion,
+ Integer.valueOf(versionCode)));
+ }
boolean extended = false;
- if (ver == 3)
+ switch (ver) {
+ case DIRC_VERSION_MINIMUM:
+ break;
+ case DIRC_VERSION_EXTENDED:
+ case DIRC_VERSION_PATHCOMPRESS:
extended = true;
- else if (ver != 2)
- throw new CorruptObjectException(MessageFormat.format(
- JGitText.get().unknownDIRCVersion, Integer.valueOf(ver)));
+ break;
+ default:
+ throw new CorruptObjectException(MessageFormat
+ .format(JGitText.get().unknownDIRCVersion, ver));
+ }
+ version = ver;
entryCnt = NB.decodeInt32(hdr, 8);
if (entryCnt < 0)
throw new CorruptObjectException(JGitText.get().DIRCHasTooManyEntries);
@@ -467,7 +490,8 @@ public class DirCache {
final MutableInteger infoAt = new MutableInteger();
for (int i = 0; i < entryCnt; i++) {
- sortedEntries[i] = new DirCacheEntry(infos, infoAt, in, md, smudge);
+ sortedEntries[i] = new DirCacheEntry(infos, infoAt, in, md, smudge,
+ version, i == 0 ? null : sortedEntries[i - 1]);
}
// After the file entries are index extensions, and then a footer.
@@ -606,11 +630,20 @@ public class DirCache {
final MessageDigest foot = Constants.newMessageDigest();
final DigestOutputStream dos = new DigestOutputStream(os, foot);
- boolean extended = false;
- for (int i = 0; i < entryCnt; i++) {
- if (sortedEntries[i].isExtended()) {
- extended = true;
- break;
+ if (version == null && this.repository != null) {
+ // A new DirCache is being written.
+ DirCacheConfig config = repository.getConfig()
+ .get(DirCacheConfig::new);
+ version = config.getIndexVersion();
+ }
+ if (version == null
+ || version == DirCacheVersion.DIRC_VERSION_MINIMUM) {
+ version = DirCacheVersion.DIRC_VERSION_MINIMUM;
+ for (int i = 0; i < entryCnt; i++) {
+ if (sortedEntries[i].isExtended()) {
+ version = DirCacheVersion.DIRC_VERSION_EXTENDED;
+ break;
+ }
}
}
@@ -618,7 +651,7 @@ public class DirCache {
//
final byte[] tmp = new byte[128];
System.arraycopy(SIG_DIRC, 0, tmp, 0, SIG_DIRC.length);
- NB.encodeInt32(tmp, 4, extended ? 3 : 2);
+ NB.encodeInt32(tmp, 4, version.getVersionCode());
NB.encodeInt32(tmp, 8, entryCnt);
dos.write(tmp, 0, 12);
@@ -650,7 +683,7 @@ public class DirCache {
if (e.mightBeRacilyClean(smudge)) {
e.smudgeRacilyClean();
}
- e.write(dos);
+ e.write(dos, version, i == 0 ? null : sortedEntries[i - 1]);
}
if (writeTree) {
@@ -982,4 +1015,76 @@ public class DirCache {
}
}
}
+
+ enum DirCacheVersion implements ConfigEnum {
+
+ /** Minimum index version on-disk format that we support. */
+ DIRC_VERSION_MINIMUM(2),
+ /** Version 3 supports extended flags. */
+ DIRC_VERSION_EXTENDED(3),
+ /**
+ * Version 4 adds very simple "path compression": it strips out the
+ * common prefix between the last entry written and the current entry.
+ * Instead of writing two entries with paths "foo/bar/baz/a.txt" and
+ * "foo/bar/baz/b.txt" it only writes "b.txt" for the second entry.
+ * <p>
+ * It is also <em>not</em> padded.
+ * </p>
+ */
+ DIRC_VERSION_PATHCOMPRESS(4);
+
+ private final int version;
+
+ private DirCacheVersion(int versionCode) {
+ this.version = versionCode;
+ }
+
+ public int getVersionCode() {
+ return version;
+ }
+
+ @Override
+ public String toConfigValue() {
+ return Integer.toString(version);
+ }
+
+ @Override
+ public boolean matchConfigValue(String in) {
+ try {
+ return version == Integer.parseInt(in);
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+
+ public static DirCacheVersion fromInt(int val) {
+ for (DirCacheVersion v : DirCacheVersion.values()) {
+ if (val == v.getVersionCode()) {
+ return v;
+ }
+ }
+ return null;
+ }
+ }
+
+ private static class DirCacheConfig {
+
+ private final DirCacheVersion indexVersion;
+
+ public DirCacheConfig(Config cfg) {
+ boolean manyFiles = cfg.getBoolean(
+ ConfigConstants.CONFIG_FEATURE_SECTION,
+ ConfigConstants.CONFIG_KEY_MANYFILES, false);
+ indexVersion = cfg.getEnum(DirCacheVersion.values(),
+ ConfigConstants.CONFIG_INDEX_SECTION, null,
+ ConfigConstants.CONFIG_KEY_VERSION,
+ manyFiles ? DirCacheVersion.DIRC_VERSION_PATHCOMPRESS
+ : DirCacheVersion.DIRC_VERSION_EXTENDED);
+ }
+
+ public DirCacheVersion getIndexVersion() {
+ return indexVersion;
+ }
+
+ }
}
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 ced379ff1d..dcb84825fe 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java
@@ -1,8 +1,8 @@
/*
- * Copyright (C) 2008-2009, Google Inc.
+ * Copyright (C) 2008, 2009, Google Inc.
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
* Copyright (C) 2010, Matthias Sohn <matthias.sohn@sap.com>
- * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com> and others
+ * Copyright (C) 2010, 2020, Christian Halstrick <christian.halstrick@sap.com> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -26,6 +26,7 @@ import java.text.MessageFormat;
import java.time.Instant;
import java.util.Arrays;
+import org.eclipse.jgit.dircache.DirCache.DirCacheVersion;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
@@ -112,15 +113,16 @@ public class DirCacheEntry {
/** Flags which are never stored to disk. */
private byte inCoreFlags;
- DirCacheEntry(final byte[] sharedInfo, final MutableInteger infoAt,
- final InputStream in, final MessageDigest md, final Instant smudge)
+ DirCacheEntry(byte[] sharedInfo, MutableInteger infoAt, InputStream in,
+ MessageDigest md, Instant smudge, DirCacheVersion version,
+ DirCacheEntry previous)
throws IOException {
info = sharedInfo;
infoOffset = infoAt.value;
IO.readFully(in, info, infoOffset, INFO_LEN);
- final int len;
+ int len;
if (isExtended()) {
len = INFO_LEN_EXTENDED;
IO.readFully(in, info, infoOffset + INFO_LEN, INFO_LEN_EXTENDED - INFO_LEN);
@@ -134,31 +136,66 @@ public class DirCacheEntry {
infoAt.value += len;
md.update(info, infoOffset, len);
+ int toRemove = 0;
+ if (version == DirCacheVersion.DIRC_VERSION_PATHCOMPRESS) {
+ // Read variable int and update digest
+ int b = in.read();
+ md.update((byte) b);
+ toRemove = b & 0x7F;
+ while ((b & 0x80) != 0) {
+ toRemove++;
+ b = in.read();
+ md.update((byte) b);
+ toRemove = (toRemove << 7) | (b & 0x7F);
+ }
+ if (toRemove < 0
+ || (previous != null && toRemove > previous.path.length)) {
+ if (previous == null) {
+ throw new IOException(MessageFormat.format(
+ JGitText.get().DIRCCorruptLengthFirst,
+ Integer.valueOf(toRemove)));
+ }
+ throw new IOException(MessageFormat.format(
+ JGitText.get().DIRCCorruptLength,
+ Integer.valueOf(toRemove), previous.getPathString()));
+ }
+ }
int pathLen = NB.decodeUInt16(info, infoOffset + P_FLAGS) & NAME_MASK;
int skipped = 0;
if (pathLen < NAME_MASK) {
path = new byte[pathLen];
- IO.readFully(in, path, 0, pathLen);
- md.update(path, 0, pathLen);
- } else {
- final ByteArrayOutputStream tmp = new ByteArrayOutputStream();
- {
- final byte[] buf = new byte[NAME_MASK];
- IO.readFully(in, buf, 0, NAME_MASK);
- tmp.write(buf);
- }
- for (;;) {
- final int c = in.read();
- if (c < 0)
- throw new EOFException(JGitText.get().shortReadOfBlock);
- if (c == 0)
- break;
- tmp.write(c);
+ if (version == DirCacheVersion.DIRC_VERSION_PATHCOMPRESS
+ && previous != null) {
+ System.arraycopy(previous.path, 0, path, 0,
+ previous.path.length - toRemove);
+ IO.readFully(in, path, previous.path.length - toRemove,
+ pathLen - (previous.path.length - toRemove));
+ md.update(path, previous.path.length - toRemove,
+ pathLen - (previous.path.length - toRemove));
+ pathLen = pathLen - (previous.path.length - toRemove);
+ } else {
+ IO.readFully(in, path, 0, pathLen);
+ md.update(path, 0, pathLen);
}
+ } else if (version != DirCacheVersion.DIRC_VERSION_PATHCOMPRESS
+ || previous == null || toRemove == previous.path.length) {
+ ByteArrayOutputStream tmp = new ByteArrayOutputStream();
+ byte[] buf = new byte[NAME_MASK];
+ IO.readFully(in, buf, 0, NAME_MASK);
+ tmp.write(buf);
+ readNulTerminatedString(in, tmp);
path = tmp.toByteArray();
pathLen = path.length;
- skipped = 1; // we already skipped 1 '\0' above to break the loop.
md.update(path, 0, pathLen);
+ skipped = 1; // we already skipped 1 '\0' in readNulTerminatedString
+ md.update((byte) 0);
+ } else {
+ ByteArrayOutputStream tmp = new ByteArrayOutputStream();
+ tmp.write(previous.path, 0, previous.path.length - toRemove);
+ pathLen = readNulTerminatedString(in, tmp);
+ path = tmp.toByteArray();
+ md.update(path, previous.path.length - toRemove, pathLen);
+ skipped = 1; // we already skipped 1 '\0' in readNulTerminatedString
md.update((byte) 0);
}
@@ -172,17 +209,26 @@ public class DirCacheEntry {
throw p;
}
- // Index records are padded out to the next 8 byte alignment
- // for historical reasons related to how C Git read the files.
- //
- final int actLen = len + pathLen;
- final int expLen = (actLen + 8) & ~7;
- final int padLen = expLen - actLen - skipped;
- if (padLen > 0) {
- IO.skipFully(in, padLen);
- md.update(nullpad, 0, padLen);
+ if (version == DirCacheVersion.DIRC_VERSION_PATHCOMPRESS) {
+ if (skipped == 0) {
+ int b = in.read();
+ if (b < 0) {
+ throw new EOFException(JGitText.get().shortReadOfBlock);
+ }
+ md.update((byte) b);
+ }
+ } else {
+ // Index records are padded out to the next 8 byte alignment
+ // for historical reasons related to how C Git read the files.
+ //
+ final int actLen = len + pathLen;
+ final int expLen = (actLen + 8) & ~7;
+ final int padLen = expLen - actLen - skipped;
+ if (padLen > 0) {
+ IO.skipFully(in, padLen);
+ md.update(nullpad, 0, padLen);
+ }
}
-
if (mightBeRacilyClean(smudge)) {
smudgeRacilyClean();
}
@@ -283,19 +329,61 @@ public class DirCacheEntry {
System.arraycopy(src.info, src.infoOffset, info, 0, INFO_LEN);
}
- void write(OutputStream os) throws IOException {
- final int len = isExtended() ? INFO_LEN_EXTENDED : INFO_LEN;
- final int pathLen = path.length;
- os.write(info, infoOffset, len);
- os.write(path, 0, pathLen);
+ private int readNulTerminatedString(InputStream in, OutputStream out)
+ throws IOException {
+ int n = 0;
+ for (;;) {
+ int c = in.read();
+ if (c < 0) {
+ throw new EOFException(JGitText.get().shortReadOfBlock);
+ }
+ if (c == 0) {
+ break;
+ }
+ out.write(c);
+ n++;
+ }
+ return n;
+ }
- // Index records are padded out to the next 8 byte alignment
- // for historical reasons related to how C Git read the files.
- //
- final int actLen = len + pathLen;
- final int expLen = (actLen + 8) & ~7;
- if (actLen != expLen)
- os.write(nullpad, 0, expLen - actLen);
+ void write(OutputStream os, DirCacheVersion version, DirCacheEntry previous)
+ throws IOException {
+ final int len = isExtended() ? INFO_LEN_EXTENDED : INFO_LEN;
+ if (version != DirCacheVersion.DIRC_VERSION_PATHCOMPRESS) {
+ os.write(info, infoOffset, len);
+ os.write(path, 0, path.length);
+ // Index records are padded out to the next 8 byte alignment
+ // for historical reasons related to how C Git read the files.
+ //
+ int entryLen = len + path.length;
+ int expLen = (entryLen + 8) & ~7;
+ if (entryLen != expLen)
+ os.write(nullpad, 0, expLen - entryLen);
+ } else {
+ int pathCommon = 0;
+ int toRemove;
+ if (previous != null) {
+ // Figure out common prefix
+ int pathLen = Math.min(path.length, previous.path.length);
+ while (pathCommon < pathLen
+ && path[pathCommon] == previous.path[pathCommon]) {
+ pathCommon++;
+ }
+ toRemove = previous.path.length - pathCommon;
+ } else {
+ toRemove = 0;
+ }
+ byte[] tmp = new byte[16];
+ int n = tmp.length;
+ tmp[--n] = (byte) (toRemove & 0x7F);
+ while ((toRemove >>>= 7) != 0) {
+ tmp[--n] = (byte) (0x80 | (--toRemove & 0x7F));
+ }
+ os.write(info, infoOffset, len);
+ os.write(tmp, n, tmp.length - n);
+ os.write(path, pathCommon, path.length - pathCommon);
+ os.write(0);
+ }
}
/**
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 d04887c4dd..7ec5eaea10 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -230,10 +230,8 @@ public class JGitText extends TranslationBundle {
/***/ public String countingObjects;
/***/ public String createBranchFailedUnknownReason;
/***/ public String createBranchUnexpectedResult;
- /***/ public String createJGitConfigFailed;
/***/ public String createNewFileFailed;
/***/ public String createRequiresZeroOldId;
- /***/ public String createXDGConfigHomeFailed;
/***/ public String credentialPassword;
/***/ public String credentialPassphrase;
/***/ public String credentialUsername;
@@ -253,6 +251,8 @@ public class JGitText extends TranslationBundle {
/***/ public String dirCacheFileIsNotLocked;
/***/ public String dirCacheIsNotLocked;
/***/ public String DIRCChecksumMismatch;
+ /***/ public String DIRCCorruptLength;
+ /***/ public String DIRCCorruptLengthFirst;
/***/ public String DIRCExtensionIsTooLargeAt;
/***/ public String DIRCExtensionNotSupportedByThisVersion;
/***/ public String DIRCHasTooManyEntries;
@@ -437,6 +437,10 @@ public class JGitText extends TranslationBundle {
/***/ public String lockFailedRetry;
/***/ public String lockOnNotClosed;
/***/ public String lockOnNotHeld;
+ /***/ public String logInconsistentFiletimeDiff;
+ /***/ public String logLargerFiletimeDiff;
+ /***/ public String logSmallerFiletime;
+ /***/ public String logXDGConfigHomeInvalid;
/***/ public String maxCountMustBeNonNegative;
/***/ public String mergeConflictOnNonNoteEntries;
/***/ public String mergeConflictOnNotes;
@@ -650,6 +654,7 @@ public class JGitText extends TranslationBundle {
/***/ public String sourceRefNotSpecifiedForRefspec;
/***/ public String squashCommitNotUpdatingHEAD;
/***/ public String sshCommandFailed;
+ /***/ public String sshCommandTimeout;
/***/ public String sslFailureExceptionMessage;
/***/ public String sslFailureInfo;
/***/ public String sslFailureCause;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBundleWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBundleWriter.java
new file mode 100644
index 0000000000..736f381d78
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBundleWriter.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020, Google LLC and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.internal.storage.dfs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jgit.internal.storage.pack.CachedPack;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.transport.BundleWriter;
+
+/** Writes {@link DfsRepository} to a Git bundle. */
+public class DfsBundleWriter {
+ /**
+ * Writes the entire {@link DfsRepository} to a Git bundle.
+ * <p>
+ * This method try to avoid traversing the pack files as much as possible
+ * and dumps all objects as-is to a Git bundle.
+ *
+ * @param pm
+ * progress monitor
+ * @param os
+ * Git bundle output
+ * @param db
+ * repository
+ * @throws IOException
+ * thrown if the output stream throws one.
+ */
+ public static void writeEntireRepositoryAsBundle(ProgressMonitor pm,
+ OutputStream os, DfsRepository db) throws IOException {
+ BundleWriter bw = new BundleWriter(db);
+ db.getRefDatabase().getRefs().forEach(bw::include);
+ List<CachedPack> packs = new ArrayList<>();
+ for (DfsPackFile p : db.getObjectDatabase().getPacks()) {
+ packs.add(new DfsCachedPack(p));
+ }
+ bw.addObjectsAsIs(packs);
+ bw.writeBundle(pm, os);
+ }
+
+ private DfsBundleWriter() {
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BasePackBitmapIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BasePackBitmapIndex.java
index c9bb167f02..ec53818b4e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BasePackBitmapIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BasePackBitmapIndex.java
@@ -59,12 +59,26 @@ abstract class BasePackBitmapIndex extends PackBitmapIndex {
* @return the full bitmap
*/
EWAHCompressedBitmap getBitmap() {
+ EWAHCompressedBitmap bitmap = getBitmapWithoutCaching();
+ // Cache the result.
+ bitmapContainer = bitmap;
+ return bitmap;
+ }
+
+ /**
+ * Compute and return the full bitmap, do NOT cache the expanded bitmap,
+ * which saves memory and should only be used during bitmap creation in
+ * garbage collection.
+ *
+ * @return the full bitmap
+ */
+ EWAHCompressedBitmap getBitmapWithoutCaching() {
// Fast path to immediately return the expanded result.
Object r = bitmapContainer;
if (r instanceof EWAHCompressedBitmap)
return (EWAHCompressedBitmap) r;
- // Expand the bitmap and cache the result.
+ // Expand the bitmap but not cache the result.
XorCompressedBitmap xb = (XorCompressedBitmap) r;
EWAHCompressedBitmap out = xb.bitmap;
for (;;) {
@@ -72,7 +86,6 @@ abstract class BasePackBitmapIndex extends PackBitmapIndex {
if (r instanceof EWAHCompressedBitmap) {
out = out.xor((EWAHCompressedBitmap) r);
out.trim();
- bitmapContainer = out;
return out;
}
xb = (XorCompressedBitmap) r;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java
index 4b25284517..dd5d03c6e9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackBitmapIndexRemapper.java
@@ -156,7 +156,8 @@ public class PackBitmapIndexRemapper extends PackBitmapIndex
return null;
inflated.clear();
- for (IntIterator i = oldBitmap.getBitmap().intIterator(); i.hasNext();)
+ for (IntIterator i = oldBitmap.getBitmapWithoutCaching()
+ .intIterator(); i.hasNext();)
inflated.set(prevToNewMapping[i.next()]);
bitmap = inflated.toEWAHCompressedBitmap();
bitmap.trim();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java
index 9cf95d0720..eb0ac6a062 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/PackIndexV1.java
@@ -49,11 +49,11 @@ class PackIndexV1 extends PackIndex {
idxHeader[k] = NB.decodeUInt32(fanoutTable, k * 4);
idxdata = new byte[idxHeader.length][];
for (int k = 0; k < idxHeader.length; k++) {
- int n;
+ long n;
if (k == 0) {
- n = (int) (idxHeader[k]);
+ n = idxHeader[k];
} else {
- n = (int) (idxHeader[k] - idxHeader[k - 1]);
+ n = idxHeader[k] - idxHeader[k - 1];
}
if (n > 0) {
final long len = n * (Constants.OBJECT_ID_LENGTH + 4);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
index 80c8e10dec..3e8cb3a3f2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/WindowCache.java
@@ -1,6 +1,6 @@
/*
- * Copyright (C) 2008-2009, Google Inc.
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2009 Google Inc.
+ * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -19,6 +19,7 @@ import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.concurrent.atomic.LongAdder;
@@ -376,14 +377,14 @@ public class WindowCache {
* @return the cached instance.
*/
public static WindowCache getInstance() {
- return cache;
+ return cache.publishMBeanIfNeeded();
}
static final ByteWindow get(PackFile pack, long offset)
throws IOException {
final WindowCache c = cache;
final ByteWindow r = c.getOrLoad(pack, c.toStart(offset));
- if (c != cache) {
+ if (c != cache.publishMBeanIfNeeded()) {
// The cache was reconfigured while we were using the old one
// to load this window. The window is still valid, but our
// cache may think its still live. Ensure the window is removed
@@ -433,6 +434,8 @@ public class WindowCache {
private final StatsRecorderImpl mbean;
+ private final AtomicBoolean publishMBean = new AtomicBoolean();
+
private boolean useStrongRefs;
private WindowCache(WindowCacheConfig cfg) {
@@ -470,9 +473,7 @@ public class WindowCache {
mbean = new StatsRecorderImpl();
statsRecorder = mbean;
- if (cfg.getExposeStatsViaJmx()) {
- Monitoring.registerMBean(mbean, "block_cache"); //$NON-NLS-1$
- }
+ publishMBean.set(cfg.getExposeStatsViaJmx());
if (maxFiles < 1)
throw new IllegalArgumentException(JGitText.get().openFilesMustBeAtLeast1);
@@ -480,6 +481,13 @@ public class WindowCache {
throw new IllegalArgumentException(JGitText.get().windowSizeMustBeLesserThanLimit);
}
+ private WindowCache publishMBeanIfNeeded() {
+ if (publishMBean.getAndSet(false)) {
+ Monitoring.registerMBean(mbean, "block_cache"); //$NON-NLS-1$
+ }
+ return this;
+ }
+
/**
* @return cache statistics for the WindowCache
*/
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
index 824c62ad9a..3e4b5df6aa 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
@@ -756,6 +756,19 @@ public class PackWriter implements AutoCloseable {
/**
* Prepare the list of objects to be written to the pack stream.
+ *
+ * <p>
+ * PackWriter will concat and write out the specified packs as-is.
+ *
+ * @param c
+ * cached packs to be written.
+ */
+ public void preparePack(Collection<? extends CachedPack> c) {
+ cachedPacks.addAll(c);
+ }
+
+ /**
+ * Prepare the list of objects to be written to the pack stream.
* <p>
* Basing on these 2 sets, another set of objects to put in a pack file is
* created: this set consists of all objects reachable (ancestors) from
@@ -1548,6 +1561,7 @@ public class PackWriter implements AutoCloseable {
endPhase(monitor);
}
+ @SuppressWarnings("Finally")
private void parallelDeltaSearch(ProgressMonitor monitor,
ObjectToPack[] list, int cnt, int threads) throws IOException {
DeltaCache dc = new ThreadSafeDeltaCache(config);
@@ -1569,15 +1583,22 @@ public class PackWriter implements AutoCloseable {
// Caller didn't give us a way to run the tasks, spawn up a
// temporary thread pool and make sure it tears down cleanly.
ExecutorService pool = Executors.newFixedThreadPool(threads);
+ Throwable e1 = null;
try {
runTasks(pool, pm, taskBlock, errors);
+ } catch (Exception e) {
+ e1 = e;
} finally {
pool.shutdown();
for (;;) {
try {
- if (pool.awaitTermination(60, TimeUnit.SECONDS))
+ if (pool.awaitTermination(60, TimeUnit.SECONDS)) {
break;
+ }
} catch (InterruptedException e) {
+ if (e1 != null) {
+ e.addSuppressed(e1);
+ }
throw new IOException(JGitText
.get().packingCancelledDuringObjectsWriting, e);
}
@@ -2182,10 +2203,12 @@ public class PackWriter implements AutoCloseable {
// Check if this object needs to be rejected, doing the cheaper
// checks first.
- boolean reject = filterSpec.getBlobLimit() >= 0 &&
- type == OBJ_BLOB &&
- !want.contains(src) &&
- reader.getObjectSize(src, OBJ_BLOB) > filterSpec.getBlobLimit();
+ boolean reject =
+ (!filterSpec.allowsType(type) && !want.contains(src)) ||
+ (filterSpec.getBlobLimit() >= 0 &&
+ type == OBJ_BLOB &&
+ !want.contains(src) &&
+ reader.getObjectSize(src, OBJ_BLOB) > filterSpec.getBlobLimit());
if (!reject) {
addObject(src, type, pathHashCode);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/MergedReftable.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/MergedReftable.java
index 36335153a1..a78f4d24da 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/MergedReftable.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/MergedReftable.java
@@ -82,7 +82,7 @@ public class MergedReftable extends Reftable {
return 0;
}
long minUpdateIndex = tables[0].minUpdateIndex();
- for (int i = 0; i < tables.length - 1; i++) {
+ for (int i = 1; i < tables.length; i++) {
if (tables[i].minUpdateIndex() < minUpdateIndex) {
minUpdateIndex = tables[i].minUpdateIndex();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java
index f6695bdf7d..b11b230a8f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java
@@ -86,7 +86,7 @@ public interface BitmapIndex {
/**
* Returns the corresponding raw compressed EWAH bitmap of the bitmap.
- *
+ *
* @return the corresponding {@code EWAHCompressedBitmap}
* @since 5.8
*/
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java
index 66d7d51bdf..4f93fda49f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitBuilder.java
@@ -361,7 +361,9 @@ public class CommitBuilder {
* header</a>.
* <p>
* CRLF and CR will be sanitized to LF and signature will have a hanging
- * indent of one space starting with line two.
+ * indent of one space starting with line two. A trailing line break is
+ * <em>not</em> written; the caller is supposed to terminate the GPG
+ * signature header by writing a single newline.
* </p>
*
* @param in
@@ -375,22 +377,24 @@ public class CommitBuilder {
*/
static void writeGpgSignatureString(String in, OutputStream out)
throws IOException, IllegalArgumentException {
- for (int i = 0; i < in.length(); ++i) {
+ int length = in.length();
+ for (int i = 0; i < length; ++i) {
char ch = in.charAt(i);
switch (ch) {
case '\r':
- if (i + 1 < in.length() && in.charAt(i + 1) == '\n') {
- out.write('\n');
- out.write(' ');
+ if (i + 1 < length && in.charAt(i + 1) == '\n') {
++i;
- } else {
+ }
+ if (i + 1 < length) {
out.write('\n');
out.write(' ');
}
break;
case '\n':
- out.write('\n');
- out.write(' ');
+ if (i + 1 < length) {
+ out.write('\n');
+ out.write(' ');
+ }
break;
default:
// sanity check
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
index eef822fa4b..834fff5dd9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
* Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com>
- * Copyright (C) 2012-2013, Robin Rosenberg and others
+ * Copyright (C) 2012, 2020, Robin Rosenberg and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -93,6 +93,12 @@ public final class ConfigConstants {
public static final String CONFIG_GPG_SECTION = "gpg";
/**
+ * The "protocol" section
+ * @since 5.9
+ */
+ public static final String CONFIG_PROTOCOL_SECTION = "protocol";
+
+ /**
* The "format" key
* @since 5.2
*/
@@ -662,4 +668,33 @@ public final class ConfigConstants {
* @since 5.8
*/
public static final String CONFIG_KEY_WINDOW_MEMORY = "windowmemory";
+
+ /**
+ * The "feature" section
+ *
+ * @since 5.9
+ */
+ public static final String CONFIG_FEATURE_SECTION = "feature";
+
+ /**
+ * The "feature.manyFiles" key
+ *
+ * @since 5.9
+ */
+ public static final String CONFIG_KEY_MANYFILES = "manyFiles";
+
+ /**
+ * The "index" section
+ *
+ * @since 5.9
+ */
+ public static final String CONFIG_INDEX_SECTION = "index";
+
+ /**
+ * The "version" key
+ *
+ * @since 5.9
+ */
+ public static final String CONFIG_KEY_VERSION = "version";
+
}
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 506d333120..6c217fdf25 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -588,7 +588,8 @@ public class ResolveMerger extends ThreeWayMerger {
final int modeO = tw.getRawMode(T_OURS);
final int modeT = tw.getRawMode(T_THEIRS);
final int modeB = tw.getRawMode(T_BASE);
-
+ boolean gitLinkMerging = isGitLink(modeO) || isGitLink(modeT)
+ || isGitLink(modeB);
if (modeO == 0 && modeT == 0 && modeB == 0)
// File is either untracked or new, staged but uncommitted
return true;
@@ -737,31 +738,28 @@ public class ResolveMerger extends ThreeWayMerger {
return false;
}
- boolean gitlinkConflict = isGitLink(modeO) || isGitLink(modeT);
- // Don't attempt to resolve submodule link conflicts
- if (gitlinkConflict || !attributes.canBeContentMerged()) {
+ if (gitLinkMerging && ignoreConflicts) {
+ // Always select 'ours' in case of GITLINK merge failures so
+ // a caller can use virtual commit.
+ add(tw.getRawPath(), ours, DirCacheEntry.STAGE_0, EPOCH, 0);
+ return true;
+ } else if (gitLinkMerging) {
+ add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0);
+ add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
+ add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 0);
+ MergeResult<SubmoduleConflict> result = createGitLinksMergeResult(
+ base, ours, theirs);
+ result.setContainsConflicts(true);
+ mergeResults.put(tw.getPathString(), result);
+ unmergedPaths.add(tw.getPathString());
+ return true;
+ } else if (!attributes.canBeContentMerged()) {
add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0);
add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 0);
- if (gitlinkConflict) {
- MergeResult<SubmoduleConflict> result = new MergeResult<>(
- Arrays.asList(
- new SubmoduleConflict(base == null ? null
- : base.getEntryObjectId()),
- new SubmoduleConflict(ours == null ? null
- : ours.getEntryObjectId()),
- new SubmoduleConflict(theirs == null ? null
- : theirs.getEntryObjectId())));
- result.setContainsConflicts(true);
- mergeResults.put(tw.getPathString(), result);
- if (!ignoreConflicts) {
- unmergedPaths.add(tw.getPathString());
- }
- } else {
- // attribute merge issues are conflicts but not failures
- unmergedPaths.add(tw.getPathString());
- }
+ // attribute merge issues are conflicts but not failures
+ unmergedPaths.add(tw.getPathString());
return true;
}
@@ -786,45 +784,73 @@ public class ResolveMerger extends ThreeWayMerger {
// OURS or THEIRS has been deleted
if (((modeO != 0 && !tw.idEqual(T_BASE, T_OURS)) || (modeT != 0 && !tw
.idEqual(T_BASE, T_THEIRS)))) {
- MergeResult<RawText> result = contentMerge(base, ours, theirs,
- attributes);
-
- if (ignoreConflicts) {
- // In case a conflict is detected the working tree file is
- // again filled with new content (containing conflict
- // markers). But also stage 0 of the index is filled with
- // that content.
- result.setContainsConflicts(false);
- updateIndex(base, ours, theirs, result, attributes);
- } else {
+ if (gitLinkMerging && ignoreConflicts) {
+ add(tw.getRawPath(), ours, DirCacheEntry.STAGE_0, EPOCH, 0);
+ } else if (gitLinkMerging) {
add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0);
add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
- DirCacheEntry e = add(tw.getRawPath(), theirs,
- DirCacheEntry.STAGE_3, EPOCH, 0);
-
- // OURS was deleted checkout THEIRS
- if (modeO == 0) {
- // Check worktree before checking out THEIRS
- if (isWorktreeDirty(work, ourDce)) {
- return false;
- }
- if (nonTree(modeT)) {
- if (e != null) {
- addToCheckout(tw.getPathString(), e, attributes);
+ add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 0);
+ MergeResult<SubmoduleConflict> result = createGitLinksMergeResult(
+ base, ours, theirs);
+ result.setContainsConflicts(true);
+ mergeResults.put(tw.getPathString(), result);
+ unmergedPaths.add(tw.getPathString());
+ } else {
+ MergeResult<RawText> result = contentMerge(base, ours,
+ theirs, attributes);
+
+ if (ignoreConflicts) {
+ // In case a conflict is detected the working tree file
+ // is again filled with new content (containing conflict
+ // markers). But also stage 0 of the index is filled
+ // with that content.
+ result.setContainsConflicts(false);
+ updateIndex(base, ours, theirs, result, attributes);
+ } else {
+ add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH,
+ 0);
+ add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH,
+ 0);
+ DirCacheEntry e = add(tw.getRawPath(), theirs,
+ DirCacheEntry.STAGE_3, EPOCH, 0);
+
+ // OURS was deleted checkout THEIRS
+ if (modeO == 0) {
+ // Check worktree before checking out THEIRS
+ if (isWorktreeDirty(work, ourDce)) {
+ return false;
+ }
+ if (nonTree(modeT)) {
+ if (e != null) {
+ addToCheckout(tw.getPathString(), e,
+ attributes);
+ }
}
}
- }
- unmergedPaths.add(tw.getPathString());
+ unmergedPaths.add(tw.getPathString());
- // generate a MergeResult for the deleted file
- mergeResults.put(tw.getPathString(), result);
+ // generate a MergeResult for the deleted file
+ mergeResults.put(tw.getPathString(), result);
+ }
}
}
}
return true;
}
+ private static MergeResult<SubmoduleConflict> createGitLinksMergeResult(
+ CanonicalTreeParser base, CanonicalTreeParser ours,
+ CanonicalTreeParser theirs) {
+ return new MergeResult<>(Arrays.asList(
+ new SubmoduleConflict(
+ base == null ? null : base.getEntryObjectId()),
+ new SubmoduleConflict(
+ ours == null ? null : ours.getEntryObjectId()),
+ new SubmoduleConflict(
+ theirs == null ? null : theirs.getEntryObjectId())));
+ }
+
/**
* Does the content merge. The three texts base, ours and theirs are
* specified with {@link CanonicalTreeParser}. If any of the parsers is
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
index 57eed3ad2a..4649d33ff8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
@@ -17,12 +17,16 @@ import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.pack.CachedPack;
import org.eclipse.jgit.internal.storage.pack.PackWriter;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
@@ -62,6 +66,8 @@ public class BundleWriter {
private final Set<ObjectId> tagTargets;
+ private final List<CachedPack> cachedPacks = new ArrayList<>();
+
private PackConfig packConfig;
private ObjectCountCallback callback;
@@ -150,6 +156,26 @@ public class BundleWriter {
}
/**
+ * Add objects to the bundle file.
+ *
+ * <p>
+ * When this method is used, object traversal is disabled and specified pack
+ * files are directly saved to the Git bundle file.
+ *
+ * <p>
+ * Unlike {@link #include}, this doesn't affect the refs. Even if the
+ * objects are not reachable from any ref, they will be included in the
+ * bundle file.
+ *
+ * @param c
+ * pack to include
+ * @since 5.9
+ */
+ public void addObjectsAsIs(Collection<? extends CachedPack> c) {
+ cachedPacks.addAll(c);
+ }
+
+ /**
* Assume a commit is available on the recipient's side.
* <p>
* In order to fetch from a bundle the recipient must have any assumed
@@ -187,19 +213,24 @@ public class BundleWriter {
try (PackWriter packWriter = newPackWriter()) {
packWriter.setObjectCountCallback(callback);
- final HashSet<ObjectId> inc = new HashSet<>();
- final HashSet<ObjectId> exc = new HashSet<>();
- inc.addAll(include.values());
- for (RevCommit r : assume)
- exc.add(r.getId());
packWriter.setIndexDisabled(true);
packWriter.setDeltaBaseAsOffset(true);
- packWriter.setThin(!exc.isEmpty());
packWriter.setReuseValidatingObjects(false);
- if (exc.isEmpty()) {
- packWriter.setTagTargets(tagTargets);
+ if (cachedPacks.isEmpty()) {
+ HashSet<ObjectId> inc = new HashSet<>();
+ HashSet<ObjectId> exc = new HashSet<>();
+ inc.addAll(include.values());
+ for (RevCommit r : assume) {
+ exc.add(r.getId());
+ }
+ if (exc.isEmpty()) {
+ packWriter.setTagTargets(tagTargets);
+ }
+ packWriter.setThin(!exc.isEmpty());
+ packWriter.preparePack(monitor, inc, exc);
+ } else {
+ packWriter.preparePack(cachedPacks);
}
- packWriter.preparePack(monitor, inc, exc);
final Writer w = new OutputStreamWriter(os, UTF_8);
w.write(TransportBundle.V2_BUNDLE_SIGNATURE);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FilterSpec.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FilterSpec.java
index d09b5579fa..a8cf849fed 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FilterSpec.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FilterSpec.java
@@ -10,6 +10,15 @@
package org.eclipse.jgit.transport;
+import static java.math.BigInteger.ZERO;
+import static java.util.Objects.requireNonNull;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
+import static org.eclipse.jgit.lib.Constants.OBJ_TAG;
+import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
+import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_FILTER;
+
+import java.math.BigInteger;
import java.text.MessageFormat;
import org.eclipse.jgit.annotations.Nullable;
@@ -24,11 +33,54 @@ import org.eclipse.jgit.internal.JGitText;
*/
public final class FilterSpec {
+ /** Immutable bit-set representation of a set of Git object types. */
+ static class ObjectTypes {
+ static ObjectTypes ALL = allow(OBJ_BLOB, OBJ_TREE, OBJ_COMMIT, OBJ_TAG);
+
+ private final BigInteger val;
+
+ private ObjectTypes(BigInteger val) {
+ this.val = requireNonNull(val);
+ }
+
+ static ObjectTypes allow(int... types) {
+ BigInteger bits = ZERO;
+ for (int type : types) {
+ bits = bits.setBit(type);
+ }
+ return new ObjectTypes(bits);
+ }
+
+ boolean contains(int type) {
+ return val.testBit(type);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ObjectTypes)) {
+ return false;
+ }
+
+ ObjectTypes other = (ObjectTypes) obj;
+ return other.val.equals(val);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return val.hashCode();
+ }
+ }
+
+ private final ObjectTypes types;
+
private final long blobLimit;
private final long treeDepthLimit;
- private FilterSpec(long blobLimit, long treeDepthLimit) {
+ private FilterSpec(ObjectTypes types, long blobLimit, long treeDepthLimit) {
+ this.types = requireNonNull(types);
this.blobLimit = blobLimit;
this.treeDepthLimit = treeDepthLimit;
}
@@ -53,7 +105,8 @@ public final class FilterSpec {
public static FilterSpec fromFilterLine(String filterLine)
throws PackProtocolException {
if (filterLine.equals("blob:none")) { //$NON-NLS-1$
- return FilterSpec.withBlobLimit(0);
+ return FilterSpec.withObjectTypes(
+ ObjectTypes.allow(OBJ_TREE, OBJ_COMMIT, OBJ_TAG));
} else if (filterLine.startsWith("blob:limit=")) { //$NON-NLS-1$
long blobLimit = -1;
try {
@@ -86,8 +139,18 @@ public final class FilterSpec {
}
/**
+ * @param types
+ * set of permitted object types, for use in "blob:none" and
+ * "object:none" filters
+ * @return a filter spec which restricts to objects of the specified types
+ */
+ static FilterSpec withObjectTypes(ObjectTypes types) {
+ return new FilterSpec(types, -1, -1);
+ }
+
+ /**
* @param blobLimit
- * the blob limit in a "blob:[limit]" or "blob:none" filter line
+ * the blob limit in a "blob:[limit]" filter line
* @return a filter spec which filters blobs above a certain size
*/
static FilterSpec withBlobLimit(long blobLimit) {
@@ -95,7 +158,7 @@ public final class FilterSpec {
throw new IllegalArgumentException(
"blobLimit cannot be negative: " + blobLimit); //$NON-NLS-1$
}
- return new FilterSpec(blobLimit, -1);
+ return new FilterSpec(ObjectTypes.ALL, blobLimit, -1);
}
/**
@@ -109,13 +172,25 @@ public final class FilterSpec {
throw new IllegalArgumentException(
"treeDepthLimit cannot be negative: " + treeDepthLimit); //$NON-NLS-1$
}
- return new FilterSpec(-1, treeDepthLimit);
+ return new FilterSpec(ObjectTypes.ALL, -1, treeDepthLimit);
}
/**
* A placeholder that indicates no filtering.
*/
- public static final FilterSpec NO_FILTER = new FilterSpec(-1, -1);
+ public static final FilterSpec NO_FILTER = new FilterSpec(ObjectTypes.ALL, -1, -1);
+
+ /**
+ * @param type
+ * a Git object type, such as
+ * {@link org.eclipse.jgit.lib.Constants#OBJ_BLOB}
+ * @return whether this filter allows objects of the specified type
+ *
+ * @since 5.9
+ */
+ public boolean allowsType(int type) {
+ return types.contains(type);
+ }
/**
* @return -1 if this filter does not filter blobs based on size, or a
@@ -138,7 +213,7 @@ public final class FilterSpec {
* @return true if this filter doesn't filter out anything
*/
public boolean isNoOp() {
- return blobLimit == -1 && treeDepthLimit == -1;
+ return types.equals(ObjectTypes.ALL) && blobLimit == -1 && treeDepthLimit == -1;
}
/**
@@ -146,14 +221,17 @@ public final class FilterSpec {
*/
@Nullable
public String filterLine() {
- if (blobLimit == 0) {
- return GitProtocolConstants.OPTION_FILTER + " blob:none"; //$NON-NLS-1$
+ if (isNoOp()) {
+ return null;
+ } else if (types.equals(ObjectTypes.allow(OBJ_TREE, OBJ_COMMIT, OBJ_TAG)) &&
+ blobLimit == -1 && treeDepthLimit == -1) {
+ return OPTION_FILTER + " blob:none"; //$NON-NLS-1$
+ } else if (types.equals(ObjectTypes.ALL) && blobLimit >= 0 && treeDepthLimit == -1) {
+ return OPTION_FILTER + " blob:limit=" + blobLimit; //$NON-NLS-1$
+ } else if (types.equals(ObjectTypes.ALL) && blobLimit == -1 && treeDepthLimit >= 0) {
+ return OPTION_FILTER + " tree:" + treeDepthLimit; //$NON-NLS-1$
+ } else {
+ throw new IllegalStateException();
}
-
- if (blobLimit > 0) {
- return GitProtocolConstants.OPTION_FILTER + " blob:limit=" + blobLimit; //$NON-NLS-1$
- }
-
- return null;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteSession.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteSession.java
index f9f50d46e7..dd4c967f91 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteSession.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteSession.java
@@ -4,7 +4,7 @@
* Copyright (C) 2009, Google, Inc.
* Copyright (C) 2009, JetBrains s.r.o.
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -18,26 +18,20 @@ package org.eclipse.jgit.transport;
import java.io.IOException;
/**
- * Create a remote "session" for executing remote commands.
- * <p>
- * Clients should subclass RemoteSession to create an alternate way for JGit to
- * execute remote commands. (The client application may already have this
- * functionality available.) Note that this class is just a factory for creating
- * remote processes. If the application already has a persistent connection to
- * the remote machine, RemoteSession may do nothing more than return a new
- * RemoteProcess when exec is called.
+ * An abstraction of a remote "session" for executing remote commands.
*/
public interface RemoteSession {
+
/**
- * Generate a new remote process to execute the given command. This function
- * should also start execution and may need to create the streams prior to
- * execution.
+ * Creates a new remote {@link Process} to execute the given command. The
+ * returned process's streams exist and are connected, and execution of the
+ * process is already started.
*
* @param commandName
* command to execute
* @param timeout
- * timeout value, in seconds, for command execution
- * @return a new remote process
+ * timeout value, in seconds, for creating the remote process
+ * @return a new remote process, already started
* @throws java.io.IOException
* may be thrown in several cases. For example, on problems
* opening input or output streams or on problems connecting or
@@ -48,7 +42,7 @@ public interface RemoteSession {
Process exec(String commandName, int timeout) throws IOException;
/**
- * Obtain an {@link FtpChannel} for performing FTP operations over this
+ * Obtains an {@link FtpChannel} for performing FTP operations over this
* {@link RemoteSession}. The default implementation returns {@code null}.
*
* @return the {@link FtpChannel}
@@ -59,7 +53,7 @@ public interface RemoteSession {
}
/**
- * Disconnect the remote session
+ * Disconnects the remote session.
*/
void disconnect();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
index ef845f4dce..e216a56ac6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -27,12 +27,15 @@ import org.eclipse.jgit.util.SystemReader;
* Different implementations of the session factory may be used to control
* communicating with the end-user as well as reading their personal SSH
* configuration settings, such as known hosts and private keys.
+ * </p>
* <p>
- * A {@link org.eclipse.jgit.transport.RemoteSession} must be returned to the
- * factory that created it. Callers are encouraged to retain the
- * SshSessionFactory for the duration of the period they are using the Session.
+ * A {@link RemoteSession} must be returned to the factory that created it.
+ * Callers are encouraged to retain the SshSessionFactory for the duration of
+ * the period they are using the session.
+ * </p>
*/
public abstract class SshSessionFactory {
+
private static SshSessionFactory INSTANCE = loadSshSessionFactory();
private static SshSessionFactory loadSshSessionFactory() {
@@ -43,12 +46,13 @@ public abstract class SshSessionFactory {
}
return null;
}
-
+
/**
- * Get the currently configured JVM-wide factory.
+ * Gets the currently configured JVM-wide factory.
* <p>
- * By default the factory will read from the user's <code>$HOME/.ssh</code>
- * and assume OpenSSH compatibility.
+ * By default the factory will read from the user's {@code $HOME/.ssh} and
+ * assume OpenSSH compatibility.
+ * </p>
*
* @return factory the current factory for this JVM.
*/
@@ -57,11 +61,11 @@ public abstract class SshSessionFactory {
}
/**
- * Change the JVM-wide factory to a different implementation.
+ * Changes the JVM-wide factory to a different implementation.
*
* @param newFactory
- * factory for future sessions to be created through. If null the
- * default factory will be restored.
+ * factory for future sessions to be created through; if
+ * {@code null} the default factory will be restored.
*/
public static void setInstance(SshSessionFactory newFactory) {
if (newFactory != null) {
@@ -85,26 +89,23 @@ public abstract class SshSessionFactory {
}
/**
- * Open (or reuse) a session to a host.
- * <p>
- * A reasonable UserInfo that can interact with the end-user (if necessary)
- * is installed on the returned session by this method.
- * <p>
- * The caller must connect the session by invoking <code>connect()</code> if
- * it has not already been connected.
+ * Opens (or reuses) a session to a host. The returned session is connected
+ * and authenticated and is ready for further use.
*
* @param uri
- * URI information about the remote host
+ * URI of the remote host to connect to
* @param credentialsProvider
- * provider to support authentication, may be null.
+ * provider to support authentication, may be {@code null} if no
+ * user input for authentication is needed
* @param fs
- * the file system abstraction which will be necessary to perform
- * certain file system operations.
+ * the file system abstraction to use for certain file
+ * operations, such as reading configuration files
* @param tms
- * Timeout value, in milliseconds.
- * @return a session that can contact the remote host.
+ * connection timeout for creating the session, in milliseconds
+ * @return a connected and authenticated session for communicating with the
+ * remote host given by the {@code uri}
* @throws org.eclipse.jgit.errors.TransportException
- * the session could not be created.
+ * if the session could not be created
*/
public abstract RemoteSession getSession(URIish uri,
CredentialsProvider credentialsProvider, FS fs, int tms)
@@ -120,7 +121,7 @@ public abstract class SshSessionFactory {
public abstract String getType();
/**
- * Close (or recycle) a session to a host.
+ * Closes (or recycles) a session to a host.
*
* @param session
* a session previously obtained from this factory's
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
index cc577fa11e..0b38159c09 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java
@@ -21,6 +21,7 @@ import java.util.Map;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.internal.storage.file.LazyObjectIdSetFile;
import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Config.SectionParser;
import org.eclipse.jgit.lib.ObjectChecker;
import org.eclipse.jgit.lib.ObjectIdSet;
@@ -60,11 +61,19 @@ public class TransferConfig {
}
/**
- * A git configuration variable for which versions of the Git protocol to prefer.
- * Used in protocol.version.
+ * A git configuration variable for which versions of the Git protocol to
+ * prefer. Used in protocol.version.
+ *
+ * @since 5.9
*/
- enum ProtocolVersion {
+ public enum ProtocolVersion {
+ /**
+ * Git wire protocol version 0 (the default).
+ */
V0("0"), //$NON-NLS-1$
+ /**
+ * Git wire protocol version 2.
+ */
V2("2"); //$NON-NLS-1$
final String name;
@@ -73,6 +82,15 @@ public class TransferConfig {
this.name = name;
}
+ /**
+ * Returns version number
+ *
+ * @return string version
+ */
+ public String version() {
+ return name;
+ }
+
@Nullable
static ProtocolVersion parse(@Nullable String name) {
if (name == null) {
@@ -177,7 +195,9 @@ public class TransferConfig {
"uploadpack", "allowreachablesha1inwant", false);
allowFilter = rc.getBoolean(
"uploadpack", "allowfilter", false);
- protocolVersion = ProtocolVersion.parse(rc.getString("protocol", null, "version"));
+ protocolVersion = ProtocolVersion.parse(rc
+ .getString(ConfigConstants.CONFIG_PROTOCOL_SECTION, null,
+ ConfigConstants.CONFIG_KEY_VERSION));
hideRefs = rc.getStringList("uploadpack", null, "hiderefs");
allowSidebandAll = rc.getBoolean(
"uploadpack", "allowsidebandall", false);
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 994af2607c..4c26dd0c40 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -2,7 +2,7 @@
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
* Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
* Copyright (C) 2010, Matthias Sohn <matthias.sohn@sap.com>
- * Copyright (C) 2012-2013, Robin Rosenberg and others
+ * Copyright (C) 2012-2020, Robin Rosenberg and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -522,6 +522,17 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
return state.options;
}
+ /**
+ * Retrieves the {@link Repository} this {@link WorkingTreeIterator}
+ * operates on.
+ *
+ * @return the {@link Repository}
+ * @since 5.9
+ */
+ public Repository getRepository() {
+ return repository;
+ }
+
/** {@inheritDoc} */
@Override
public int idOffset() {
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 91574efec4..bf7b753693 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -52,7 +52,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
-import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -186,12 +186,18 @@ public abstract class FS {
*/
public static final class FileStoreAttributes {
+ /**
+ * Marker to detect undefined values when reading from the config file.
+ */
private static final Duration UNDEFINED_DURATION = Duration
.ofNanos(Long.MAX_VALUE);
/**
* Fallback filesystem timestamp resolution. The worst case timestamp
* resolution on FAT filesystems is 2 seconds.
+ * <p>
+ * Must be at least 1 second.
+ * </p>
*/
public static final Duration FALLBACK_TIMESTAMP_RESOLUTION = Duration
.ofMillis(2000);
@@ -204,6 +210,25 @@ public abstract class FS {
public static final FileStoreAttributes FALLBACK_FILESTORE_ATTRIBUTES = new FileStoreAttributes(
FALLBACK_TIMESTAMP_RESOLUTION);
+ private static final long ONE_MICROSECOND = TimeUnit.MICROSECONDS
+ .toNanos(1);
+
+ private static final long ONE_MILLISECOND = TimeUnit.MILLISECONDS
+ .toNanos(1);
+
+ private static final long ONE_SECOND = TimeUnit.SECONDS.toNanos(1);
+
+ /**
+ * Minimum file system timestamp resolution granularity to check, in
+ * nanoseconds. Should be a positive power of ten smaller than
+ * {@link #ONE_SECOND}. Must be strictly greater than zero, i.e.,
+ * minimum value is 1 nanosecond.
+ * <p>
+ * Currently set to 1 microsecond, but could also be lower still.
+ * </p>
+ */
+ private static final long MINIMUM_RESOLUTION_NANOS = ONE_MICROSECOND;
+
private static final String JAVA_VERSION_PREFIX = System
.getProperty("java.vendor") + '|' //$NON-NLS-1$
+ System.getProperty("java.version") + '|'; //$NON-NLS-1$
@@ -235,9 +260,10 @@ public abstract class FS {
* @see java.util.concurrent.Executors#newCachedThreadPool()
*/
private static final Executor FUTURE_RUNNER = new ThreadPoolExecutor(0,
- 5, 30L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(),
+ 5, 30L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
runnable -> {
- Thread t = new Thread(runnable, "FileStoreAttributeReader-" //$NON-NLS-1$
+ Thread t = new Thread(runnable,
+ "JGit-FileStoreAttributeReader-" //$NON-NLS-1$
+ threadNumber.getAndIncrement());
// Make sure these threads don't prevent application/JVM
// shutdown.
@@ -246,12 +272,34 @@ public abstract class FS {
});
/**
+ * Use a separate executor with at most one thread to synchronize
+ * writing to the config. We write asynchronously since the config
+ * itself might be on a different file system, which might otherwise
+ * lead to locking problems.
+ * <p>
+ * Writing the config must not use a daemon thread, otherwise we may
+ * leave an inconsistent state on disk when the JVM shuts down. Use a
+ * small keep-alive time to avoid delays on shut-down.
+ * </p>
+ */
+ private static final Executor SAVE_RUNNER = new ThreadPoolExecutor(0, 1,
+ 1L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(),
+ runnable -> {
+ Thread t = new Thread(runnable,
+ "JGit-FileStoreAttributeWriter-" //$NON-NLS-1$
+ + threadNumber.getAndIncrement());
+ // Make sure these threads do finish
+ t.setDaemon(false);
+ return t;
+ });
+
+ /**
* Whether FileStore attributes should be determined asynchronously
*
* @param async
* whether FileStore attributes should be determined
- * asynchronously. If false access to cached attributes may block
- * for some seconds for the first call per FileStore
+ * asynchronously. If false access to cached attributes may
+ * block for some seconds for the first call per FileStore
* @since 5.6.2
*/
public static void setBackground(boolean async) {
@@ -294,6 +342,10 @@ public abstract class FS {
return cached;
}
FileStoreAttributes attrs = getFileStoreAttributes(dir);
+ if (attrs == null) {
+ // Don't cache, result might be late
+ return FALLBACK_FILESTORE_ATTRIBUTES;
+ }
attrCacheByPath.put(dir, attrs);
return attrs;
} catch (SecurityException e) {
@@ -367,7 +419,9 @@ public abstract class FS {
if (LOG.isDebugEnabled()) {
LOG.debug(c.toString());
}
- saveToConfig(s, c);
+ FileStoreAttributes newAttrs = c;
+ SAVE_RUNNER.execute(
+ () -> saveToConfig(s, newAttrs));
}
attributes = Optional.of(c);
} finally {
@@ -382,12 +436,16 @@ public abstract class FS {
});
// even if measuring in background wait a little - if the result
// arrives, it's better than returning the large fallback
- Optional<FileStoreAttributes> d = background.get() ? f.get(
+ boolean runInBackground = background.get();
+ Optional<FileStoreAttributes> d = runInBackground ? f.get(
100, TimeUnit.MILLISECONDS) : f.get();
if (d.isPresent()) {
return d.get();
+ } else if (runInBackground) {
+ // return null until measurement is finished
+ return null;
}
- // return fallback until measurement is finished
+ // fall through and return fallback
} catch (IOException | InterruptedException
| ExecutionException | CancellationException e) {
LOG.error(e.getMessage(), e);
@@ -467,24 +525,21 @@ public abstract class FS {
private static Optional<Duration> measureFsTimestampResolution(
FileStore s, Path dir) {
- LOG.debug("{}: start measure timestamp resolution {} in {}", //$NON-NLS-1$
- Thread.currentThread(), s, dir);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("{}: start measure timestamp resolution {} in {}", //$NON-NLS-1$
+ Thread.currentThread(), s, dir);
+ }
Path probe = dir.resolve(".probe-" + UUID.randomUUID()); //$NON-NLS-1$
try {
Files.createFile(probe);
- FileTime t1 = Files.getLastModifiedTime(probe);
- FileTime t2 = t1;
- Instant t1i = t1.toInstant();
- for (long i = 1; t2.compareTo(t1) <= 0; i += 1 + i / 20) {
- Files.setLastModifiedTime(probe,
- FileTime.from(t1i.plusNanos(i * 1000)));
- t2 = Files.getLastModifiedTime(probe);
- }
- Duration fsResolution = Duration.between(t1.toInstant(), t2.toInstant());
+ Duration fsResolution = getFsResolution(s, dir, probe);
Duration clockResolution = measureClockResolution();
fsResolution = fsResolution.plus(clockResolution);
- LOG.debug("{}: end measure timestamp resolution {} in {}", //$NON-NLS-1$
- Thread.currentThread(), s, dir);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(
+ "{}: end measure timestamp resolution {} in {}; got {}", //$NON-NLS-1$
+ Thread.currentThread(), s, dir, fsResolution);
+ }
return Optional.of(fsResolution);
} catch (SecurityException e) {
// Log it here; most likely deleteProbe() below will also run
@@ -501,6 +556,92 @@ public abstract class FS {
return Optional.empty();
}
+ private static Duration getFsResolution(FileStore s, Path dir,
+ Path probe) throws IOException {
+ File probeFile = probe.toFile();
+ FileTime t1 = Files.getLastModifiedTime(probe);
+ Instant t1i = t1.toInstant();
+ FileTime t2;
+ Duration last = FALLBACK_TIMESTAMP_RESOLUTION;
+ long minScale = MINIMUM_RESOLUTION_NANOS;
+ long scale = ONE_SECOND;
+ long high = TimeUnit.MILLISECONDS.toSeconds(last.toMillis());
+ long low = 0;
+ // Try up-front at microsecond and millisecond
+ long[] tries = { ONE_MICROSECOND, ONE_MILLISECOND };
+ for (long interval : tries) {
+ if (interval >= ONE_MILLISECOND) {
+ probeFile.setLastModified(
+ t1i.plusNanos(interval).toEpochMilli());
+ } else {
+ Files.setLastModifiedTime(probe,
+ FileTime.from(t1i.plusNanos(interval)));
+ }
+ t2 = Files.getLastModifiedTime(probe);
+ if (t2.compareTo(t1) > 0) {
+ Duration diff = Duration.between(t1i, t2.toInstant());
+ if (!diff.isZero() && !diff.isNegative()
+ && diff.compareTo(last) < 0) {
+ scale = interval;
+ high = 1;
+ last = diff;
+ break;
+ }
+ } else {
+ // Makes no sense going below
+ minScale = Math.max(minScale, interval);
+ }
+ }
+ // Binary search loop
+ while (high > low) {
+ long mid = (high + low) / 2;
+ if (mid == 0) {
+ // Smaller than current scale. Adjust scale.
+ long newScale = scale / 10;
+ if (newScale < minScale) {
+ break;
+ }
+ high *= scale / newScale;
+ low *= scale / newScale;
+ scale = newScale;
+ mid = (high + low) / 2;
+ }
+ long delta = mid * scale;
+ if (scale >= ONE_MILLISECOND) {
+ probeFile.setLastModified(
+ t1i.plusNanos(delta).toEpochMilli());
+ } else {
+ Files.setLastModifiedTime(probe,
+ FileTime.from(t1i.plusNanos(delta)));
+ }
+ t2 = Files.getLastModifiedTime(probe);
+ int cmp = t2.compareTo(t1);
+ if (cmp > 0) {
+ high = mid;
+ Duration diff = Duration.between(t1i, t2.toInstant());
+ if (diff.isZero() || diff.isNegative()) {
+ LOG.warn(JGitText.get().logInconsistentFiletimeDiff,
+ Thread.currentThread(), s, dir, t2, t1, diff,
+ last);
+ break;
+ } else if (diff.compareTo(last) > 0) {
+ LOG.warn(JGitText.get().logLargerFiletimeDiff,
+ Thread.currentThread(), s, dir, diff, last);
+ break;
+ }
+ last = diff;
+ } else if (cmp < 0) {
+ LOG.warn(JGitText.get().logSmallerFiletime,
+ Thread.currentThread(), s, dir, t2, t1, last);
+ break;
+ } else {
+ // No discernible difference
+ low = mid + 1;
+ }
+ }
+ return last;
+ }
+
private static Duration measureClockResolution() {
Duration clockResolution = Duration.ZERO;
for (int i = 0; i < 10; i++) {
@@ -1356,7 +1497,7 @@ public abstract class FS {
String v;
try {
v = readPipe(gitExe.getParentFile(),
- new String[] { "git", "--version" }, //$NON-NLS-1$ //$NON-NLS-2$
+ new String[] { gitExe.getPath(), "--version" }, //$NON-NLS-1$
Charset.defaultCharset().name());
} catch (CommandFailedException e) {
LOG.warn(e.getMessage());
@@ -1375,7 +1516,8 @@ public abstract class FS {
String w;
try {
w = readPipe(gitExe.getParentFile(),
- new String[] { "git", "config", "--system", "--edit" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ new String[] { gitExe.getPath(), "config", "--system", //$NON-NLS-1$ //$NON-NLS-2$
+ "--edit" }, //$NON-NLS-1$
Charset.defaultCharset().name(), env);
} catch (CommandFailedException e) {
LOG.warn(e.getMessage());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
index c9d2770b18..fb63dc02bb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
@@ -54,6 +54,8 @@ import org.slf4j.LoggerFactory;
public class FS_POSIX extends FS {
private static final Logger LOG = LoggerFactory.getLogger(FS_POSIX.class);
+ private static final String DEFAULT_GIT_LOCATION = "/usr/bin/git"; //$NON-NLS-1$
+
private static final int DEFAULT_UMASK = 0022;
private volatile int umask = -1;
@@ -138,24 +140,46 @@ public class FS_POSIX extends FS {
String path = SystemReader.getInstance().getenv("PATH"); //$NON-NLS-1$
File gitExe = searchPath(path, "git"); //$NON-NLS-1$
- if (gitExe == null) {
- if (SystemReader.getInstance().isMacOS()) {
+ if (SystemReader.getInstance().isMacOS()) {
+ if (gitExe == null
+ || DEFAULT_GIT_LOCATION.equals(gitExe.getPath())) {
if (searchPath(path, "bash") != null) { //$NON-NLS-1$
// On MacOSX, PATH is shorter when Eclipse is launched from the
// Finder than from a terminal. Therefore try to launch bash as a
// login shell and search using that.
- String w;
try {
- w = readPipe(userHome(),
+ String w = readPipe(userHome(),
new String[]{"bash", "--login", "-c", "which git"}, // //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
Charset.defaultCharset().name());
+ if (!StringUtils.isEmptyOrNull(w)) {
+ gitExe = new File(w);
+ }
} catch (CommandFailedException e) {
LOG.warn(e.getMessage());
- return null;
}
- if (!StringUtils.isEmptyOrNull(w)) {
- gitExe = new File(w);
+ }
+ }
+ if (gitExe != null
+ && DEFAULT_GIT_LOCATION.equals(gitExe.getPath())) {
+ // If we still have the default git exe, it's an XCode wrapper
+ // that may prompt the user to install the XCode command line
+ // tools if not already present. Avoid the prompt by returning
+ // null if no XCode git is there.
+ try {
+ String w = readPipe(userHome(),
+ new String[] { "xcode-select", "-p" }, //$NON-NLS-1$ //$NON-NLS-2$
+ Charset.defaultCharset().name());
+ if (StringUtils.isEmptyOrNull(w)) {
+ gitExe = null;
+ } else {
+ File realGitExe = new File(new File(w),
+ DEFAULT_GIT_LOCATION.substring(1));
+ if (!realGitExe.exists()) {
+ gitExe = null;
+ }
}
+ } catch (CommandFailedException e) {
+ gitExe = null;
}
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SshSupport.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SshSupport.java
index a151cd336f..e29704158d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SshSupport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SshSupport.java
@@ -11,6 +11,7 @@ package org.eclipse.jgit.util;
import java.io.IOException;
import java.text.MessageFormat;
+import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.errors.CommandFailedException;
@@ -61,11 +62,21 @@ public class SshSupport {
CommandFailedException failure = null;
@SuppressWarnings("resource")
MessageWriter stderr = new MessageWriter();
+ @SuppressWarnings("resource")
+ MessageWriter stdout = new MessageWriter();
String out;
- try (MessageWriter stdout = new MessageWriter()) {
+ try {
+ long start = System.nanoTime();
session = SshSessionFactory.getInstance().getSession(sshUri,
provider, fs, 1000 * timeout);
- process = session.exec(command, 0);
+ int commandTimeout = timeout;
+ if (timeout > 0) {
+ commandTimeout = checkTimeout(command, timeout, start);
+ }
+ process = session.exec(command, commandTimeout);
+ if (timeout > 0) {
+ commandTimeout = checkTimeout(command, timeout, start);
+ }
errorThread = new StreamCopyThread(process.getErrorStream(),
stderr.getRawStream());
errorThread.start();
@@ -73,9 +84,15 @@ public class SshSupport {
stdout.getRawStream());
outThread.start();
try {
- // waitFor with timeout has a bug - JSch' exitValue() throws the
- // wrong exception type :(
- if (process.waitFor() == 0) {
+ boolean finished = false;
+ if (timeout <= 0) {
+ process.waitFor();
+ finished = true;
+ } else {
+ finished = process.waitFor(commandTimeout,
+ TimeUnit.SECONDS);
+ }
+ if (finished) {
out = stdout.toString();
} else {
out = null; // still running after timeout
@@ -103,15 +120,26 @@ public class SshSupport {
}
}
if (process != null) {
- if (process.exitValue() != 0) {
- failure = new CommandFailedException(process.exitValue(),
+ try {
+ if (process.exitValue() != 0) {
+ failure = new CommandFailedException(
+ process.exitValue(),
+ MessageFormat.format(
+ JGitText.get().sshCommandFailed,
+ command, stderr.toString()));
+ }
+ // It was successful after all
+ out = stdout.toString();
+ } catch (IllegalThreadStateException e) {
+ failure = new CommandFailedException(0,
MessageFormat.format(
- JGitText.get().sshCommandFailed, command,
- stderr.toString()));
+ JGitText.get().sshCommandTimeout, command,
+ Integer.valueOf(timeout)));
}
process.destroy();
}
stderr.close();
+ stdout.close();
if (session != null) {
SshSessionFactory.getInstance().releaseSession(session);
}
@@ -122,4 +150,17 @@ public class SshSupport {
return out;
}
+ private static int checkTimeout(String command, int timeout, long since)
+ throws CommandFailedException {
+ long elapsed = System.nanoTime() - since;
+ int newTimeout = timeout
+ - (int) TimeUnit.NANOSECONDS.toSeconds(elapsed);
+ if (newTimeout <= 0) {
+ // All time used up for connecting the session
+ throw new CommandFailedException(0,
+ MessageFormat.format(JGitText.get().sshCommandTimeout,
+ command, Integer.valueOf(timeout)));
+ }
+ return newTimeout;
+ }
}
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 bcb8380625..447f417e7e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java
@@ -17,7 +17,6 @@ import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
-import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -116,11 +115,9 @@ public abstract class SystemReader {
.getAbsolutePath();
}
try {
- Path xdgHomePath = Paths.get(configHomePath);
- Files.createDirectories(xdgHomePath);
- return xdgHomePath;
- } catch (IOException | InvalidPathException e) {
- LOG.error(JGitText.get().createXDGConfigHomeFailed,
+ return Paths.get(configHomePath);
+ } catch (InvalidPathException e) {
+ LOG.error(JGitText.get().logXDGConfigHomeInvalid,
configHomePath, e);
}
return null;
@@ -130,16 +127,9 @@ public abstract class SystemReader {
public FileBasedConfig openJGitConfig(Config parent, FS fs) {
Path xdgPath = getXDGConfigHome(fs);
if (xdgPath != null) {
- Path configPath = null;
- try {
- configPath = xdgPath.resolve("jgit"); //$NON-NLS-1$
- Files.createDirectories(configPath);
- configPath = configPath.resolve(Constants.CONFIG);
- return new FileBasedConfig(parent, configPath.toFile(), fs);
- } catch (IOException e) {
- LOG.error(JGitText.get().createJGitConfigFailed, configPath,
- e);
- }
+ Path configPath = xdgPath.resolve("jgit") //$NON-NLS-1$
+ .resolve(Constants.CONFIG);
+ return new FileBasedConfig(parent, configPath.toFile(), fs);
}
return new FileBasedConfig(parent,
new File(fs.userHome(), ".jgitconfig"), fs); //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java
index 8c9b1bf5cc..0e335a9dc4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2010, 2013 Marc Strapetz <marc.strapetz@syntevo.com>
- * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com> and others
+ * Copyright (C) 2015, 2020 Ivan Motsch <ivan.motsch@bsiag.com> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -13,26 +13,58 @@ package org.eclipse.jgit.util.io;
import java.io.IOException;
import java.io.InputStream;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.Set;
import org.eclipse.jgit.diff.RawText;
/**
* An InputStream that normalizes CRLF to LF.
- *
- * Existing single CR are not changed to LF, but retained as is.
- *
- * Optionally, a binary check on the first 8000 bytes is performed and in case
- * of binary files, canonicalization is turned off (for the complete file).
* <p>
- * This is the former EolCanonicalizingInputStream with a new name in order to
- * have same naming for all LF / CRLF streams
+ * Existing single CR are not changed to LF but are retained as is.
+ * </p>
+ * <p>
+ * Optionally, a binary check on the first 8kB is performed and in case of
+ * binary files, canonicalization is turned off (for the complete file). If
+ * binary checking determines that the input is CR/LF-delimited text and the
+ * stream has been created for checkout, canonicalization is also turned off.
+ * </p>
*
* @since 4.3
*/
public class AutoLFInputStream extends InputStream {
+
+ // This is the former EolCanonicalizingInputStream with a new name in order
+ // to have same naming for all LF / CRLF streams.
+
+ /**
+ * Flags for controlling auto-detection of binary vs. text content (for
+ * text=auto).
+ *
+ * @since 5.9
+ */
+ public enum StreamFlag {
+ /**
+ * Check the first 8kB for binary content and switch off
+ * canonicalization off for the whole file if so.
+ */
+ DETECT_BINARY,
+ /**
+ * If {@link #DETECT_BINARY} is set, throw an {@link IsBinaryException}
+ * if binary content is detected.
+ */
+ ABORT_IF_BINARY,
+ /**
+ * If {@link #DETECT_BINARY} is set and content is found to be CR-LF
+ * delimited text, switch off canonicalization.
+ */
+ FOR_CHECKOUT
+ }
+
private final byte[] single = new byte[1];
- private final byte[] buf = new byte[8096];
+ private final byte[] buf = new byte[8 * 1024];
private final InputStream in;
@@ -40,11 +72,23 @@ public class AutoLFInputStream extends InputStream {
private int ptr;
+ /**
+ * Set to {@code true} if no CR/LF processing is to be done: if the input is
+ * binary data, or CR/LF-delimited text and {@link StreamFlag#FOR_CHECKOUT}
+ * was given.
+ */
+ private boolean passAsIs;
+
+ /**
+ * Set to {@code true} if the input was detected to be binary data.
+ */
private boolean isBinary;
private boolean detectBinary;
- private boolean abortIfBinary;
+ private final boolean abortIfBinary;
+
+ private final boolean forCheckout;
/**
* A special exception thrown when {@link AutoLFInputStream} is told to
@@ -62,20 +106,64 @@ public class AutoLFInputStream extends InputStream {
}
/**
- * Creates a new InputStream, wrapping the specified stream
+ * Factory method for creating an {@link AutoLFInputStream} with the
+ * specified {@link StreamFlag flags}.
+ *
+ * @param in
+ * raw input stream
+ * @param flags
+ * {@link StreamFlag}s controlling the stream behavior
+ * @return a new {@link AutoLFInputStream}
+ * @since 5.9
+ */
+ public static AutoLFInputStream create(InputStream in,
+ StreamFlag... flags) {
+ if (flags == null) {
+ return new AutoLFInputStream(in, null);
+ }
+ EnumSet<StreamFlag> set = EnumSet.noneOf(StreamFlag.class);
+ set.addAll(Arrays.asList(flags));
+ return new AutoLFInputStream(in, set);
+ }
+
+ /**
+ * Creates a new InputStream, wrapping the specified stream.
+ *
+ * @param in
+ * raw input stream
+ * @param flags
+ * {@link StreamFlag}s controlling the stream behavior;
+ * {@code null} is treated as an empty set
+ * @since 5.9
+ */
+ public AutoLFInputStream(InputStream in, Set<StreamFlag> flags) {
+ this.in = in;
+ this.detectBinary = flags != null
+ && flags.contains(StreamFlag.DETECT_BINARY);
+ this.abortIfBinary = flags != null
+ && flags.contains(StreamFlag.ABORT_IF_BINARY);
+ this.forCheckout = flags != null
+ && flags.contains(StreamFlag.FOR_CHECKOUT);
+ }
+
+ /**
+ * Creates a new InputStream, wrapping the specified stream.
*
* @param in
* raw input stream
* @param detectBinary
* whether binaries should be detected
* @since 2.0
+ * @deprecated since 5.9, use {@link #create(InputStream, StreamFlag...)}
+ * instead
*/
+ @Deprecated
public AutoLFInputStream(InputStream in, boolean detectBinary) {
this(in, detectBinary, false);
}
/**
- * Creates a new InputStream, wrapping the specified stream
+ * Creates a new InputStream, wrapping the specified stream.
*
* @param in
* raw input stream
@@ -84,12 +172,16 @@ public class AutoLFInputStream extends InputStream {
* @param abortIfBinary
* throw an IOException if the file is binary
* @since 3.3
+ * @deprecated since 5.9, use {@link #create(InputStream, StreamFlag...)}
+ * instead
*/
+ @Deprecated
public AutoLFInputStream(InputStream in, boolean detectBinary,
boolean abortIfBinary) {
this.in = in;
this.detectBinary = detectBinary;
this.abortIfBinary = abortIfBinary;
+ this.forCheckout = false;
}
/** {@inheritDoc} */
@@ -118,7 +210,7 @@ public class AutoLFInputStream extends InputStream {
}
byte b = buf[ptr++];
- if (isBinary || b != '\r') {
+ if (passAsIs || b != '\r') {
// Logic for binary files ends here
bs[i++] = b;
continue;
@@ -170,9 +262,14 @@ public class AutoLFInputStream extends InputStream {
}
if (detectBinary) {
isBinary = RawText.isBinary(buf, cnt);
+ passAsIs = isBinary;
detectBinary = false;
- if (isBinary && abortIfBinary)
+ if (isBinary && abortIfBinary) {
throw new IsBinaryException();
+ }
+ if (!passAsIs && forCheckout) {
+ passAsIs = RawText.isCrLfText(buf, cnt);
+ }
}
ptr = 0;
return true;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFOutputStream.java
index e235aa0ed4..195fdb4213 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFOutputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFOutputStream.java
@@ -1,43 +1,12 @@
/*
* Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com>
+ * Copyright (C) 2020, Thomas Wolf <thomas.wolf@paranor.ch> and others
*
- * 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
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://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.
+ * SPDX-License-Identifier: BSD-3-Clause
*/
package org.eclipse.jgit.util.io;
@@ -49,11 +18,15 @@ import org.eclipse.jgit.diff.RawText;
/**
* An OutputStream that reduces CRLF to LF.
- *
+ * <p>
* Existing single CR are not changed to LF, but retained as is.
- *
+ * </p>
+ * <p>
* A binary check on the first 8000 bytes is performed and in case of binary
- * files, canonicalization is turned off (for the complete file).
+ * files, canonicalization is turned off (for the complete file). If the binary
+ * check determines that the input is not binary but text with CR/LF,
+ * canonicalization is also turned off.
+ * </p>
*
* @since 4.3
*/
@@ -76,9 +49,7 @@ public class AutoLFOutputStream extends OutputStream {
private boolean isBinary;
/**
- * <p>
* Constructor for AutoLFOutputStream.
- * </p>
*
* @param out
* an {@link java.io.OutputStream} object.
@@ -88,9 +59,7 @@ public class AutoLFOutputStream extends OutputStream {
}
/**
- * <p>
* Constructor for AutoLFOutputStream.
- * </p>
*
* @param out
* an {@link java.io.OutputStream} object.
@@ -123,14 +92,11 @@ public class AutoLFOutputStream extends OutputStream {
public void write(byte[] b, int startOff, int startLen)
throws IOException {
final int overflow = buffer(b, startOff, startLen);
- if (overflow < 0) {
+ if (overflow <= 0) {
return;
}
final int off = startOff + startLen - overflow;
final int len = overflow;
- if (len == 0) {
- return;
- }
int lastw = off;
if (isBinary) {
out.write(b, off, len);
@@ -190,6 +156,9 @@ public class AutoLFOutputStream extends OutputStream {
private void decideMode() throws IOException {
if (detectBinary) {
isBinary = RawText.isBinary(binbuf, binbufcnt);
+ if (!isBinary) {
+ isBinary = RawText.isCrLfText(binbuf, binbufcnt);
+ }
detectBinary = false;
}
int cachedLen = binbufcnt;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java
index c33c869b64..88ee2aee88 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com> and others
+ * Copyright (C) 2015, 2020 Ivan Motsch <ivan.motsch@bsiag.com> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -12,12 +12,14 @@ package org.eclipse.jgit.util.io;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.EnumSet;
import org.eclipse.jgit.attributes.Attributes;
import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
import org.eclipse.jgit.treewalk.WorkingTreeOptions;
import org.eclipse.jgit.util.SystemReader;
+import org.eclipse.jgit.util.io.AutoLFInputStream.StreamFlag;
/**
* Utility used to create input and output stream wrappers for
@@ -71,7 +73,7 @@ public final class EolStreamTypeUtil {
/**
* Wrap the input stream depending on
- * {@link org.eclipse.jgit.lib.CoreConfig.EolStreamType}
+ * {@link org.eclipse.jgit.lib.CoreConfig.EolStreamType}.
*
* @param in
* original stream
@@ -82,15 +84,38 @@ public final class EolStreamTypeUtil {
*/
public static InputStream wrapInputStream(InputStream in,
EolStreamType conversion) {
+ return wrapInputStream(in, conversion, false);
+ }
+
+ /**
+ * Wrap the input stream depending on
+ * {@link org.eclipse.jgit.lib.CoreConfig.EolStreamType}.
+ *
+ * @param in
+ * original stream
+ * @param conversion
+ * to be performed
+ * @param forCheckout
+ * whether the stream is for checking out from the repository
+ * @return the converted stream depending on
+ * {@link org.eclipse.jgit.lib.CoreConfig.EolStreamType}
+ * @since 5.9
+ */
+ public static InputStream wrapInputStream(InputStream in,
+ EolStreamType conversion, boolean forCheckout) {
switch (conversion) {
case TEXT_CRLF:
return new AutoCRLFInputStream(in, false);
case TEXT_LF:
- return new AutoLFInputStream(in, false);
+ return AutoLFInputStream.create(in);
case AUTO_CRLF:
return new AutoCRLFInputStream(in, true);
case AUTO_LF:
- return new AutoLFInputStream(in, true);
+ EnumSet<StreamFlag> flags = forCheckout
+ ? EnumSet.of(StreamFlag.DETECT_BINARY,
+ StreamFlag.FOR_CHECKOUT)
+ : EnumSet.of(StreamFlag.DETECT_BINARY);
+ return new AutoLFInputStream(in, flags);
default:
return in;
}
@@ -98,7 +123,7 @@ public final class EolStreamTypeUtil {
/**
* Wrap the output stream depending on
- * {@link org.eclipse.jgit.lib.CoreConfig.EolStreamType}
+ * {@link org.eclipse.jgit.lib.CoreConfig.EolStreamType}.
*
* @param out
* original stream