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_filters45
-rw-r--r--org.eclipse.jgit/BUILD2
-rw-r--r--org.eclipse.jgit/META-INF/MANIFEST.MF85
-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.properties2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java152
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java34
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java47
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java22
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/errors/TooLargeObjectInPackException.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffAlgorithm.java119
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/Edit.java15
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java37
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java141
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java89
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java94
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java22
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java33
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java139
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/LargePackedWholeObject.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java65
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java24
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java67
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java195
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java103
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java18
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapWalker.java59
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchingProgressMonitor.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java27
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/SubmoduleConfig.java13
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/TextProgressMonitor.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/WorkQueue.java (renamed from org.eclipse.jgit/src/org/eclipse/jgit/lib/WorkQueue.java)9
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeStrategy.java18
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java60
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java13
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java51
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyOneSided.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyRecursive.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyResolve.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java15
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/ThreeWayMerger.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java90
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java39
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java65
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java94
58 files changed, 1799 insertions, 434 deletions
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index 40255670ad..7c175e3905 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -3,8 +3,8 @@
<resource path="META-INF/MANIFEST.MF">
<filter id="924844039">
<message_arguments>
- <message_argument value="4.7.3"/>
- <message_argument value="4.7.0"/>
+ <message_argument value="4.8.1"/>
+ <message_argument value="4.8.0"/>
</message_arguments>
</filter>
</resource>
@@ -12,26 +12,23 @@
<filter id="336658481">
<message_arguments>
<message_argument value="org.eclipse.jgit.lib.ConfigConstants"/>
- <message_argument value="CONFIG_KEY_AUTODETACH"/>
- </message_arguments>
- </filter>
- <filter id="336658481">
- <message_arguments>
- <message_argument value="org.eclipse.jgit.lib.ConfigConstants"/>
- <message_argument value="CONFIG_KEY_LOGEXPIRY"/>
+ <message_argument value="CONFIG_KEY_SUPPORTSATOMICFILECREATION"/>
</message_arguments>
</filter>
- <filter id="336658481">
+ <filter id="1141899266">
<message_arguments>
- <message_argument value="org.eclipse.jgit.lib.ConfigConstants"/>
+ <message_argument value="4.5"/>
+ <message_argument value="4.8"/>
<message_argument value="CONFIG_KEY_SUPPORTSATOMICFILECREATION"/>
</message_arguments>
</filter>
- <filter id="1141899266">
+ </resource>
+ <resource path="src/org/eclipse/jgit/lib/Constants.java" type="org.eclipse.jgit.lib.Constants">
+ <filter comment="LOCK_SUFFIX was backported to 4.7.3" id="1141899266">
<message_arguments>
- <message_argument value="4.5"/>
<message_argument value="4.7"/>
- <message_argument value="CONFIG_KEY_SUPPORTSATOMICFILECREATION"/>
+ <message_argument value="4.8"/>
+ <message_argument value="LOCK_SUFFIX"/>
</message_arguments>
</filter>
</resource>
@@ -39,16 +36,32 @@
<filter id="1141899266">
<message_arguments>
<message_argument value="4.5"/>
- <message_argument value="4.7"/>
+ <message_argument value="4.8"/>
<message_argument value="createNewFile(File)"/>
</message_arguments>
</filter>
<filter id="1141899266">
<message_arguments>
<message_argument value="4.5"/>
- <message_argument value="4.7"/>
+ <message_argument value="4.8"/>
<message_argument value="supportsAtomicCreateNewFile()"/>
</message_arguments>
</filter>
+ <filter id="1141899266">
+ <message_arguments>
+ <message_argument value="4.7"/>
+ <message_argument value="4.8"/>
+ <message_argument value="createNewFileAtomic(File)"/>
+ </message_arguments>
+ </filter>
+ </resource>
+ <resource path="src/org/eclipse/jgit/util/FS.java" type="org.eclipse.jgit.util.FS$LockToken">
+ <filter id="1141899266">
+ <message_arguments>
+ <message_argument value="4.7"/>
+ <message_argument value="4.8"/>
+ <message_argument value="LockToken"/>
+ </message_arguments>
+ </filter>
</resource>
</component>
diff --git a/org.eclipse.jgit/BUILD b/org.eclipse.jgit/BUILD
index 75f4fe69c1..a8a53f2742 100644
--- a/org.eclipse.jgit/BUILD
+++ b/org.eclipse.jgit/BUILD
@@ -5,7 +5,7 @@ INSECURE_CIPHER_FACTORY = [
]
SRCS = glob(
- ["src/**"],
+ ["src/**/*.java"],
exclude = INSECURE_CIPHER_FACTORY,
)
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index 4b3732395c..9b6a9f182a 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -2,12 +2,12 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %plugin_name
Bundle-SymbolicName: org.eclipse.jgit
-Bundle-Version: 4.7.4.qualifier
+Bundle-Version: 4.8.1.qualifier
Bundle-Localization: plugin
Bundle-Vendor: %provider_name
Bundle-ActivationPolicy: lazy
-Export-Package: org.eclipse.jgit.annotations;version="4.7.4",
- org.eclipse.jgit.api;version="4.7.4";
+Export-Package: org.eclipse.jgit.annotations;version="4.8.1",
+ org.eclipse.jgit.api;version="4.8.1";
uses:="org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.diff,
@@ -21,51 +21,51 @@ Export-Package: org.eclipse.jgit.annotations;version="4.7.4",
org.eclipse.jgit.submodule,
org.eclipse.jgit.transport,
org.eclipse.jgit.merge",
- org.eclipse.jgit.api.errors;version="4.7.4";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
- org.eclipse.jgit.attributes;version="4.7.4",
- org.eclipse.jgit.blame;version="4.7.4";
+ org.eclipse.jgit.api.errors;version="4.8.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.errors",
+ org.eclipse.jgit.attributes;version="4.8.1",
+ org.eclipse.jgit.blame;version="4.8.1";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.diff",
- org.eclipse.jgit.diff;version="4.7.4";
+ org.eclipse.jgit.diff;version="4.8.1";
uses:="org.eclipse.jgit.patch,
org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.util",
- org.eclipse.jgit.dircache;version="4.7.4";
+ org.eclipse.jgit.dircache;version="4.8.1";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.util,
org.eclipse.jgit.events,
org.eclipse.jgit.attributes",
- org.eclipse.jgit.errors;version="4.7.4";
+ org.eclipse.jgit.errors;version="4.8.1";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.internal.storage.pack,
org.eclipse.jgit.transport,
org.eclipse.jgit.dircache",
- org.eclipse.jgit.events;version="4.7.4";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.fnmatch;version="4.7.4",
- org.eclipse.jgit.gitrepo;version="4.7.4";
+ org.eclipse.jgit.events;version="4.8.1";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.fnmatch;version="4.8.1",
+ org.eclipse.jgit.gitrepo;version="4.8.1";
uses:="org.eclipse.jgit.api,
org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
org.xml.sax.helpers,
org.xml.sax",
- org.eclipse.jgit.gitrepo.internal;version="4.7.4";x-internal:=true,
- org.eclipse.jgit.hooks;version="4.7.4";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.ignore;version="4.7.4",
- org.eclipse.jgit.ignore.internal;version="4.7.4";x-friends:="org.eclipse.jgit.test",
- org.eclipse.jgit.internal;version="4.7.4";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
- org.eclipse.jgit.internal.ketch;version="4.7.4";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.dfs;version="4.7.4";
+ org.eclipse.jgit.gitrepo.internal;version="4.8.1";x-internal:=true,
+ org.eclipse.jgit.hooks;version="4.8.1";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.ignore;version="4.8.1",
+ org.eclipse.jgit.ignore.internal;version="4.8.1";x-friends:="org.eclipse.jgit.test",
+ org.eclipse.jgit.internal;version="4.8.1";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
+ org.eclipse.jgit.internal.ketch;version="4.8.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.dfs;version="4.8.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="4.7.4";
+ org.eclipse.jgit.internal.storage.file;version="4.8.1";
x-friends:="org.eclipse.jgit.test,
org.eclipse.jgit.junit,
org.eclipse.jgit.junit.http,
@@ -73,9 +73,9 @@ Export-Package: org.eclipse.jgit.annotations;version="4.7.4",
org.eclipse.jgit.lfs,
org.eclipse.jgit.pgm,
org.eclipse.jgit.pgm.test",
- org.eclipse.jgit.internal.storage.pack;version="4.7.4";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.internal.storage.reftree;version="4.7.4";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
- org.eclipse.jgit.lib;version="4.7.4";
+ org.eclipse.jgit.internal.storage.pack;version="4.8.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.internal.storage.reftree;version="4.8.1";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
+ org.eclipse.jgit.lib;version="4.8.1";
uses:="org.eclipse.jgit.revwalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.util,
@@ -85,32 +85,33 @@ Export-Package: org.eclipse.jgit.annotations;version="4.7.4",
org.eclipse.jgit.treewalk,
org.eclipse.jgit.transport,
org.eclipse.jgit.submodule",
- org.eclipse.jgit.merge;version="4.7.4";
+ org.eclipse.jgit.lib.internal;version="4.8.1";x-internal:=true,
+ org.eclipse.jgit.merge;version="4.8.1";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.diff,
org.eclipse.jgit.dircache,
org.eclipse.jgit.api",
- org.eclipse.jgit.nls;version="4.7.4",
- org.eclipse.jgit.notes;version="4.7.4";
+ org.eclipse.jgit.nls;version="4.8.1",
+ org.eclipse.jgit.notes;version="4.8.1";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.merge",
- org.eclipse.jgit.patch;version="4.7.4";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
- org.eclipse.jgit.revplot;version="4.7.4";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
- org.eclipse.jgit.revwalk;version="4.7.4";
+ org.eclipse.jgit.patch;version="4.8.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.diff",
+ org.eclipse.jgit.revplot;version="4.8.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.revwalk",
+ org.eclipse.jgit.revwalk;version="4.8.1";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.treewalk,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.diff,
org.eclipse.jgit.revwalk.filter",
- org.eclipse.jgit.revwalk.filter;version="4.7.4";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.file;version="4.7.4";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
- org.eclipse.jgit.storage.pack;version="4.7.4";uses:="org.eclipse.jgit.lib",
- org.eclipse.jgit.submodule;version="4.7.4";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
- org.eclipse.jgit.transport;version="4.7.4";
+ org.eclipse.jgit.revwalk.filter;version="4.8.1";uses:="org.eclipse.jgit.revwalk,org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.file;version="4.8.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.util",
+ org.eclipse.jgit.storage.pack;version="4.8.1";uses:="org.eclipse.jgit.lib",
+ org.eclipse.jgit.submodule;version="4.8.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.treewalk.filter,org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.transport;version="4.8.1";
uses:="org.eclipse.jgit.transport.resolver,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.internal.storage.pack,
@@ -122,24 +123,24 @@ Export-Package: org.eclipse.jgit.annotations;version="4.7.4",
org.eclipse.jgit.transport.http,
org.eclipse.jgit.errors,
org.eclipse.jgit.storage.pack",
- org.eclipse.jgit.transport.http;version="4.7.4";uses:="javax.net.ssl",
- org.eclipse.jgit.transport.resolver;version="4.7.4";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
- org.eclipse.jgit.treewalk;version="4.7.4";
+ org.eclipse.jgit.transport.http;version="4.8.1";uses:="javax.net.ssl",
+ org.eclipse.jgit.transport.resolver;version="4.8.1";uses:="org.eclipse.jgit.lib,org.eclipse.jgit.transport",
+ org.eclipse.jgit.treewalk;version="4.8.1";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.revwalk,
org.eclipse.jgit.attributes,
org.eclipse.jgit.treewalk.filter,
org.eclipse.jgit.util,
org.eclipse.jgit.dircache",
- org.eclipse.jgit.treewalk.filter;version="4.7.4";uses:="org.eclipse.jgit.treewalk",
- org.eclipse.jgit.util;version="4.7.4";
+ org.eclipse.jgit.treewalk.filter;version="4.8.1";uses:="org.eclipse.jgit.treewalk",
+ org.eclipse.jgit.util;version="4.8.1";
uses:="org.eclipse.jgit.lib,
org.eclipse.jgit.transport.http,
org.eclipse.jgit.storage.file,
org.ietf.jgss",
- org.eclipse.jgit.util.io;version="4.7.4",
- org.eclipse.jgit.util.sha1;version="4.7.4",
- org.eclipse.jgit.util.time;version="4.7.4"
+ org.eclipse.jgit.util.io;version="4.8.1",
+ org.eclipse.jgit.util.sha1;version="4.8.1",
+ org.eclipse.jgit.util.time;version="4.8.1"
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
com.jcraft.jsch;version="[0.1.37,0.2.0)",
diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF
index c95c3458c5..63e6bbccbe 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: 4.7.4.qualifier
-Eclipse-SourceBundle: org.eclipse.jgit;version="4.7.4.qualifier";roots="."
+Bundle-Version: 4.8.1.qualifier
+Eclipse-SourceBundle: org.eclipse.jgit;version="4.8.1.qualifier";roots="."
diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml
index c42e38d3d7..673f3ffc0d 100644
--- a/org.eclipse.jgit/pom.xml
+++ b/org.eclipse.jgit/pom.xml
@@ -53,7 +53,7 @@
<parent>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit-parent</artifactId>
- <version>4.7.4-SNAPSHOT</version>
+ <version>4.8.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 34457c9367..c9aaa39945 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -437,6 +437,7 @@ noHEADExistsAndNoExplicitStartingRevisionWasSpecified=No HEAD exists and no expl
noHMACsupport=No {0} support: {1}
noMergeBase=No merge base could be determined. Reason={0}. {1}
noMergeHeadSpecified=No merge head specified
+nonBareLinkFilesNotSupported=Link files are not supported with nonbare repos
noSuchRef=no such ref
notABoolean=Not a boolean: {0}
notABundle=not a bundle
@@ -544,6 +545,7 @@ renamesFindingExact=Finding exact renames
renamesRejoiningModifies=Rejoining modified file pairs
repositoryAlreadyExists=Repository already exists: {0}
repositoryConfigFileInvalid=Repository config file {0} invalid {1}
+repositoryIsRequired=repository is required
repositoryNotFound=repository not found: {0}
repositoryState_applyMailbox=Apply mailbox
repositoryState_bare=Bare
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
index 4b815b439d..d450c64679 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, 2013 Chris Aniszczyk <caniszczyk@gmail.com>
+ * Copyright (C) 2011, 2017 Chris Aniszczyk <caniszczyk@gmail.com>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -58,6 +58,7 @@ import org.eclipse.jgit.dircache.DirCacheCheckout;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.BranchConfig.BranchRebaseMode;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
@@ -75,6 +76,8 @@ import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.transport.TagOpt;
import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.FileUtils;
/**
* Clone a repository into a new working directory
@@ -106,6 +109,46 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
private Collection<String> branchesToClone;
+ private Callback callback;
+
+ private boolean directoryExistsInitially;
+
+ private boolean gitDirExistsInitially;
+
+ /**
+ * Callback for status of clone operation.
+ *
+ * @since 4.8
+ */
+ public interface Callback {
+ /**
+ * Notify initialized submodules.
+ *
+ * @param submodules
+ * the submodules
+ *
+ */
+ void initializedSubmodules(Collection<String> submodules);
+
+ /**
+ * Notify starting to clone a submodule.
+ *
+ * @param path
+ * the submodule path
+ */
+ void cloningSubmodule(String path);
+
+ /**
+ * Notify checkout of commit
+ *
+ * @param commit
+ * the id of the commit being checked out
+ * @param path
+ * the submodule path
+ */
+ void checkingOut(AnyObjectId commit, String path);
+ }
+
/**
* Create clone command with no repository set
*/
@@ -130,26 +173,55 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
@Override
public Git call() throws GitAPIException, InvalidRemoteException,
org.eclipse.jgit.api.errors.TransportException {
+ URIish u = null;
+ try {
+ u = new URIish(uri);
+ verifyDirectories(u);
+ } catch (URISyntaxException e) {
+ throw new InvalidRemoteException(
+ MessageFormat.format(JGitText.get().invalidURL, uri));
+ }
Repository repository = null;
+ FetchResult fetchResult = null;
+ Thread cleanupHook = new Thread(() -> cleanup());
+ Runtime.getRuntime().addShutdownHook(cleanupHook);
try {
- URIish u = new URIish(uri);
- repository = init(u);
- FetchResult result = fetch(repository, u);
- if (!noCheckout)
- checkout(repository, result);
- return new Git(repository, true);
+ repository = init();
+ fetchResult = fetch(repository, u);
} catch (IOException ioe) {
if (repository != null) {
repository.close();
}
+ cleanup();
throw new JGitInternalException(ioe.getMessage(), ioe);
} catch (URISyntaxException e) {
if (repository != null) {
repository.close();
}
+ cleanup();
throw new InvalidRemoteException(MessageFormat.format(
JGitText.get().invalidRemote, remote));
+ } catch (GitAPIException | RuntimeException e) {
+ if (repository != null) {
+ repository.close();
+ }
+ cleanup();
+ throw e;
+ } finally {
+ Runtime.getRuntime().removeShutdownHook(cleanupHook);
+ }
+ if (!noCheckout) {
+ try {
+ checkout(repository, fetchResult);
+ } catch (IOException ioe) {
+ repository.close();
+ throw new JGitInternalException(ioe.getMessage(), ioe);
+ } catch (GitAPIException | RuntimeException e) {
+ repository.close();
+ throw e;
+ }
}
+ return new Git(repository, true);
}
private static boolean isNonEmptyDirectory(File dir) {
@@ -160,12 +232,12 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
return false;
}
- private Repository init(URIish u) throws GitAPIException {
- InitCommand command = Git.init();
- command.setBare(bare);
+ private void verifyDirectories(URIish u) {
if (directory == null && gitDir == null) {
directory = new File(u.getHumanishName(), Constants.DOT_GIT);
}
+ directoryExistsInitially = directory != null && directory.exists();
+ gitDirExistsInitially = gitDir != null && gitDir.exists();
validateDirs(directory, gitDir, bare);
if (isNonEmptyDirectory(directory)) {
throw new JGitInternalException(MessageFormat.format(
@@ -175,6 +247,11 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
throw new JGitInternalException(MessageFormat.format(
JGitText.get().cloneNonEmptyDirectory, gitDir.getName()));
}
+ }
+
+ private Repository init() throws GitAPIException {
+ InitCommand command = Git.init();
+ command.setBare(bare);
if (directory != null) {
command.setDirectory(directory);
}
@@ -280,12 +357,18 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
private void cloneSubmodules(Repository clonedRepo) throws IOException,
GitAPIException {
SubmoduleInitCommand init = new SubmoduleInitCommand(clonedRepo);
- if (init.call().isEmpty())
+ Collection<String> submodules = init.call();
+ if (submodules.isEmpty()) {
return;
+ }
+ if (callback != null) {
+ callback.initializedSubmodules(submodules);
+ }
SubmoduleUpdateCommand update = new SubmoduleUpdateCommand(clonedRepo);
configure(update);
update.setProgressMonitor(monitor);
+ update.setCallback(callback);
if (!update.call().isEmpty()) {
SubmoduleWalk walk = SubmoduleWalk.forIndex(clonedRepo);
while (walk.next()) {
@@ -523,6 +606,19 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
return this;
}
+ /**
+ * Register a progress callback.
+ *
+ * @param callback
+ * the callback
+ * @return {@code this}
+ * @since 4.8
+ */
+ public CloneCommand setCallback(Callback callback) {
+ this.callback = callback;
+ return this;
+ }
+
private static void validateDirs(File directory, File gitDir, boolean bare)
throws IllegalStateException {
if (directory != null) {
@@ -548,4 +644,38 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
}
}
}
+
+ private void cleanup() {
+ try {
+ if (directory != null) {
+ if (!directoryExistsInitially) {
+ FileUtils.delete(directory, FileUtils.RECURSIVE
+ | FileUtils.SKIP_MISSING | FileUtils.IGNORE_ERRORS);
+ } else {
+ deleteChildren(directory);
+ }
+ }
+ if (gitDir != null) {
+ if (!gitDirExistsInitially) {
+ FileUtils.delete(gitDir, FileUtils.RECURSIVE
+ | FileUtils.SKIP_MISSING | FileUtils.IGNORE_ERRORS);
+ } else {
+ deleteChildren(directory);
+ }
+ }
+ } catch (IOException e) {
+ // Ignore; this is a best-effort cleanup in error cases, and
+ // IOException should not be raised anyway
+ }
+ }
+
+ private void deleteChildren(File file) throws IOException {
+ if (!FS.DETECTED.isDirectory(file)) {
+ return;
+ }
+ for (File child : file.listFiles()) {
+ FileUtils.delete(child, FileUtils.RECURSIVE | FileUtils.SKIP_MISSING
+ | FileUtils.IGNORE_ERRORS);
+ }
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
index cc3302b486..785c20c8af 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
@@ -99,6 +99,24 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
private FetchRecurseSubmodulesMode submoduleRecurseMode = null;
+ private Callback callback;
+
+ /**
+ * Callback for status of fetch operation.
+ *
+ * @since 4.8
+ *
+ */
+ public interface Callback {
+ /**
+ * Notify fetching a submodule.
+ *
+ * @param name
+ * the submodule name.
+ */
+ void fetchingSubmodule(String name);
+ }
+
/**
* @param repo
*/
@@ -173,6 +191,9 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
.setThin(thin).setRefSpecs(refSpecs)
.setDryRun(dryRun)
.setRecurseSubmodules(recurseMode);
+ if (callback != null) {
+ callback.fetchingSubmodule(walk.getPath());
+ }
results.addSubmodule(walk.getPath(), f.call());
}
}
@@ -434,4 +455,17 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
this.tagOption = tagOpt;
return this;
}
+
+ /**
+ * Register a progress callback.
+ *
+ * @param callback
+ * the callback
+ * @return {@code this}
+ * @since 4.8
+ */
+ public FetchCommand setCallback(Callback callback) {
+ this.callback = callback;
+ return this;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
index ae822da568..9c5ae432e2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
@@ -203,62 +203,63 @@ public class PullCommand extends TransportCommand<PullCommand, PullResult> {
@Override
public PullResult call() throws GitAPIException,
WrongRepositoryStateException, InvalidConfigurationException,
- DetachedHeadException, InvalidRemoteException, CanceledException,
+ InvalidRemoteException, CanceledException,
RefNotFoundException, RefNotAdvertisedException, NoHeadException,
org.eclipse.jgit.api.errors.TransportException {
checkCallable();
monitor.beginTask(JGitText.get().pullTaskName, 2);
+ Config repoConfig = repo.getConfig();
- String branchName;
+ String branchName = null;
try {
String fullBranch = repo.getFullBranch();
- if (fullBranch == null)
- throw new NoHeadException(
- JGitText.get().pullOnRepoWithoutHEADCurrentlyNotSupported);
- if (!fullBranch.startsWith(Constants.R_HEADS)) {
- // we can not pull if HEAD is detached and branch is not
- // specified explicitly
- throw new DetachedHeadException();
+ if (fullBranch != null
+ && fullBranch.startsWith(Constants.R_HEADS)) {
+ branchName = fullBranch.substring(Constants.R_HEADS.length());
}
- branchName = fullBranch.substring(Constants.R_HEADS.length());
} catch (IOException e) {
throw new JGitInternalException(
JGitText.get().exceptionCaughtDuringExecutionOfPullCommand,
e);
}
+ if (remoteBranchName == null && branchName != null) {
+ // get the name of the branch in the remote repository
+ // stored in configuration key branch.<branch name>.merge
+ remoteBranchName = repoConfig.getString(
+ ConfigConstants.CONFIG_BRANCH_SECTION, branchName,
+ ConfigConstants.CONFIG_KEY_MERGE);
+ }
+ if (remoteBranchName == null) {
+ remoteBranchName = branchName;
+ }
+ if (remoteBranchName == null) {
+ throw new NoHeadException(
+ JGitText.get().cannotCheckoutFromUnbornBranch);
+ }
if (!repo.getRepositoryState().equals(RepositoryState.SAFE))
throw new WrongRepositoryStateException(MessageFormat.format(
JGitText.get().cannotPullOnARepoWithState, repo
.getRepositoryState().name()));
- Config repoConfig = repo.getConfig();
- if (remote == null) {
+ if (remote == null && branchName != null) {
// get the configured remote for the currently checked out branch
// stored in configuration key branch.<branch name>.remote
remote = repoConfig.getString(
ConfigConstants.CONFIG_BRANCH_SECTION, branchName,
ConfigConstants.CONFIG_KEY_REMOTE);
}
- if (remote == null)
+ if (remote == null) {
// fall back to default remote
remote = Constants.DEFAULT_REMOTE_NAME;
-
- if (remoteBranchName == null)
- // get the name of the branch in the remote repository
- // stored in configuration key branch.<branch name>.merge
- remoteBranchName = repoConfig.getString(
- ConfigConstants.CONFIG_BRANCH_SECTION, branchName,
- ConfigConstants.CONFIG_KEY_MERGE);
+ }
// determines whether rebase should be used after fetching
- if (pullRebaseMode == null) {
+ if (pullRebaseMode == null && branchName != null) {
pullRebaseMode = getRebaseMode(branchName, repoConfig);
}
- if (remoteBranchName == null)
- remoteBranchName = branchName;
final boolean isRemote = !remote.equals("."); //$NON-NLS-1$
String remoteUri;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
index 29d5d49a45..4d3dff02cd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
@@ -89,6 +89,8 @@ public class SubmoduleUpdateCommand extends
private MergeStrategy strategy = MergeStrategy.RECURSIVE;
+ private CloneCommand.Callback callback;
+
/**
* @param repo
*/
@@ -161,6 +163,9 @@ public class SubmoduleUpdateCommand extends
Repository submoduleRepo = generator.getRepository();
// Clone repository is not present
if (submoduleRepo == null) {
+ if (callback != null) {
+ callback.cloningSubmodule(generator.getPath());
+ }
CloneCommand clone = Git.cloneRepository();
configure(clone);
clone.setURI(url);
@@ -201,6 +206,10 @@ public class SubmoduleUpdateCommand extends
Constants.HEAD, true);
refUpdate.setNewObjectId(commit);
refUpdate.forceUpdate();
+ if (callback != null) {
+ callback.checkingOut(commit,
+ generator.getPath());
+ }
}
} finally {
submoduleRepo.close();
@@ -225,4 +234,17 @@ public class SubmoduleUpdateCommand extends
this.strategy = strategy;
return this;
}
+
+ /**
+ * Set status callback for submodule clone operation.
+ *
+ * @param callback
+ * the callback
+ * @return {@code this}
+ * @since 4.8
+ */
+ public SubmoduleUpdateCommand setCallback(CloneCommand.Callback callback) {
+ this.callback = callback;
+ return this;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/TooLargeObjectInPackException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/TooLargeObjectInPackException.java
index 08c25c28ed..b841f57f1e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/TooLargeObjectInPackException.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/TooLargeObjectInPackException.java
@@ -38,7 +38,8 @@
package org.eclipse.jgit.api.errors;
/**
- * Exception thrown when the server rejected a too large pack
+ * Exception thrown when PackParser finds an object larger than a predefined
+ * limit
*
* @since 4.4
*/
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffAlgorithm.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffAlgorithm.java
index bd6e5c859a..5f01366c47 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffAlgorithm.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffAlgorithm.java
@@ -121,23 +121,7 @@ public abstract class DiffAlgorithm {
Subsequence<S> as = Subsequence.a(a, region);
Subsequence<S> bs = Subsequence.b(b, region);
EditList e = Subsequence.toBase(diffNonCommon(cs, as, bs), as, bs);
-
- // The last insertion may need to be shifted later if it
- // inserts elements that were previously reduced out as
- // common at the end.
- //
- Edit last = e.get(e.size() - 1);
- if (last.getType() == Edit.Type.INSERT) {
- while (last.endB < b.size()
- && cmp.equals(b, last.beginB, b, last.endB)) {
- last.beginA++;
- last.endA++;
- last.beginB++;
- last.endB++;
- }
- }
-
- return e;
+ return normalize(cmp, e, a, b);
}
case EMPTY:
@@ -153,6 +137,107 @@ public abstract class DiffAlgorithm {
}
/**
+ * Reorganize an {@link EditList} for better diff consistency.
+ * <p>
+ * {@code DiffAlgorithms} may return {@link Edit.Type#INSERT} or
+ * {@link Edit.Type#DELETE} edits that can be "shifted". For
+ * example, the deleted section
+ * <pre>
+ * -a
+ * -b
+ * -c
+ * a
+ * b
+ * c
+ * </pre>
+ * can be shifted down by 1, 2 or 3 locations.
+ * <p>
+ * To avoid later merge issues, we shift such edits to a
+ * consistent location. {@code normalize} uses a simple strategy of
+ * shifting such edits to their latest possible location.
+ * <p>
+ * This strategy may not always produce an aesthetically pleasing
+ * diff. For instance, it works well with
+ * <pre>
+ * function1 {
+ * ...
+ * }
+ *
+ * +function2 {
+ * + ...
+ * +}
+ * +
+ * function3 {
+ * ...
+ * }
+ * </pre>
+ * but less so for
+ * <pre>
+ * #
+ * # comment1
+ * #
+ * function1() {
+ * }
+ *
+ * #
+ * +# comment3
+ * +#
+ * +function3() {
+ * +}
+ * +
+ * +#
+ * # comment2
+ * #
+ * function2() {
+ * }
+ * </pre>
+ * <a href="https://github.com/mhagger/diff-slider-tools">More
+ * sophisticated strategies</a> are possible, say by calculating a
+ * suitable "aesthetic cost" for each possible position and using
+ * the lowest cost, but {@code normalize} just shifts edits
+ * to the end as much as possible.
+ *
+ * @param <S>
+ * type of sequence being compared.
+ * @param cmp
+ * the comparator supplying the element equivalence function.
+ * @param e
+ * a modifiable edit list comparing the provided sequences.
+ * @param a
+ * the first (also known as old or pre-image) sequence.
+ * @param b
+ * the second (also known as new or post-image) sequence.
+ * @return a modifiable edit list with edit regions shifted to their
+ * latest possible location. The result list is never null.
+ * @since 4.7
+ */
+ private static <S extends Sequence> EditList normalize(
+ SequenceComparator<? super S> cmp, EditList e, S a, S b) {
+ Edit prev = null;
+ for (int i = e.size() - 1; i >= 0; i--) {
+ Edit cur = e.get(i);
+ Edit.Type curType = cur.getType();
+
+ int maxA = (prev == null) ? a.size() : prev.beginA;
+ int maxB = (prev == null) ? b.size() : prev.beginB;
+
+ if (curType == Edit.Type.INSERT) {
+ while (cur.endA < maxA && cur.endB < maxB
+ && cmp.equals(b, cur.beginB, b, cur.endB)) {
+ cur.shift(1);
+ }
+ } else if (curType == Edit.Type.DELETE) {
+ while (cur.endA < maxA && cur.endB < maxB
+ && cmp.equals(a, cur.beginA, a, cur.endA)) {
+ cur.shift(1);
+ }
+ }
+ prev = cur;
+ }
+ return e;
+ }
+
+ /**
* Compare two sequences and identify a list of edits between them.
*
* This method should be invoked only after the two sequences have been
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/Edit.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/Edit.java
index 7eecd13513..a2e167fd20 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/Edit.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/Edit.java
@@ -170,6 +170,21 @@ public class Edit {
}
/**
+ * Move the edit region by the specified amount.
+ *
+ * @param amount
+ * the region is shifted by this amount, and can be positive or
+ * negative.
+ * @since 4.8
+ */
+ public final void shift(int amount) {
+ beginA += amount;
+ endA += amount;
+ beginB += amount;
+ endB += amount;
+ }
+
+ /**
* Construct a new edit representing the region before cut.
*
* @param cut
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
index bc52473d93..d899429c3b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/RenameDetector.java
@@ -220,7 +220,9 @@ public class RenameDetector {
* must be allocated, and 1,000,000 file compares may need to be performed.
*
* @param limit
- * new file limit.
+ * new file limit. 0 means no limit; a negative number means no
+ * inexact rename detection will be performed, only exact rename
+ * detection.
*/
public void setRenameLimit(int limit) {
renameLimit = limit;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
index 84f0da9e88..aed76ac66b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -1299,8 +1299,13 @@ public class DirCacheCheckout {
return;
}
+ String name = f.getName();
+ if (name.length() > 200) {
+ name = name.substring(0, 200);
+ }
File tmpFile = File.createTempFile(
- "._" + f.getName(), null, parentDir); //$NON-NLS-1$
+ "._" + name, null, parentDir); //$NON-NLS-1$
+
EolStreamType nonNullEolStreamType;
if (checkoutMetadata.eolStreamType != null) {
nonNullEolStreamType = checkoutMetadata.eolStreamType;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
index 94c8e437c3..ddc6addbc3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/ManifestParser.java
@@ -60,6 +60,8 @@ import java.util.Set;
import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.gitrepo.RepoProject.CopyFile;
+import org.eclipse.jgit.gitrepo.RepoProject.LinkFile;
+import org.eclipse.jgit.gitrepo.RepoProject.ReferenceFile;
import org.eclipse.jgit.gitrepo.internal.RepoText;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Repository;
@@ -209,6 +211,15 @@ public class ManifestParser extends DefaultHandler {
currentProject.getPath(),
attributes.getValue("src"), //$NON-NLS-1$
attributes.getValue("dest"))); //$NON-NLS-1$
+ } else if ("linkfile".equals(qName)) { //$NON-NLS-1$
+ if (currentProject == null) {
+ throw new SAXException(RepoText.get().invalidManifest);
+ }
+ currentProject.addLinkFile(new LinkFile(
+ rootRepo,
+ currentProject.getPath(),
+ attributes.getValue("src"), //$NON-NLS-1$
+ attributes.getValue("dest"))); //$NON-NLS-1$
} else if ("include".equals(qName)) { //$NON-NLS-1$
String name = attributes.getValue("name"); //$NON-NLS-1$
if (includedReader != null) {
@@ -359,19 +370,25 @@ public class ManifestParser extends DefaultHandler {
else
last = p;
}
- removeNestedCopyfiles();
+ removeNestedCopyAndLinkfiles();
}
- /** Remove copyfiles that sit in a subdirectory of any other project. */
- void removeNestedCopyfiles() {
+ private void removeNestedCopyAndLinkfiles() {
for (RepoProject proj : filteredProjects) {
List<CopyFile> copyfiles = new ArrayList<>(proj.getCopyFiles());
proj.clearCopyFiles();
for (CopyFile copyfile : copyfiles) {
- if (!isNestedCopyfile(copyfile)) {
+ if (!isNestedReferencefile(copyfile)) {
proj.addCopyFile(copyfile);
}
}
+ List<LinkFile> linkfiles = new ArrayList<>(proj.getLinkFiles());
+ proj.clearLinkFiles();
+ for (LinkFile linkfile : linkfiles) {
+ if (!isNestedReferencefile(linkfile)) {
+ proj.addLinkFile(linkfile);
+ }
+ }
}
}
@@ -393,18 +410,18 @@ public class ManifestParser extends DefaultHandler {
return false;
}
- private boolean isNestedCopyfile(CopyFile copyfile) {
- if (copyfile.dest.indexOf('/') == -1) {
- // If the copyfile is at root level then it won't be nested.
+ private boolean isNestedReferencefile(ReferenceFile referencefile) {
+ if (referencefile.dest.indexOf('/') == -1) {
+ // If the referencefile is at root level then it won't be nested.
return false;
}
for (RepoProject proj : filteredProjects) {
- if (proj.getPath().compareTo(copyfile.dest) > 0) {
+ if (proj.getPath().compareTo(referencefile.dest) > 0) {
// Early return as remaining projects can't be ancestor of this
- // copyfile config (filteredProjects is sorted).
+ // referencefile config (filteredProjects is sorted).
return false;
}
- if (proj.isAncestorOf(copyfile.dest)) {
+ if (proj.isAncestorOf(referencefile.dest)) {
return true;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
index e105dc063b..1de8a0be2e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
@@ -49,11 +49,14 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.UnsupportedOperationException;
+import java.net.URI;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.StringJoiner;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.Git;
@@ -67,6 +70,7 @@ import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.gitrepo.ManifestParser.IncludedFileReader;
import org.eclipse.jgit.gitrepo.RepoProject.CopyFile;
+import org.eclipse.jgit.gitrepo.RepoProject.LinkFile;
import org.eclipse.jgit.gitrepo.internal.RepoText;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.CommitBuilder;
@@ -106,7 +110,8 @@ import org.eclipse.jgit.util.FileUtils;
*/
public class RepoCommand extends GitCommand<RevCommit> {
private String manifestPath;
- private String uri;
+ private String baseUri;
+ private URI targetUri;
private String groupsParam;
private String branch;
private String targetBranch = Constants.HEAD;
@@ -274,7 +279,25 @@ public class RepoCommand extends GitCommand<RevCommit> {
* @return this command
*/
public RepoCommand setURI(String uri) {
- this.uri = uri;
+ this.baseUri = uri;
+ return this;
+ }
+
+ /**
+ * Set the URI of the superproject (this repository), so the .gitmodules
+ * file can specify the submodule URLs relative to the superproject.
+ *
+ * @param uri
+ * the URI of the repository holding the superproject.
+ * @return this command
+ * @since 4.8
+ */
+ public RepoCommand setTargetURI(String uri) {
+ // The repo name is interpreted as a directory, for example
+ // Gerrit (http://gerrit.googlesource.com/gerrit) has a
+ // .gitmodules referencing ../plugins/hooks, which is
+ // on http://gerrit.googlesource.com/plugins/hooks,
+ this.targetUri = URI.create(uri + "/"); //$NON-NLS-1$
return this;
}
@@ -452,9 +475,8 @@ public class RepoCommand extends GitCommand<RevCommit> {
public RevCommit call() throws GitAPIException {
try {
checkCallable();
- if (uri == null || uri.length() == 0) {
- throw new IllegalArgumentException(
- JGitText.get().uriNotConfigured);
+ if (baseUri == null) {
+ baseUri = ""; //$NON-NLS-1$
}
if (inputStream == null) {
if (manifestPath == null || manifestPath.length() == 0)
@@ -478,7 +500,7 @@ public class RepoCommand extends GitCommand<RevCommit> {
git = new Git(repo);
ManifestParser parser = new ManifestParser(
- includedReader, manifestPath, branch, uri, groupsParam, repo);
+ includedReader, manifestPath, branch, baseUri, groupsParam, repo);
try {
parser.read(inputStream);
for (RepoProject proj : parser.getFilteredProjects()) {
@@ -486,6 +508,7 @@ public class RepoCommand extends GitCommand<RevCommit> {
proj.getPath(),
proj.getRevision(),
proj.getCopyFiles(),
+ proj.getLinkFiles(),
proj.getGroups(),
proj.getRecommendShallow());
}
@@ -550,8 +573,13 @@ public class RepoCommand extends GitCommand<RevCommit> {
rec.append("\n"); //$NON-NLS-1$
attributes.append(rec.toString());
}
+
+ URI submodUrl = URI.create(nameUri);
+ if (targetUri != null) {
+ submodUrl = relativize(targetUri, submodUrl);
+ }
cfg.setString("submodule", path, "path", path); //$NON-NLS-1$ //$NON-NLS-2$
- cfg.setString("submodule", path, "url", nameUri); //$NON-NLS-1$ //$NON-NLS-2$
+ cfg.setString("submodule", path, "url", submodUrl.toString()); //$NON-NLS-1$ //$NON-NLS-2$
// create gitlink
DirCacheEntry dcEntry = new DirCacheEntry(path);
@@ -568,6 +596,25 @@ public class RepoCommand extends GitCommand<RevCommit> {
dcEntry.setFileMode(FileMode.REGULAR_FILE);
builder.add(dcEntry);
}
+ for (LinkFile linkfile : proj.getLinkFiles()) {
+ String link;
+ if (linkfile.dest.contains("/")) { //$NON-NLS-1$
+ link = FileUtils.relativizeGitPath(
+ linkfile.dest.substring(0,
+ linkfile.dest.lastIndexOf('/')),
+ proj.getPath() + "/" + linkfile.src); //$NON-NLS-1$
+ } else {
+ link = proj.getPath() + "/" + linkfile.src; //$NON-NLS-1$
+ }
+
+ objectId = inserter.insert(Constants.OBJ_BLOB,
+ link.getBytes(
+ Constants.CHARACTER_ENCODING));
+ dcEntry = new DirCacheEntry(linkfile.dest);
+ dcEntry.setObjectId(objectId);
+ dcEntry.setFileMode(FileMode.SYMLINK);
+ builder.add(dcEntry);
+ }
}
String content = cfg.toText();
@@ -642,13 +689,20 @@ public class RepoCommand extends GitCommand<RevCommit> {
}
private void addSubmodule(String url, String path, String revision,
- List<CopyFile> copyfiles, Set<String> groups, String recommendShallow)
+ List<CopyFile> copyfiles, List<LinkFile> linkfiles,
+ Set<String> groups, String recommendShallow)
throws GitAPIException, IOException {
if (repo.isBare()) {
RepoProject proj = new RepoProject(url, path, revision, null, groups, recommendShallow);
proj.addCopyFiles(copyfiles);
+ proj.addLinkFiles(linkfiles);
bareProjects.add(proj);
} else {
+ if (!linkfiles.isEmpty()) {
+ throw new UnsupportedOperationException(
+ JGitText.get().nonBareLinkFilesNotSupported);
+ }
+
SubmoduleAddCommand add = git
.submoduleAdd()
.setPath(path)
@@ -672,6 +726,77 @@ public class RepoCommand extends GitCommand<RevCommit> {
}
}
+ /*
+ * Assume we are document "a/b/index.html", what should we put in a href to get to "a/" ?
+ * Returns the child if either base or child is not a bare path. This provides a missing feature in
+ * java.net.URI (see http://bugs.java.com/view_bug.do?bug_id=6226081).
+ */
+ private static final String SLASH = "/"; //$NON-NLS-1$
+ static URI relativize(URI current, URI target) {
+
+ // We only handle bare paths for now.
+ if (!target.toString().equals(target.getPath())) {
+ return target;
+ }
+ if (!current.toString().equals(current.getPath())) {
+ return target;
+ }
+
+ String cur = current.normalize().getPath();
+ String dest = target.normalize().getPath();
+
+ // TODO(hanwen): maybe (absolute, relative) should throw an exception.
+ if (cur.startsWith(SLASH) != dest.startsWith(SLASH)) {
+ return target;
+ }
+
+ while (cur.startsWith(SLASH)) {
+ cur = cur.substring(1);
+ }
+ while (dest.startsWith(SLASH)) {
+ dest = dest.substring(1);
+ }
+
+ if (cur.indexOf('/') == -1 || dest.indexOf('/') == -1) {
+ // Avoid having to special-casing in the next two ifs.
+ String prefix = "prefix/"; //$NON-NLS-1$
+ cur = prefix + cur;
+ dest = prefix + dest;
+ }
+
+ if (!cur.endsWith(SLASH)) {
+ // The current file doesn't matter.
+ int lastSlash = cur.lastIndexOf('/');
+ cur = cur.substring(0, lastSlash);
+ }
+ String destFile = ""; //$NON-NLS-1$
+ if (!dest.endsWith(SLASH)) {
+ // We always have to provide the destination file.
+ int lastSlash = dest.lastIndexOf('/');
+ destFile = dest.substring(lastSlash + 1, dest.length());
+ dest = dest.substring(0, dest.lastIndexOf('/'));
+ }
+
+ String[] cs = cur.split(SLASH);
+ String[] ds = dest.split(SLASH);
+
+ int common = 0;
+ while (common < cs.length && common < ds.length && cs[common].equals(ds[common])) {
+ common++;
+ }
+
+ StringJoiner j = new StringJoiner(SLASH);
+ for (int i = common; i < cs.length; i++) {
+ j.add(".."); //$NON-NLS-1$
+ }
+ for (int i = common; i < ds.length; i++) {
+ j.add(ds[i]);
+ }
+
+ j.add(destFile);
+ return URI.create(j.toString());
+ }
+
private static String findRef(String ref, Repository repo)
throws IOException {
if (!ObjectId.isId(ref)) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
index 700cf11070..00cd38d69e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
@@ -70,14 +70,17 @@ public class RepoProject implements Comparable<RepoProject> {
private final String remote;
private final Set<String> groups;
private final List<CopyFile> copyfiles;
+ private final List<LinkFile> linkfiles;
private String recommendShallow;
private String url;
private String defaultRevision;
/**
- * The representation of a copy file configuration.
+ * The representation of a reference file configuration.
+ *
+ * @since 4.8
*/
- public static class CopyFile {
+ public static class ReferenceFile {
final Repository repo;
final String path;
final String src;
@@ -93,12 +96,31 @@ public class RepoProject implements Comparable<RepoProject> {
* @param dest
* the destination path relative to the super project.
*/
- public CopyFile(Repository repo, String path, String src, String dest) {
+ public ReferenceFile(Repository repo, String path, String src, String dest) {
this.repo = repo;
this.path = path;
this.src = src;
this.dest = dest;
}
+ }
+
+ /**
+ * The representation of a copy file configuration.
+ */
+ public static class CopyFile extends ReferenceFile {
+ /**
+ * @param repo
+ * the super project.
+ * @param path
+ * the path of the project containing this copyfile config.
+ * @param src
+ * the source path relative to the sub repo.
+ * @param dest
+ * the destination path relative to the super project.
+ */
+ public CopyFile(Repository repo, String path, String src, String dest) {
+ super(repo, path, src, dest);
+ }
/**
* Do the copy file action.
@@ -126,6 +148,27 @@ public class RepoProject implements Comparable<RepoProject> {
}
/**
+ * The representation of a link file configuration.
+ *
+ * @since 4.8
+ */
+ public static class LinkFile extends ReferenceFile {
+ /**
+ * @param repo
+ * the super project.
+ * @param path
+ * the path of the project containing this linkfile config.
+ * @param src
+ * the source path relative to the sub repo.
+ * @param dest
+ * the destination path relative to the super project.
+ */
+ public LinkFile(Repository repo, String path, String src, String dest) {
+ super(repo, path, src, dest);
+ }
+ }
+
+ /**
* @param name
* the relative path to the {@code remote}
* @param path
@@ -156,6 +199,7 @@ public class RepoProject implements Comparable<RepoProject> {
this.groups = groups;
this.recommendShallow = recommendShallow;
copyfiles = new ArrayList<>();
+ linkfiles = new ArrayList<>();
}
/**
@@ -250,6 +294,16 @@ public class RepoProject implements Comparable<RepoProject> {
}
/**
+ * Getter for the linkfile configurations.
+ *
+ * @return Immutable copy of {@code linkfiles}
+ * @since 4.8
+ */
+ public List<LinkFile> getLinkFiles() {
+ return Collections.unmodifiableList(linkfiles);
+ }
+
+ /**
* Get the url of the sub repo.
*
* @return {@code url}
@@ -335,6 +389,35 @@ public class RepoProject implements Comparable<RepoProject> {
this.copyfiles.clear();
}
+ /**
+ * Add a link file configuration.
+ *
+ * @param linkfile
+ * @since 4.8
+ */
+ public void addLinkFile(LinkFile linkfile) {
+ linkfiles.add(linkfile);
+ }
+
+ /**
+ * Add a bunch of linkfile configurations.
+ *
+ * @param linkFiles
+ * @since 4.8
+ */
+ public void addLinkFiles(Collection<LinkFile> linkFiles) {
+ this.linkfiles.addAll(linkFiles);
+ }
+
+ /**
+ * Clear all the linkfiles.
+ *
+ * @since 4.8
+ */
+ public void clearLinkFiles() {
+ this.linkfiles.clear();
+ }
+
private String getPathWithSlash() {
if (path.endsWith("/")) //$NON-NLS-1$
return path;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
index c1aca6a136..62a674924a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
@@ -42,9 +42,12 @@
*/
package org.eclipse.jgit.hooks;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
import java.util.concurrent.Callable;
import org.eclipse.jgit.api.errors.AbortedByHookException;
@@ -147,12 +150,19 @@ abstract class GitHook<T> implements Callable<T> {
*/
protected void doRun() throws AbortedByHookException {
final ByteArrayOutputStream errorByteArray = new ByteArrayOutputStream();
- final PrintStream hookErrRedirect = new PrintStream(errorByteArray);
+ PrintStream hookErrRedirect = null;
+ try {
+ hookErrRedirect = new PrintStream(errorByteArray, false,
+ UTF_8.name());
+ } catch (UnsupportedEncodingException e) {
+ // UTF-8 is guaranteed to be available
+ }
ProcessResult result = FS.DETECTED.runHookIfPresent(getRepository(),
getHookName(), getParameters(), getOutputStream(),
hookErrRedirect, getStdinArgs());
if (result.isExecutedWithError()) {
- throw new AbortedByHookException(errorByteArray.toString(),
+ throw new AbortedByHookException(
+ new String(errorByteArray.toByteArray(), UTF_8),
getHookName(), result.getExitCode());
}
}
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 41f3c2a488..a8dfc2d2a2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -496,6 +496,7 @@ public class JGitText extends TranslationBundle {
/***/ public String noHMACsupport;
/***/ public String noMergeBase;
/***/ public String noMergeHeadSpecified;
+ /***/ public String nonBareLinkFilesNotSupported;
/***/ public String noSuchRef;
/***/ public String notABoolean;
/***/ public String notABundle;
@@ -603,6 +604,7 @@ public class JGitText extends TranslationBundle {
/***/ public String renamesRejoiningModifies;
/***/ public String repositoryAlreadyExists;
/***/ public String repositoryConfigFileInvalid;
+ /***/ public String repositoryIsRequired;
/***/ public String repositoryNotFound;
/***/ public String repositoryState_applyMailbox;
/***/ public String repositoryState_bare;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
index ef0b80c11f..6fff656e7b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBlockCache.java
@@ -322,6 +322,7 @@ public final class DfsBlockCache {
HashEntry e1 = table.get(slot);
DfsBlock v = scan(e1, key, position);
if (v != null) {
+ ctx.stats.blockCacheHit++;
statHit.incrementAndGet();
return v;
}
@@ -334,6 +335,7 @@ public final class DfsBlockCache {
if (e2 != e1) {
v = scan(e2, key, position);
if (v != null) {
+ ctx.stats.blockCacheHit++;
statHit.incrementAndGet();
creditSpace(blockSize);
return v;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
index d3803024a5..55f9cc2127 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
@@ -53,12 +53,12 @@ import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.UN
import static org.eclipse.jgit.internal.storage.pack.PackExt.BITMAP_INDEX;
import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
+import static org.eclipse.jgit.internal.storage.pack.PackWriter.NONE;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
-import java.util.Collections;
import java.util.EnumSet;
import java.util.GregorianCalendar;
import java.util.HashSet;
@@ -112,7 +112,8 @@ public class DfsGarbageCollector {
private List<DfsPackFile> packsBefore;
private List<DfsPackFile> expiredGarbagePacks;
- private Set<ObjectId> allHeads;
+ private Set<ObjectId> allHeadsAndTags;
+ private Set<ObjectId> allTags;
private Set<ObjectId> nonHeads;
private Set<ObjectId> txnHeads;
private Set<ObjectId> tagTargets;
@@ -234,7 +235,7 @@ public class DfsGarbageCollector {
JGitText.get().supportOnlyPackIndexVersion2);
startTimeMillis = SystemReader.getInstance().getCurrentTime();
- ctx = (DfsReader) objdb.newReader();
+ ctx = objdb.newReader();
try {
refdb.refresh();
objdb.clearCache();
@@ -242,30 +243,36 @@ public class DfsGarbageCollector {
Collection<Ref> refsBefore = getAllRefs();
readPacksBefore();
- if (packsBefore.isEmpty()) {
- if (!expiredGarbagePacks.isEmpty()) {
- objdb.commitPack(noPacks(), toPrune());
- }
- return true;
- }
-
- allHeads = new HashSet<>();
+ Set<ObjectId> allHeads = new HashSet<>();
+ allHeadsAndTags = new HashSet<>();
+ allTags = new HashSet<>();
nonHeads = new HashSet<>();
txnHeads = new HashSet<>();
tagTargets = new HashSet<>();
for (Ref ref : refsBefore) {
- if (ref.isSymbolic() || ref.getObjectId() == null)
+ if (ref.isSymbolic() || ref.getObjectId() == null) {
continue;
- if (isHead(ref) || isTag(ref))
+ }
+ if (isHead(ref)) {
allHeads.add(ref.getObjectId());
- else if (RefTreeNames.isRefTree(refdb, ref.getName()))
+ } else if (isTag(ref)) {
+ allTags.add(ref.getObjectId());
+ } else if (RefTreeNames.isRefTree(refdb, ref.getName())) {
txnHeads.add(ref.getObjectId());
- else
+ } else {
nonHeads.add(ref.getObjectId());
- if (ref.getPeeledObjectId() != null)
+ }
+ if (ref.getPeeledObjectId() != null) {
tagTargets.add(ref.getPeeledObjectId());
+ }
}
- tagTargets.addAll(allHeads);
+ // Don't exclude tags that are also branch tips.
+ allTags.removeAll(allHeads);
+ allHeadsAndTags.addAll(allHeads);
+ allHeadsAndTags.addAll(allTags);
+
+ // Hoist all branch tips and tags earlier in the pack file
+ tagTargets.addAll(allHeadsAndTags);
boolean rollback = true;
try {
@@ -307,13 +314,12 @@ public class DfsGarbageCollector {
packsBefore = new ArrayList<>(packs.length);
expiredGarbagePacks = new ArrayList<>(packs.length);
- long mostRecentGC = mostRecentGC(packs);
long now = SystemReader.getInstance().getCurrentTime();
for (DfsPackFile p : packs) {
DfsPackDescription d = p.getPackDescription();
if (d.getPackSource() != UNREACHABLE_GARBAGE) {
packsBefore.add(p);
- } else if (packIsExpiredGarbage(d, mostRecentGC, now)) {
+ } else if (packIsExpiredGarbage(d, now)) {
expiredGarbagePacks.add(p);
} else if (packIsCoalesceableGarbage(d, now)) {
packsBefore.add(p);
@@ -321,39 +327,13 @@ public class DfsGarbageCollector {
}
}
- private static long mostRecentGC(DfsPackFile[] packs) {
- long r = 0;
- for (DfsPackFile p : packs) {
- DfsPackDescription d = p.getPackDescription();
- if (d.getPackSource() == GC || d.getPackSource() == GC_REST) {
- r = Math.max(r, d.getLastModified());
- }
- }
- return r;
- }
-
- private boolean packIsExpiredGarbage(DfsPackDescription d,
- long mostRecentGC, long now) {
- // It should be safe to remove an UNREACHABLE_GARBAGE pack if it:
- //
- // (a) Predates the most recent prior run of this class. This check
- // ensures the graph traversal algorithm had a chance to consider
- // all objects in this pack and copied them into a GC or GC_REST
- // pack if the graph contained live edges to the objects.
- //
- // This check is safe because of the ordering of packing; the GC
- // packs are written first and then the UNREACHABLE_GARBAGE is
- // constructed. Any UNREACHABLE_GARBAGE dated earlier than the GC
- // was input to the prior GC's graph traversal.
- //
- // (b) Is older than garbagePackTtl. This check gives concurrent
- // inserter threads sufficient time to identify an object is not
- // in the graph and should have a new copy written, rather than
- // relying on something from an UNREACHABLE_GARBAGE pack.
- //
- // Both (a) and (b) must be met to safely remove UNREACHABLE_GARBAGE.
+ private boolean packIsExpiredGarbage(DfsPackDescription d, long now) {
+ // Consider the garbage pack as expired when it's older than
+ // garbagePackTtl. This check gives concurrent inserter threads
+ // sufficient time to identify an object is not in the graph and should
+ // have a new copy written, rather than relying on something from an
+ // UNREACHABLE_GARBAGE pack.
return d.getPackSource() == UNREACHABLE_GARBAGE
- && d.getLastModified() < mostRecentGC
&& garbageTtlMillis > 0
&& now - d.getLastModified() >= garbageTtlMillis;
}
@@ -448,12 +428,12 @@ public class DfsGarbageCollector {
}
private void packHeads(ProgressMonitor pm) throws IOException {
- if (allHeads.isEmpty())
+ if (allHeadsAndTags.isEmpty())
return;
try (PackWriter pw = newPackWriter()) {
pw.setTagTargets(tagTargets);
- pw.preparePack(pm, allHeads, PackWriter.NONE);
+ pw.preparePack(pm, allHeadsAndTags, NONE, NONE, allTags);
if (0 < pw.getObjectCount())
writePack(GC, pw, pm,
estimateGcPackSize(INSERT, RECEIVE, COMPACT, GC));
@@ -467,7 +447,7 @@ public class DfsGarbageCollector {
try (PackWriter pw = newPackWriter()) {
for (ObjectIdSet packedObjs : newPackObj)
pw.excludeObjects(packedObjs);
- pw.preparePack(pm, nonHeads, allHeads);
+ pw.preparePack(pm, nonHeads, allHeadsAndTags);
if (0 < pw.getObjectCount())
writePack(GC_REST, pw, pm,
estimateGcPackSize(INSERT, RECEIVE, COMPACT, GC_REST));
@@ -481,7 +461,7 @@ public class DfsGarbageCollector {
try (PackWriter pw = newPackWriter()) {
for (ObjectIdSet packedObjs : newPackObj)
pw.excludeObjects(packedObjs);
- pw.preparePack(pm, txnHeads, PackWriter.NONE);
+ pw.preparePack(pm, txnHeads, NONE);
if (0 < pw.getObjectCount())
writePack(GC_TXN, pw, pm, 0 /* unknown pack size */);
}
@@ -605,8 +585,4 @@ public class DfsGarbageCollector {
DfsBlockCache.getInstance().getOrCreate(pack, null);
return pack;
}
-
- private static List<DfsPackDescription> noPacks() {
- return Collections.emptyList();
- }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java
index fd72756e39..e65c9fda7a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsInserter.java
@@ -530,7 +530,7 @@ public class DfsInserter extends ObjectInserter {
}
private class Reader extends ObjectReader {
- private final DfsReader ctx = new DfsReader(db);
+ private final DfsReader ctx = db.newReader();
@Override
public ObjectReader newReader() {
@@ -647,7 +647,7 @@ public class DfsInserter extends ObjectInserter {
@Override
public ObjectStream openStream() throws IOException {
- final DfsReader ctx = new DfsReader(db);
+ final DfsReader ctx = db.newReader();
if (srcPack != packKey) {
try {
// Post DfsInserter.flush() use the normal code path.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
index b1cb72dec9..32ee6c288e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
@@ -170,7 +170,7 @@ public abstract class DfsObjDatabase extends ObjectDatabase {
}
@Override
- public ObjectReader newReader() {
+ public DfsReader newReader() {
return new DfsReader(this);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
index 0a29ac515f..f7c87a4e79 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
@@ -201,7 +201,7 @@ public class DfsPackCompactor {
pm = NullProgressMonitor.INSTANCE;
DfsObjDatabase objdb = repo.getObjectDatabase();
- try (DfsReader ctx = (DfsReader) objdb.newReader()) {
+ try (DfsReader ctx = objdb.newReader()) {
PackConfig pc = new PackConfig(repo);
pc.setIndexVersion(2);
pc.setDeltaCompress(false);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
index f15d427f8d..ae2e7e4127 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFile.java
@@ -251,6 +251,8 @@ public final class DfsPackFile {
PackIndex idx;
try {
+ ctx.stats.readIdx++;
+ long start = System.nanoTime();
ReadableChannel rc = ctx.db.openFile(packDesc, INDEX);
try {
InputStream in = Channels.newInputStream(rc);
@@ -260,10 +262,11 @@ public final class DfsPackFile {
bs = (wantSize / bs) * bs;
else if (bs <= 0)
bs = wantSize;
- in = new BufferedInputStream(in, bs);
- idx = PackIndex.read(in);
+ idx = PackIndex.read(new BufferedInputStream(in, bs));
+ ctx.stats.readIdxBytes += rc.position();
} finally {
rc.close();
+ ctx.stats.readIdxMicros += elapsedMicros(start);
}
} catch (EOFException e) {
invalid = true;
@@ -286,6 +289,10 @@ public final class DfsPackFile {
}
}
+ private static long elapsedMicros(long start) {
+ return (System.nanoTime() - start) / 1000L;
+ }
+
final boolean isGarbage() {
return packDesc.getPackSource() == UNREACHABLE_GARBAGE;
}
@@ -314,6 +321,8 @@ public final class DfsPackFile {
long size;
PackBitmapIndex idx;
try {
+ ctx.stats.readBitmap++;
+ long start = System.nanoTime();
ReadableChannel rc = ctx.db.openFile(packDesc, BITMAP_INDEX);
try {
InputStream in = Channels.newInputStream(rc);
@@ -329,6 +338,8 @@ public final class DfsPackFile {
} finally {
size = rc.position();
rc.close();
+ ctx.stats.readIdxBytes += size;
+ ctx.stats.readIdxMicros += elapsedMicros(start);
}
} catch (EOFException e) {
IOException e2 = new IOException(MessageFormat.format(
@@ -777,6 +788,8 @@ public final class DfsPackFile {
if (invalid)
throw new PackInvalidException(getPackName());
+ ctx.stats.readBlock++;
+ long start = System.nanoTime();
ReadableChannel rc = ctx.db.openFile(packDesc, PACK);
try {
int size = blockSize(rc);
@@ -803,6 +816,7 @@ public final class DfsPackFile {
byte[] buf = new byte[size];
rc.position(pos);
int cnt = read(rc, ByteBuffer.wrap(buf, 0, size));
+ ctx.stats.readBlockBytes += cnt;
if (cnt != size) {
if (0 <= len) {
throw new EOFException(MessageFormat.format(
@@ -824,10 +838,10 @@ public final class DfsPackFile {
length = len = rc.size();
}
- DfsBlock v = new DfsBlock(key, pos, buf);
- return v;
+ return new DfsBlock(key, pos, buf);
} finally {
rc.close();
+ ctx.stats.readBlockMicros += elapsedMicros(start);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
index 755b163127..d611469afc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
@@ -95,7 +95,7 @@ import org.eclipse.jgit.util.BlockList;
* See the base {@link ObjectReader} documentation for details. Notably, a
* reader is not thread safe.
*/
-public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
+public class DfsReader extends ObjectReader implements ObjectReuseAsIs {
private static final int MAX_RESOLVE_MATCHES = 256;
/** Temporary buffer large enough for at least one raw object id. */
@@ -104,17 +104,21 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
/** Database this reader loads objects from. */
final DfsObjDatabase db;
- private Inflater inf;
+ final DfsReaderIoStats.Accumulator stats = new DfsReaderIoStats.Accumulator();
+ private Inflater inf;
private DfsBlock block;
-
private DeltaBaseCache baseCache;
-
private DfsPackFile last;
-
private boolean avoidUnreachable;
- DfsReader(DfsObjDatabase db) {
+ /**
+ * Initialize a new DfsReader
+ *
+ * @param db
+ * parent DfsObjDatabase.
+ */
+ protected DfsReader(DfsObjDatabase db) {
this.db = db;
this.streamFileThreshold = db.getReaderOptions().getStreamFileThreshold();
}
@@ -131,7 +135,7 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
@Override
public ObjectReader newReader() {
- return new DfsReader(db);
+ return db.newReader();
}
@Override
@@ -170,6 +174,7 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
PackList packList = db.getPackList();
resolveImpl(packList, id, matches);
if (matches.size() < MAX_RESOLVE_MATCHES && packList.dirty()) {
+ stats.scanPacks++;
resolveImpl(db.scanPacks(packList), id, matches);
}
return matches;
@@ -198,6 +203,7 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
if (hasImpl(packList, objectId)) {
return true;
} else if (packList.dirty()) {
+ stats.scanPacks++;
return hasImpl(db.scanPacks(packList), objectId);
}
return false;
@@ -234,6 +240,7 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
return checkType(ldr, objectId, typeHint);
}
if (packList.dirty()) {
+ stats.scanPacks++;
ldr = openImpl(db.scanPacks(packList), objectId);
if (ldr != null) {
return checkType(ldr, objectId, typeHint);
@@ -316,6 +323,7 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
List<FoundObject<T>> r = new ArrayList<>();
findAllImpl(packList, pending, r);
if (!pending.isEmpty() && packList.dirty()) {
+ stats.scanPacks++;
findAllImpl(db.scanPacks(packList), pending, r);
}
for (T t : pending) {
@@ -452,7 +460,6 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
final IOException findAllError = error;
return new AsyncObjectSizeQueue<T>() {
private FoundObject<T> cur;
-
private long sz;
@Override
@@ -718,9 +725,10 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
for (int dstoff = 0;;) {
int n = inf.inflate(dstbuf, dstoff, dstbuf.length - dstoff);
dstoff += n;
- if (inf.finished() || (headerOnly && dstoff == dstbuf.length))
+ if (inf.finished() || (headerOnly && dstoff == dstbuf.length)) {
+ stats.inflatedBytes += dstoff;
return dstoff;
- if (inf.needsInput()) {
+ } else if (inf.needsInput()) {
pin(pack, position);
position += block.setInput(position, inf);
} else if (n == 0)
@@ -764,6 +772,11 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
block = null;
}
+ /** @return IO statistics accumulated by this reader. */
+ public DfsReaderIoStats getIoStats() {
+ return new DfsReaderIoStats(stats);
+ }
+
/** Release the current window cursor. */
@Override
public void close() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java
new file mode 100644
index 0000000000..9a174c81d0
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReaderIoStats.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2017, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.internal.storage.dfs;
+
+/** IO statistics for a {@link DfsReader}. */
+public class DfsReaderIoStats {
+ /** POJO to accumulate IO statistics. */
+ public static class Accumulator {
+ /** Number of times the reader explicitly called scanPacks. */
+ long scanPacks;
+
+ /** Total number of complete pack indexes read into memory. */
+ long readIdx;
+
+ /** Total number of complete bitmap indexes read into memory. */
+ long readBitmap;
+
+ /** Total number of bytes read from indexes. */
+ long readIdxBytes;
+
+ /** Total microseconds spent reading pack or bitmap indexes. */
+ long readIdxMicros;
+
+ /** Total number of block cache hits. */
+ long blockCacheHit;
+
+ /** Total number of discrete blocks read from pack file(s). */
+ long readBlock;
+
+ /** Total number of compressed bytes read as block sized units. */
+ long readBlockBytes;
+
+ /** Total microseconds spent reading {@link #readBlock} blocks. */
+ long readBlockMicros;
+
+ /** Total number of bytes decompressed. */
+ long inflatedBytes;
+
+ Accumulator() {
+ }
+ }
+
+ private final Accumulator stats;
+
+ DfsReaderIoStats(Accumulator stats) {
+ this.stats = stats;
+ }
+
+ /** @return number of times the reader explicitly called scanPacks. */
+ public long getScanPacks() {
+ return stats.scanPacks;
+ }
+
+ /** @return total number of complete pack indexes read into memory. */
+ public long getReadPackIndexCount() {
+ return stats.readIdx;
+ }
+
+ /** @return total number of complete bitmap indexes read into memory. */
+ public long getReadBitmapIndexCount() {
+ return stats.readBitmap;
+ }
+
+ /** @return total number of bytes read from indexes. */
+ public long getReadIndexBytes() {
+ return stats.readIdxBytes;
+ }
+
+ /** @return total microseconds spent reading pack or bitmap indexes. */
+ public long getReadIndexMicros() {
+ return stats.readIdxMicros;
+ }
+
+ /** @return total number of block cache hits. */
+ public long getBlockCacheHits() {
+ return stats.blockCacheHit;
+ }
+
+ /** @return total number of discrete blocks read from pack file(s). */
+ public long getReadBlocksCount() {
+ return stats.readBlock;
+ }
+
+ /** @return total number of compressed bytes read as block sized units. */
+ public long getReadBlocksBytes() {
+ return stats.readBlockBytes;
+ }
+
+ /** @return total microseconds spent reading blocks. */
+ public long getReadBlocksMicros() {
+ return stats.readBlockMicros;
+ }
+
+ /** @return total number of bytes decompressed. */
+ public long getInflatedBytes() {
+ return stats.inflatedBytes;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/LargePackedWholeObject.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/LargePackedWholeObject.java
index 6d40a7505e..73a93e6575 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/LargePackedWholeObject.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/LargePackedWholeObject.java
@@ -99,7 +99,7 @@ final class LargePackedWholeObject extends ObjectLoader {
@Override
public ObjectStream openStream() throws MissingObjectException, IOException {
- DfsReader ctx = new DfsReader(db);
+ DfsReader ctx = db.newReader();
InputStream in;
try {
in = new PackInputStream(pack, objectOffset + headerLength, ctx);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java
index d47b304688..ae8260a997 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/CachedObjectDirectory.java
@@ -47,8 +47,10 @@ package org.eclipse.jgit.internal.storage.file;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
+import java.util.HashSet;
import java.util.Set;
+import org.eclipse.jgit.internal.storage.file.ObjectDirectory.AlternateHandle;
import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
import org.eclipse.jgit.internal.storage.pack.PackWriter;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
@@ -160,43 +162,70 @@ class CachedObjectDirectory extends FileObjectDatabase {
return alts;
}
+ private Set<AlternateHandle.Id> skipMe(Set<AlternateHandle.Id> skips) {
+ Set<AlternateHandle.Id> withMe = new HashSet<>();
+ if (skips != null) {
+ withMe.addAll(skips);
+ }
+ withMe.add(getAlternateId());
+ return withMe;
+ }
+
@Override
void resolve(Set<ObjectId> matches, AbbreviatedObjectId id)
throws IOException {
- // In theory we could accelerate the loose object scan using our
- // unpackedObjects map, but its not worth the huge code complexity.
- // Scanning a single loose directory is fast enough, and this is
- // unlikely to be called anyway.
- //
wrapped.resolve(matches, id);
}
@Override
public boolean has(final AnyObjectId objectId) throws IOException {
- if (unpackedObjects.contains(objectId))
+ return has(objectId, null);
+ }
+
+ private boolean has(final AnyObjectId objectId, Set<AlternateHandle.Id> skips)
+ throws IOException {
+ if (unpackedObjects.contains(objectId)) {
return true;
- if (wrapped.hasPackedObject(objectId))
+ }
+ if (wrapped.hasPackedObject(objectId)) {
return true;
+ }
+ skips = skipMe(skips);
for (CachedObjectDirectory alt : myAlternates()) {
- if (alt.has(objectId))
- return true;
+ if (!skips.contains(alt.getAlternateId())) {
+ if (alt.has(objectId, skips)) {
+ return true;
+ }
+ }
}
return false;
}
@Override
- ObjectLoader openObject(final WindowCursor curs,
- final AnyObjectId objectId) throws IOException {
+ ObjectLoader openObject(final WindowCursor curs, final AnyObjectId objectId)
+ throws IOException {
+ return openObject(curs, objectId, null);
+ }
+
+ private ObjectLoader openObject(final WindowCursor curs,
+ final AnyObjectId objectId, Set<AlternateHandle.Id> skips)
+ throws IOException {
ObjectLoader ldr = openLooseObject(curs, objectId);
- if (ldr != null)
+ if (ldr != null) {
return ldr;
+ }
ldr = wrapped.openPackedObject(curs, objectId);
- if (ldr != null)
+ if (ldr != null) {
return ldr;
+ }
+ skips = skipMe(skips);
for (CachedObjectDirectory alt : myAlternates()) {
- ldr = alt.openObject(curs, objectId);
- if (ldr != null)
- return ldr;
+ if (!skips.contains(alt.getAlternateId())) {
+ ldr = alt.openObject(curs, objectId, skips);
+ if (ldr != null) {
+ return ldr;
+ }
+ }
}
return null;
}
@@ -260,4 +289,8 @@ class CachedObjectDirectory extends FileObjectDatabase {
super(id);
}
}
+
+ private AlternateHandle.Id getAlternateId() {
+ return wrapped.getAlternateId();
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
index ef9aa37d06..6a674aa658 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
@@ -490,10 +490,28 @@ public class FileRepository extends Repository {
*/
@Override
public Set<ObjectId> getAdditionalHaves() {
+ return getAdditionalHaves(null);
+ }
+
+ /**
+ * Objects known to exist but not expressed by {@link #getAllRefs()}.
+ * <p>
+ * When a repository borrows objects from another repository, it can
+ * advertise that it safely has that other repository's references, without
+ * exposing any other details about the other repository. This may help
+ * a client trying to push changes avoid pushing more than it needs to.
+ *
+ * @param skips
+ * Set of AlternateHandle Ids already seen
+ *
+ * @return unmodifiable collection of other known objects.
+ */
+ private Set<ObjectId> getAdditionalHaves(Set<AlternateHandle.Id> skips) {
HashSet<ObjectId> r = new HashSet<>();
+ skips = objectDatabase.addMe(skips);
for (AlternateHandle d : objectDatabase.myAlternates()) {
- if (d instanceof AlternateRepository) {
- Repository repo;
+ if (d instanceof AlternateRepository && !skips.contains(d.getId())) {
+ FileRepository repo;
repo = ((AlternateRepository) d).repository;
for (Ref ref : repo.getAllRefs().values()) {
@@ -502,7 +520,7 @@ public class FileRepository extends Repository {
if (ref.getPeeledObjectId() != null)
r.add(ref.getPeeledObjectId());
}
- r.addAll(repo.getAdditionalHaves());
+ r.addAll(repo.getAdditionalHaves(skips));
}
}
return r;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
index a4a2baa55b..7ff209fb81 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
@@ -80,7 +80,6 @@ import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -107,6 +106,7 @@ import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Ref.Storage;
+import org.eclipse.jgit.lib.internal.WorkQueue;
import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.ReflogEntry;
import org.eclipse.jgit.lib.ReflogReader;
@@ -151,7 +151,19 @@ public class GC {
private static final int DEFAULT_AUTOLIMIT = 6700;
- private static ExecutorService executor = Executors.newFixedThreadPool(1);
+ private static volatile ExecutorService executor;
+
+ /**
+ * Set the executor for running auto-gc in the background. If no executor is
+ * set JGit's own WorkQueue will be used.
+ *
+ * @param e
+ * the executor to be used for running auto-gc
+ * @since 4.8
+ */
+ public static void setExecutor(ExecutorService e) {
+ executor = e;
+ }
private final FileRepository repo;
@@ -265,10 +277,14 @@ public class GC {
return Collections.emptyList();
};
// TODO(ms): in 5.0 change signature and return the Future
- executor.submit(gcTask);
+ executor().submit(gcTask);
return Collections.emptyList();
}
+ private ExecutorService executor() {
+ return (executor != null) ? executor : WorkQueue.getExecutor();
+ }
+
private Collection<PackFile> doGc() throws IOException, ParseException {
if (automatic && !needGc()) {
return Collections.emptyList();
@@ -790,7 +806,9 @@ public class GC {
long time = System.currentTimeMillis();
Collection<Ref> refsBefore = getAllRefs();
+ Set<ObjectId> allHeadsAndTags = new HashSet<>();
Set<ObjectId> allHeads = new HashSet<>();
+ Set<ObjectId> allTags = new HashSet<>();
Set<ObjectId> nonHeads = new HashSet<>();
Set<ObjectId> txnHeads = new HashSet<>();
Set<ObjectId> tagTargets = new HashSet<>();
@@ -800,16 +818,21 @@ public class GC {
for (Ref ref : refsBefore) {
checkCancelled();
nonHeads.addAll(listRefLogObjects(ref, 0));
- if (ref.isSymbolic() || ref.getObjectId() == null)
+ if (ref.isSymbolic() || ref.getObjectId() == null) {
continue;
- if (isHead(ref) || isTag(ref))
+ }
+ if (isHead(ref)) {
allHeads.add(ref.getObjectId());
- else if (RefTreeNames.isRefTree(refdb, ref.getName()))
+ } else if (isTag(ref)) {
+ allTags.add(ref.getObjectId());
+ } else if (RefTreeNames.isRefTree(refdb, ref.getName())) {
txnHeads.add(ref.getObjectId());
- else
+ } else {
nonHeads.add(ref.getObjectId());
- if (ref.getPeeledObjectId() != null)
+ }
+ if (ref.getPeeledObjectId() != null) {
tagTargets.add(ref.getPeeledObjectId());
+ }
}
List<ObjectIdSet> excluded = new LinkedList<>();
@@ -819,13 +842,19 @@ public class GC {
excluded.add(f.getIndex());
}
- tagTargets.addAll(allHeads);
+ // Don't exclude tags that are also branch tips
+ allTags.removeAll(allHeads);
+ allHeadsAndTags.addAll(allHeads);
+ allHeadsAndTags.addAll(allTags);
+
+ // Hoist all branch tips and tags earlier in the pack file
+ tagTargets.addAll(allHeadsAndTags);
nonHeads.addAll(indexObjects);
List<PackFile> ret = new ArrayList<>(2);
PackFile heads = null;
- if (!allHeads.isEmpty()) {
- heads = writePack(allHeads, Collections.<ObjectId> emptySet(),
+ if (!allHeadsAndTags.isEmpty()) {
+ heads = writePack(allHeadsAndTags, PackWriter.NONE, allTags,
tagTargets, excluded);
if (heads != null) {
ret.add(heads);
@@ -833,12 +862,14 @@ public class GC {
}
}
if (!nonHeads.isEmpty()) {
- PackFile rest = writePack(nonHeads, allHeads, tagTargets, excluded);
+ PackFile rest = writePack(nonHeads, allHeadsAndTags, PackWriter.NONE,
+ tagTargets, excluded);
if (rest != null)
ret.add(rest);
}
if (!txnHeads.isEmpty()) {
- PackFile txn = writePack(txnHeads, PackWriter.NONE, null, excluded);
+ PackFile txn = writePack(txnHeads, PackWriter.NONE, PackWriter.NONE,
+ null, excluded);
if (txn != null)
ret.add(txn);
}
@@ -1074,8 +1105,9 @@ public class GC {
}
private PackFile writePack(@NonNull Set<? extends ObjectId> want,
- @NonNull Set<? extends ObjectId> have, Set<ObjectId> tagTargets,
- List<ObjectIdSet> excludeObjects) throws IOException {
+ @NonNull Set<? extends ObjectId> have, @NonNull Set<ObjectId> tags,
+ Set<ObjectId> tagTargets, List<ObjectIdSet> excludeObjects)
+ throws IOException {
checkCancelled();
File tmpPack = null;
Map<PackExt, File> tmpExts = new TreeMap<>(
@@ -1101,12 +1133,13 @@ public class GC {
// prepare the PackWriter
pw.setDeltaBaseAsOffset(true);
pw.setReuseDeltaCommits(false);
- if (tagTargets != null)
+ if (tagTargets != null) {
pw.setTagTargets(tagTargets);
+ }
if (excludeObjects != null)
for (ObjectIdSet idx : excludeObjects)
pw.excludeObjects(idx);
- pw.preparePack(pm, want, have);
+ pw.preparePack(pm, want, have, PackWriter.NONE, tags);
if (pw.getObjectCount() == 0)
return null;
checkCancelled();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
index 00e39533b1..d953b87c4b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java
@@ -64,6 +64,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
@@ -117,6 +118,8 @@ public class ObjectDirectory extends FileObjectDatabase {
/** Maximum number of candidates offered as resolutions of abbreviation. */
private static final int RESOLVE_ABBREV_LIMIT = 256;
+ private final AlternateHandle handle = new AlternateHandle(this);
+
private final Config config;
private final File objects;
@@ -294,26 +297,38 @@ public class ObjectDirectory extends FileObjectDatabase {
@Override
public boolean has(AnyObjectId objectId) {
return unpackedObjectCache.isUnpacked(objectId)
- || hasPackedInSelfOrAlternate(objectId)
- || hasLooseInSelfOrAlternate(objectId);
+ || hasPackedInSelfOrAlternate(objectId, null)
+ || hasLooseInSelfOrAlternate(objectId, null);
}
- private boolean hasPackedInSelfOrAlternate(AnyObjectId objectId) {
- if (hasPackedObject(objectId))
+ private boolean hasPackedInSelfOrAlternate(AnyObjectId objectId,
+ Set<AlternateHandle.Id> skips) {
+ if (hasPackedObject(objectId)) {
return true;
+ }
+ skips = addMe(skips);
for (AlternateHandle alt : myAlternates()) {
- if (alt.db.hasPackedInSelfOrAlternate(objectId))
- return true;
+ if (!skips.contains(alt.getId())) {
+ if (alt.db.hasPackedInSelfOrAlternate(objectId, skips)) {
+ return true;
+ }
+ }
}
return false;
}
- private boolean hasLooseInSelfOrAlternate(AnyObjectId objectId) {
- if (fileFor(objectId).exists())
+ private boolean hasLooseInSelfOrAlternate(AnyObjectId objectId,
+ Set<AlternateHandle.Id> skips) {
+ if (fileFor(objectId).exists()) {
return true;
+ }
+ skips = addMe(skips);
for (AlternateHandle alt : myAlternates()) {
- if (alt.db.hasLooseInSelfOrAlternate(objectId))
- return true;
+ if (!skips.contains(alt.getId())) {
+ if (alt.db.hasLooseInSelfOrAlternate(objectId, skips)) {
+ return true;
+ }
+ }
}
return false;
}
@@ -340,6 +355,12 @@ public class ObjectDirectory extends FileObjectDatabase {
@Override
void resolve(Set<ObjectId> matches, AbbreviatedObjectId id)
throws IOException {
+ resolve(matches, id, null);
+ }
+
+ private void resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
+ Set<AlternateHandle.Id> skips)
+ throws IOException {
// Go through the packs once. If we didn't find any resolutions
// scan for new packs and check once more.
int oldSize = matches.size();
@@ -376,10 +397,14 @@ public class ObjectDirectory extends FileObjectDatabase {
}
}
+ skips = addMe(skips);
for (AlternateHandle alt : myAlternates()) {
- alt.db.resolve(matches, id);
- if (matches.size() > RESOLVE_ABBREV_LIMIT)
- return;
+ if (!skips.contains(alt.getId())) {
+ alt.db.resolve(matches, id, skips);
+ if (matches.size() > RESOLVE_ABBREV_LIMIT) {
+ return;
+ }
+ }
}
}
@@ -388,37 +413,50 @@ public class ObjectDirectory extends FileObjectDatabase {
throws IOException {
if (unpackedObjectCache.isUnpacked(objectId)) {
ObjectLoader ldr = openLooseObject(curs, objectId);
- if (ldr != null)
+ if (ldr != null) {
return ldr;
+ }
}
- ObjectLoader ldr = openPackedFromSelfOrAlternate(curs, objectId);
- if (ldr != null)
+ ObjectLoader ldr = openPackedFromSelfOrAlternate(curs, objectId, null);
+ if (ldr != null) {
return ldr;
- return openLooseFromSelfOrAlternate(curs, objectId);
+ }
+ return openLooseFromSelfOrAlternate(curs, objectId, null);
}
private ObjectLoader openPackedFromSelfOrAlternate(WindowCursor curs,
- AnyObjectId objectId) {
+ AnyObjectId objectId, Set<AlternateHandle.Id> skips) {
ObjectLoader ldr = openPackedObject(curs, objectId);
- if (ldr != null)
+ if (ldr != null) {
return ldr;
+ }
+ skips = addMe(skips);
for (AlternateHandle alt : myAlternates()) {
- ldr = alt.db.openPackedFromSelfOrAlternate(curs, objectId);
- if (ldr != null)
- return ldr;
+ if (!skips.contains(alt.getId())) {
+ ldr = alt.db.openPackedFromSelfOrAlternate(curs, objectId, skips);
+ if (ldr != null) {
+ return ldr;
+ }
+ }
}
return null;
}
private ObjectLoader openLooseFromSelfOrAlternate(WindowCursor curs,
- AnyObjectId objectId) throws IOException {
+ AnyObjectId objectId, Set<AlternateHandle.Id> skips)
+ throws IOException {
ObjectLoader ldr = openLooseObject(curs, objectId);
- if (ldr != null)
+ if (ldr != null) {
return ldr;
+ }
+ skips = addMe(skips);
for (AlternateHandle alt : myAlternates()) {
- ldr = alt.db.openLooseFromSelfOrAlternate(curs, objectId);
- if (ldr != null)
- return ldr;
+ if (!skips.contains(alt.getId())) {
+ ldr = alt.db.openLooseFromSelfOrAlternate(curs, objectId, skips);
+ if (ldr != null) {
+ return ldr;
+ }
+ }
}
return null;
}
@@ -469,37 +507,49 @@ public class ObjectDirectory extends FileObjectDatabase {
throws IOException {
if (unpackedObjectCache.isUnpacked(id)) {
long len = getLooseObjectSize(curs, id);
- if (0 <= len)
+ if (0 <= len) {
return len;
+ }
}
- long len = getPackedSizeFromSelfOrAlternate(curs, id);
- if (0 <= len)
+ long len = getPackedSizeFromSelfOrAlternate(curs, id, null);
+ if (0 <= len) {
return len;
- return getLooseSizeFromSelfOrAlternate(curs, id);
+ }
+ return getLooseSizeFromSelfOrAlternate(curs, id, null);
}
private long getPackedSizeFromSelfOrAlternate(WindowCursor curs,
- AnyObjectId id) {
+ AnyObjectId id, Set<AlternateHandle.Id> skips) {
long len = getPackedObjectSize(curs, id);
- if (0 <= len)
+ if (0 <= len) {
return len;
+ }
+ skips = addMe(skips);
for (AlternateHandle alt : myAlternates()) {
- len = alt.db.getPackedSizeFromSelfOrAlternate(curs, id);
- if (0 <= len)
- return len;
+ if (!skips.contains(alt.getId())) {
+ len = alt.db.getPackedSizeFromSelfOrAlternate(curs, id, skips);
+ if (0 <= len) {
+ return len;
+ }
+ }
}
return -1;
}
private long getLooseSizeFromSelfOrAlternate(WindowCursor curs,
- AnyObjectId id) throws IOException {
+ AnyObjectId id, Set<AlternateHandle.Id> skips) throws IOException {
long len = getLooseObjectSize(curs, id);
- if (0 <= len)
+ if (0 <= len) {
return len;
+ }
+ skips = addMe(skips);
for (AlternateHandle alt : myAlternates()) {
- len = alt.db.getLooseSizeFromSelfOrAlternate(curs, id);
- if (0 <= len)
- return len;
+ if (!skips.contains(alt.getId())) {
+ len = alt.db.getLooseSizeFromSelfOrAlternate(curs, id, skips);
+ if (0 <= len) {
+ return len;
+ }
+ }
}
return -1;
}
@@ -546,7 +596,12 @@ public class ObjectDirectory extends FileObjectDatabase {
@Override
void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
- WindowCursor curs) throws IOException {
+ WindowCursor curs) throws IOException {
+ selectObjectRepresentation(packer, otp, curs, null);
+ }
+
+ private void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
+ WindowCursor curs, Set<AlternateHandle.Id> skips) throws IOException {
PackList pList = packList.get();
SEARCH: for (;;) {
for (final PackFile p : pList.packs) {
@@ -567,8 +622,12 @@ public class ObjectDirectory extends FileObjectDatabase {
break SEARCH;
}
- for (AlternateHandle h : myAlternates())
- h.db.selectObjectRepresentation(packer, otp, curs);
+ skips = addMe(skips);
+ for (AlternateHandle h : myAlternates()) {
+ if (!skips.contains(h.getId())) {
+ h.db.selectObjectRepresentation(packer, otp, curs, skips);
+ }
+ }
}
private void handlePackError(IOException e, PackFile p) {
@@ -930,6 +989,14 @@ public class ObjectDirectory extends FileObjectDatabase {
return alt;
}
+ Set<AlternateHandle.Id> addMe(Set<AlternateHandle.Id> skips) {
+ if (skips == null) {
+ skips = new HashSet<>();
+ }
+ skips.add(handle.getId());
+ return skips;
+ }
+
private AlternateHandle[] loadAlternates() throws IOException {
final List<AlternateHandle> l = new ArrayList<>(4);
final BufferedReader br = open(alternatesFile);
@@ -996,6 +1063,38 @@ public class ObjectDirectory extends FileObjectDatabase {
}
static class AlternateHandle {
+ static class Id {
+ String alternateId;
+
+ public Id(File object) {
+ try {
+ this.alternateId = object.getCanonicalPath();
+ } catch (Exception e) {
+ alternateId = null;
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o == null || !(o instanceof Id)) {
+ return false;
+ }
+ Id aId = (Id) o;
+ return Objects.equals(alternateId, aId.alternateId);
+ }
+
+ @Override
+ public int hashCode() {
+ if (alternateId == null) {
+ return 1;
+ }
+ return alternateId.hashCode();
+ }
+ }
+
final ObjectDirectory db;
AlternateHandle(ObjectDirectory db) {
@@ -1005,6 +1104,10 @@ public class ObjectDirectory extends FileObjectDatabase {
void close() {
db.close();
}
+
+ public Id getId(){
+ return db.getAlternateId();
+ }
}
static class AlternateRepository extends AlternateHandle {
@@ -1029,4 +1132,8 @@ public class ObjectDirectory extends FileObjectDatabase {
CachedObjectDirectory newCachedFileObjectDatabase() {
return new CachedObjectDirectory(this);
}
+
+ AlternateHandle.Id getAlternateId() {
+ return new AlternateHandle.Id(objects);
+ }
}
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 93dbee3e79..7271560e3c 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
@@ -233,7 +233,9 @@ public class PackWriter implements AutoCloseable {
private List<CachedPack> cachedPacks = new ArrayList<>(2);
- private Set<ObjectId> tagTargets = Collections.emptySet();
+ private Set<ObjectId> tagTargets = NONE;
+
+ private Set<? extends ObjectId> excludeFromBitmapSelection = NONE;
private ObjectIdSet[] excludeInPacks;
@@ -712,8 +714,7 @@ public class PackWriter implements AutoCloseable {
public void preparePack(ProgressMonitor countingMonitor,
@NonNull Set<? extends ObjectId> want,
@NonNull Set<? extends ObjectId> have) throws IOException {
- preparePack(countingMonitor,
- want, have, Collections.<ObjectId> emptySet());
+ preparePack(countingMonitor, want, have, NONE, NONE);
}
/**
@@ -721,9 +722,9 @@ public class PackWriter implements AutoCloseable {
* <p>
* Like {@link #preparePack(ProgressMonitor, Set, Set)} but also allows
* specifying commits that should not be walked past ("shallow" commits).
- * The caller is responsible for filtering out commits that should not
- * be shallow any more ("unshallow" commits as in {@link #setShallowPack})
- * from the shallow set.
+ * The caller is responsible for filtering out commits that should not be
+ * shallow any more ("unshallow" commits as in {@link #setShallowPack}) from
+ * the shallow set.
*
* @param countingMonitor
* progress during object enumeration.
@@ -731,27 +732,67 @@ public class PackWriter implements AutoCloseable {
* objects of interest, ancestors of which will be included in
* the pack. Must not be {@code null}.
* @param have
- * objects whose ancestors (up to and including
- * {@code shallow} commits) do not need to be included in the
- * pack because they are already available from elsewhere.
- * Must not be {@code null}.
+ * objects whose ancestors (up to and including {@code shallow}
+ * commits) do not need to be included in the pack because they
+ * are already available from elsewhere. Must not be
+ * {@code null}.
* @param shallow
* commits indicating the boundary of the history marked with
- * {@code have}. Shallow commits have parents but those
- * parents are considered not to be already available.
- * Parents of {@code shallow} commits and earlier generations
- * will be included in the pack if requested by {@code want}.
- * Must not be {@code null}.
+ * {@code have}. Shallow commits have parents but those parents
+ * are considered not to be already available. Parents of
+ * {@code shallow} commits and earlier generations will be
+ * included in the pack if requested by {@code want}. Must not be
+ * {@code null}.
* @throws IOException
- * an I/O problem occured while reading objects.
+ * an I/O problem occurred while reading objects.
*/
public void preparePack(ProgressMonitor countingMonitor,
@NonNull Set<? extends ObjectId> want,
@NonNull Set<? extends ObjectId> have,
@NonNull Set<? extends ObjectId> shallow) throws IOException {
+ preparePack(countingMonitor, want, have, shallow, NONE);
+ }
+
+ /**
+ * Prepare the list of objects to be written to the pack stream.
+ * <p>
+ * Like {@link #preparePack(ProgressMonitor, Set, Set)} but also allows
+ * specifying commits that should not be walked past ("shallow" commits).
+ * The caller is responsible for filtering out commits that should not be
+ * shallow any more ("unshallow" commits as in {@link #setShallowPack}) from
+ * the shallow set.
+ *
+ * @param countingMonitor
+ * progress during object enumeration.
+ * @param want
+ * objects of interest, ancestors of which will be included in
+ * the pack. Must not be {@code null}.
+ * @param have
+ * objects whose ancestors (up to and including {@code shallow}
+ * commits) do not need to be included in the pack because they
+ * are already available from elsewhere. Must not be
+ * {@code null}.
+ * @param shallow
+ * commits indicating the boundary of the history marked with
+ * {@code have}. Shallow commits have parents but those parents
+ * are considered not to be already available. Parents of
+ * {@code shallow} commits and earlier generations will be
+ * included in the pack if requested by {@code want}. Must not be
+ * {@code null}.
+ * @param noBitmaps
+ * collection of objects to be excluded from bitmap commit
+ * selection.
+ * @throws IOException
+ * an I/O problem occurred while reading objects.
+ */
+ public void preparePack(ProgressMonitor countingMonitor,
+ @NonNull Set<? extends ObjectId> want,
+ @NonNull Set<? extends ObjectId> have,
+ @NonNull Set<? extends ObjectId> shallow,
+ @NonNull Set<? extends ObjectId> noBitmaps) throws IOException {
try (ObjectWalk ow = getObjectWalk()) {
ow.assumeShallow(shallow);
- preparePack(countingMonitor, ow, want, have);
+ preparePack(countingMonitor, ow, want, have, noBitmaps);
}
}
@@ -784,13 +825,17 @@ public class PackWriter implements AutoCloseable {
* points of graph traversal). Pass {@link #NONE} if all objects
* reachable from {@code want} are desired, such as when serving
* a clone.
+ * @param noBitmaps
+ * collection of objects to be excluded from bitmap commit
+ * selection.
* @throws IOException
* when some I/O problem occur during reading objects.
*/
public void preparePack(ProgressMonitor countingMonitor,
@NonNull ObjectWalk walk,
@NonNull Set<? extends ObjectId> interestingObjects,
- @NonNull Set<? extends ObjectId> uninterestingObjects)
+ @NonNull Set<? extends ObjectId> uninterestingObjects,
+ @NonNull Set<? extends ObjectId> noBitmaps)
throws IOException {
if (countingMonitor == null)
countingMonitor = NullProgressMonitor.INSTANCE;
@@ -798,7 +843,7 @@ public class PackWriter implements AutoCloseable {
throw new IllegalArgumentException(
JGitText.get().shallowPacksRequireDepthWalk);
findObjectsToPack(countingMonitor, walk, interestingObjects,
- uninterestingObjects);
+ uninterestingObjects, noBitmaps);
}
/**
@@ -965,8 +1010,9 @@ public class PackWriter implements AutoCloseable {
/**
* Write the prepared pack to the supplied stream.
* <p>
- * Called after {@link #preparePack(ProgressMonitor, ObjectWalk, Set, Set)}
- * or {@link #preparePack(ProgressMonitor, Set, Set)}.
+ * Called after
+ * {@link #preparePack(ProgressMonitor, ObjectWalk, Set, Set, Set)} or
+ * {@link #preparePack(ProgressMonitor, Set, Set)}.
* <p>
* Performs delta search if enabled and writes the pack stream.
* <p>
@@ -1652,12 +1698,14 @@ public class PackWriter implements AutoCloseable {
private void findObjectsToPack(@NonNull ProgressMonitor countingMonitor,
@NonNull ObjectWalk walker, @NonNull Set<? extends ObjectId> want,
- @NonNull Set<? extends ObjectId> have) throws IOException {
+ @NonNull Set<? extends ObjectId> have,
+ @NonNull Set<? extends ObjectId> noBitmaps) throws IOException {
final long countingStart = System.currentTimeMillis();
beginPhase(PackingPhase.COUNTING, countingMonitor, ProgressMonitor.UNKNOWN);
stats.interestingObjects = Collections.unmodifiableSet(new HashSet<ObjectId>(want));
stats.uninterestingObjects = Collections.unmodifiableSet(new HashSet<ObjectId>(have));
+ excludeFromBitmapSelection = noBitmaps;
canBuildBitmaps = config.isBuildBitmaps()
&& !shallowPack
@@ -1874,7 +1922,6 @@ public class PackWriter implements AutoCloseable {
throws MissingObjectException, IncorrectObjectTypeException,
IOException {
BitmapBuilder haveBitmap = bitmapWalker.findObjects(have, null, true);
- bitmapWalker.reset();
BitmapBuilder wantBitmap = bitmapWalker.findObjects(want, haveBitmap,
false);
BitmapBuilder needBitmap = wantBitmap.andNot(haveBitmap);
@@ -2071,19 +2118,17 @@ public class PackWriter implements AutoCloseable {
PackWriterBitmapPreparer bitmapPreparer = new PackWriterBitmapPreparer(
reader, writeBitmaps, pm, stats.interestingObjects, config);
- Collection<PackWriterBitmapPreparer.BitmapCommit> selectedCommits =
- bitmapPreparer.selectCommits(numCommits);
+ Collection<PackWriterBitmapPreparer.BitmapCommit> selectedCommits = bitmapPreparer
+ .selectCommits(numCommits, excludeFromBitmapSelection);
beginPhase(PackingPhase.BUILDING_BITMAPS, pm, selectedCommits.size());
PackWriterBitmapWalker walker = bitmapPreparer.newBitmapWalker();
AnyObjectId last = null;
for (PackWriterBitmapPreparer.BitmapCommit cmit : selectedCommits) {
- if (cmit.isReuseWalker())
- walker.reset();
- else
+ if (!cmit.isReuseWalker()) {
walker = bitmapPreparer.newBitmapWalker();
-
+ }
BitmapBuilder bitmap = walker.findObjects(
Collections.singleton(cmit), null, false);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java
index 07a03b4040..8bedddb935 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapPreparer.java
@@ -141,6 +141,8 @@ class PackWriterBitmapPreparer {
*
* @param expectedCommitCount
* count of commits in the pack
+ * @param excludeFromBitmapSelection
+ * commits that should be excluded from bitmap selection
* @return commit objects for which bitmap indices should be built
* @throws IncorrectObjectTypeException
* if any of the processed objects is not a commit
@@ -149,7 +151,8 @@ class PackWriterBitmapPreparer {
* @throws MissingObjectException
* if an expected object is missing
*/
- Collection<BitmapCommit> selectCommits(int expectedCommitCount)
+ Collection<BitmapCommit> selectCommits(int expectedCommitCount,
+ Set<? extends ObjectId> excludeFromBitmapSelection)
throws IncorrectObjectTypeException, IOException,
MissingObjectException {
/*
@@ -164,7 +167,7 @@ class PackWriterBitmapPreparer {
RevWalk rw = new RevWalk(reader);
rw.setRetainBody(false);
CommitSelectionHelper selectionHelper = setupTipCommitBitmaps(rw,
- expectedCommitCount);
+ expectedCommitCount, excludeFromBitmapSelection);
pm.endTask();
int totCommits = selectionHelper.getCommitCount();
@@ -363,6 +366,8 @@ class PackWriterBitmapPreparer {
* @param expectedCommitCount
* expected count of commits. The actual count may be less due to
* unreachable garbage.
+ * @param excludeFromBitmapSelection
+ * commits that should be excluded from bitmap selection
* @return a {@link CommitSelectionHelper} containing bitmaps for the tip
* commits
* @throws IncorrectObjectTypeException
@@ -373,8 +378,10 @@ class PackWriterBitmapPreparer {
* if an expected object is missing
*/
private CommitSelectionHelper setupTipCommitBitmaps(RevWalk rw,
- int expectedCommitCount) throws IncorrectObjectTypeException,
- IOException, MissingObjectException {
+ int expectedCommitCount,
+ Set<? extends ObjectId> excludeFromBitmapSelection)
+ throws IncorrectObjectTypeException, IOException,
+ MissingObjectException {
BitmapBuilder reuse = commitBitmapIndex.newBitmapBuilder();
List<BitmapCommit> reuseCommits = new ArrayList<>();
for (PackBitmapIndexRemapper.Entry entry : bitmapRemapper) {
@@ -403,7 +410,8 @@ class PackWriterBitmapPreparer {
Set<RevCommit> peeledWant = new HashSet<>(want.size());
for (AnyObjectId objectId : want) {
RevObject ro = rw.peel(rw.parseAny(objectId));
- if (!(ro instanceof RevCommit) || reuse.contains(ro)) {
+ if (!(ro instanceof RevCommit) || reuse.contains(ro)
+ || excludeFromBitmapSelection.contains(ro)) {
continue;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapWalker.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapWalker.java
index 2ec4d568c7..a5c3b71eb2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapWalker.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriterBitmapWalker.java
@@ -44,7 +44,7 @@
package org.eclipse.jgit.internal.storage.pack;
import java.io.IOException;
-import java.util.Set;
+import java.util.Arrays;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
@@ -84,9 +84,60 @@ final class PackWriterBitmapWalker {
return countOfBitmapIndexMisses;
}
- BitmapBuilder findObjects(Set<? extends ObjectId> start, BitmapBuilder seen, boolean ignoreMissingStart)
+ BitmapBuilder findObjects(Iterable<? extends ObjectId> start, BitmapBuilder seen,
+ boolean ignoreMissing)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException {
+ if (!ignoreMissing) {
+ return findObjectsWalk(start, seen, false);
+ }
+
+ try {
+ return findObjectsWalk(start, seen, true);
+ } catch (MissingObjectException ignore) {
+ // An object reachable from one of the "start"s is missing.
+ // Walk from the "start"s one at a time so it can be excluded.
+ }
+
+ final BitmapBuilder result = bitmapIndex.newBitmapBuilder();
+ for (ObjectId obj : start) {
+ Bitmap bitmap = bitmapIndex.getBitmap(obj);
+ if (bitmap != null) {
+ result.or(bitmap);
+ }
+ }
+
+ for (ObjectId obj : start) {
+ if (result.contains(obj)) {
+ continue;
+ }
+ try {
+ result.or(findObjectsWalk(Arrays.asList(obj), result, false));
+ } catch (MissingObjectException ignore) {
+ // An object reachable from this "start" is missing.
+ //
+ // This can happen when the client specified a "have" line
+ // pointing to an object that is present but unreachable:
+ // "git prune" and "git fsck" only guarantee that the object
+ // database will continue to contain all objects reachable
+ // from a ref and does not guarantee connectivity for other
+ // objects in the object database.
+ //
+ // In this situation, skip the relevant "start" and move on
+ // to the next one.
+ //
+ // TODO(czhen): Make findObjectsWalk resume the walk instead
+ // once RevWalk and ObjectWalk support that.
+ }
+ }
+ return result;
+ }
+
+ private BitmapBuilder findObjectsWalk(Iterable<? extends ObjectId> start, BitmapBuilder seen,
+ boolean ignoreMissingStart)
throws MissingObjectException, IncorrectObjectTypeException,
IOException {
+ walker.reset();
final BitmapBuilder bitmapResult = bitmapIndex.newBitmapBuilder();
for (ObjectId obj : start) {
@@ -141,10 +192,6 @@ final class PackWriterBitmapWalker {
return bitmapResult;
}
- void reset() {
- walker.reset();
- }
-
/**
* A RevFilter that adds the visited commits to {@code bitmap} as a side
* effect.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchingProgressMonitor.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchingProgressMonitor.java
index 54c80522b8..a75293d6cb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchingProgressMonitor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchingProgressMonitor.java
@@ -46,6 +46,8 @@ package org.eclipse.jgit.lib;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
+import org.eclipse.jgit.lib.internal.WorkQueue;
+
/** ProgressMonitor that batches update events. */
public abstract class BatchingProgressMonitor implements ProgressMonitor {
private long delayStartTime;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java
index 1047a6df99..c4923a359e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java
@@ -43,6 +43,8 @@
package org.eclipse.jgit.lib;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
@@ -177,8 +179,8 @@ public class RebaseTodoFile {
while (tokenCount < 3 && nextSpace < lineEnd) {
switch (tokenCount) {
case 0:
- String actionToken = new String(buf, tokenBegin, nextSpace
- - tokenBegin - 1);
+ String actionToken = new String(buf, tokenBegin,
+ nextSpace - tokenBegin - 1, UTF_8);
tokenBegin = nextSpace;
action = RebaseTodoLine.Action.parse(actionToken);
if (action == null)
@@ -186,14 +188,14 @@ public class RebaseTodoFile {
break;
case 1:
nextSpace = RawParseUtils.next(buf, tokenBegin, ' ');
- String commitToken = new String(buf, tokenBegin, nextSpace
- - tokenBegin - 1);
+ String commitToken = new String(buf, tokenBegin,
+ nextSpace - tokenBegin - 1, UTF_8);
tokenBegin = nextSpace;
commit = AbbreviatedObjectId.fromString(commitToken);
break;
case 2:
- return new RebaseTodoLine(action, commit, RawParseUtils.decode(
- buf, tokenBegin, 1 + lineEnd));
+ return new RebaseTodoLine(action, commit,
+ RawParseUtils.decode(buf, tokenBegin, 1 + lineEnd));
}
tokenCount++;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
index aa70f4209b..bd23ab988d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -1151,6 +1151,33 @@ public abstract class Repository implements AutoCloseable {
}
/**
+ * Locate a reference to a commit and immediately parse its content.
+ * <p>
+ * This method only returns successfully if the commit object exists,
+ * is verified to be a commit, and was parsed without error.
+ *
+ * @param id
+ * name of the commit object.
+ * @return reference to the commit object. Never null.
+ * @throws MissingObjectException
+ * the supplied commit does not exist.
+ * @throws IncorrectObjectTypeException
+ * the supplied id is not a commit or an annotated tag.
+ * @throws IOException
+ * a pack file or loose object could not be read.
+ * @since 4.8
+ */
+ public RevCommit parseCommit(AnyObjectId id) throws IncorrectObjectTypeException,
+ IOException, MissingObjectException {
+ if (id instanceof RevCommit && ((RevCommit) id).getRawBuffer() != null) {
+ return (RevCommit) id;
+ }
+ try (RevWalk walk = new RevWalk(this)) {
+ return walk.parseCommit(id);
+ }
+ }
+
+ /**
* Create a new in-core index representation and read an index from disk.
* <p>
* The new index will be read before it is returned to the caller. Read
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
index baa5286862..53e9fe3c53 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java
@@ -55,6 +55,7 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.lib.internal.WorkQueue;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SubmoduleConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SubmoduleConfig.java
index 3126160c33..12f7b82343 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SubmoduleConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SubmoduleConfig.java
@@ -43,6 +43,10 @@
package org.eclipse.jgit.lib;
+import java.util.Locale;
+
+import org.eclipse.jgit.util.StringUtils;
+
/**
* Submodule section of a Git configuration file.
*
@@ -75,12 +79,17 @@ public class SubmoduleConfig {
@Override
public String toConfigValue() {
- return configValue;
+ return name().toLowerCase(Locale.ROOT).replace('_', '-');
}
@Override
public boolean matchConfigValue(String s) {
- return configValue.equals(s);
+ if (StringUtils.isEmptyOrNull(s)) {
+ return false;
+ }
+ s = s.replace('-', '_');
+ return name().equalsIgnoreCase(s)
+ || configValue.equalsIgnoreCase(s);
}
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TextProgressMonitor.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TextProgressMonitor.java
index 7675fccb69..c31c3c6939 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TextProgressMonitor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TextProgressMonitor.java
@@ -44,7 +44,10 @@
package org.eclipse.jgit.lib;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import java.io.IOException;
+import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
@@ -56,7 +59,7 @@ public class TextProgressMonitor extends BatchingProgressMonitor {
/** Initialize a new progress monitor. */
public TextProgressMonitor() {
- this(new PrintWriter(System.err));
+ this(new PrintWriter(new OutputStreamWriter(System.err, UTF_8)));
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WorkQueue.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/WorkQueue.java
index 07b87f58d2..3303f47722 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/WorkQueue.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/WorkQueue.java
@@ -41,7 +41,7 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package org.eclipse.jgit.lib;
+package org.eclipse.jgit.lib.internal;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
@@ -50,7 +50,7 @@ import java.util.concurrent.ThreadFactory;
/**
* Simple work queue to run tasks in the background
*/
-class WorkQueue {
+public class WorkQueue {
private static final ScheduledThreadPoolExecutor executor;
static final Object executorKiller;
@@ -94,7 +94,10 @@ class WorkQueue {
};
}
- static ScheduledThreadPoolExecutor getExecutor() {
+ /**
+ * @return the WorkQueue's executor
+ */
+ public static ScheduledThreadPoolExecutor getExecutor() {
return executor;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeStrategy.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeStrategy.java
index 656480e468..af3d5ca078 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeStrategy.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeStrategy.java
@@ -49,6 +49,8 @@ import java.text.MessageFormat;
import java.util.HashMap;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.Repository;
/**
@@ -170,4 +172,20 @@ public abstract class MergeStrategy {
* @return the new merge instance which implements this strategy.
*/
public abstract Merger newMerger(Repository db, boolean inCore);
+
+ /**
+ * Create a new merge instance.
+ * <p>
+ * The merge will happen in memory, working folder will not be modified, in
+ * case of a non-trivial merge that requires manual resolution, the merger
+ * will fail.
+ *
+ * @param inserter
+ * inserter to write results back to.
+ * @param config
+ * repo config for reading diff algorithm settings.
+ * @return the new merge instance which implements this strategy.
+ * @since 4.8
+ */
+ public abstract Merger newMerger(ObjectInserter inserter, Config config);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java
index bee2d03523..0c4488c984 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/Merger.java
@@ -47,6 +47,7 @@ package org.eclipse.jgit.merge;
import java.io.IOException;
import java.text.MessageFormat;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.NoMergeBaseException;
import org.eclipse.jgit.errors.NoMergeBaseException.MergeBaseFailureReason;
@@ -70,7 +71,15 @@ import org.eclipse.jgit.treewalk.CanonicalTreeParser;
* Instance of a specific {@link MergeStrategy} for a single {@link Repository}.
*/
public abstract class Merger {
- /** The repository this merger operates on. */
+ /**
+ * The repository this merger operates on.
+ * <p>
+ * Null if and only if the merger was constructed with {@link
+ * #Merger(ObjectInserter)}. Callers that want to assume the repo is not null
+ * (e.g. because of a previous check that the merger is not in-core) may use
+ * {@link #nonNullRepo()}.
+ */
+ @Nullable
protected final Repository db;
/** Reader to support {@link #walk} and other object loading. */
@@ -104,20 +113,55 @@ public abstract class Merger {
* the repository this merger will read and write data on.
*/
protected Merger(final Repository local) {
+ if (local == null) {
+ throw new NullPointerException(JGitText.get().repositoryIsRequired);
+ }
db = local;
- inserter = db.newObjectInserter();
+ inserter = local.newObjectInserter();
reader = inserter.newReader();
walk = new RevWalk(reader);
}
/**
+ * Create a new in-core merge instance from an inserter.
+ *
+ * @param oi
+ * the inserter to write objects to. Will be closed at the
+ * conclusion of {@code merge}, unless {@code flush} is false.
+ * @since 4.8
+ */
+ protected Merger(ObjectInserter oi) {
+ db = null;
+ inserter = oi;
+ reader = oi.newReader();
+ walk = new RevWalk(reader);
+ }
+
+ /**
* @return the repository this merger operates on.
*/
+ @Nullable
public Repository getRepository() {
return db;
}
- /** @return an object writer to create objects in {@link #getRepository()}. */
+ /**
+ * @return non-null repository instance
+ * @throws NullPointerException
+ * if the merger was constructed without a repository.
+ * @since 4.8
+ */
+ protected Repository nonNullRepo() {
+ if (db == null) {
+ throw new NullPointerException(JGitText.get().repositoryIsRequired);
+ }
+ return db;
+ }
+
+ /**
+ * @return an object writer to create objects, writing objects to {@link
+ * #getRepository()} (if a repository was provided).
+ */
public ObjectInserter getObjectInserter() {
return inserter;
}
@@ -131,7 +175,9 @@ public abstract class Merger {
*
* @param oi
* the inserter instance to use. Must be associated with the
- * repository instance returned by {@link #getRepository()}.
+ * repository instance returned by {@link #getRepository()} (if a
+ * repository was provided). Will be closed at the conclusion of
+ * {@code merge}, unless {@code flush} is false.
*/
public void setObjectInserter(ObjectInserter oi) {
walk.close();
@@ -173,9 +219,9 @@ public abstract class Merger {
*
* @since 3.5
* @param flush
- * whether to flush the underlying object inserter when finished to
- * store any content-merged blobs and virtual merged bases; if
- * false, callers are responsible for flushing.
+ * whether to flush and close the underlying object inserter when
+ * finished to store any content-merged blobs and virtual merged
+ * bases; if false, callers are responsible for flushing.
* @param tips
* source trees to be combined together. The merge base is not
* included in this set.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
index f8e1998ed7..1375cd3ea2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/RecursiveMerger.java
@@ -61,7 +61,9 @@ import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.NoMergeBaseException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -111,6 +113,17 @@ public class RecursiveMerger extends ResolveMerger {
}
/**
+ * Normal recursive merge, implies inCore.
+ *
+ * @param inserter
+ * @param config
+ * @since 4.8
+ */
+ protected RecursiveMerger(ObjectInserter inserter, Config config) {
+ super(inserter, config);
+ }
+
+ /**
* Get a single base commit for two given commits. If the two source commits
* have more than one base commit recursively merge the base commits
* together until you end up with a single base commit.
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 90107be2ed..86003e9243 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -44,6 +44,9 @@
*/
package org.eclipse.jgit.merge;
+import static org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm.HISTOGRAM;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_DIFF_SECTION;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_ALGORITHM;
import static org.eclipse.jgit.lib.Constants.CHARACTER_ENCODING;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
@@ -79,9 +82,10 @@ import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.IndexWriteException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.NoWorkTreeException;
-import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevTree;
@@ -266,18 +270,25 @@ public class ResolveMerger extends ThreeWayMerger {
*/
protected MergeAlgorithm mergeAlgorithm;
+ private static MergeAlgorithm getMergeAlgorithm(Config config) {
+ SupportedAlgorithm diffAlg = config.getEnum(
+ CONFIG_DIFF_SECTION, null, CONFIG_KEY_ALGORITHM,
+ HISTOGRAM);
+ return new MergeAlgorithm(DiffAlgorithm.getAlgorithm(diffAlg));
+ }
+
+ private static String[] defaultCommitNames() {
+ return new String[] { "BASE", "OURS", "THEIRS" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
/**
* @param local
* @param inCore
*/
protected ResolveMerger(Repository local, boolean inCore) {
super(local);
- SupportedAlgorithm diffAlg = local.getConfig().getEnum(
- ConfigConstants.CONFIG_DIFF_SECTION, null,
- ConfigConstants.CONFIG_KEY_ALGORITHM,
- SupportedAlgorithm.HISTOGRAM);
- mergeAlgorithm = new MergeAlgorithm(DiffAlgorithm.getAlgorithm(diffAlg));
- commitNames = new String[] { "BASE", "OURS", "THEIRS" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ mergeAlgorithm = getMergeAlgorithm(local.getConfig());
+ commitNames = defaultCommitNames();
this.inCore = inCore;
if (inCore) {
@@ -295,10 +306,24 @@ public class ResolveMerger extends ThreeWayMerger {
this(local, false);
}
+ /**
+ * @param inserter
+ * @param config
+ * @since 4.8
+ */
+ protected ResolveMerger(ObjectInserter inserter, Config config) {
+ super(inserter);
+ mergeAlgorithm = getMergeAlgorithm(config);
+ commitNames = defaultCommitNames();
+ inCore = true;
+ implicitDirCache = false;
+ dircache = DirCache.newInCore();
+ }
+
@Override
protected boolean mergeImpl() throws IOException {
if (implicitDirCache)
- dircache = getRepository().lockDirCache();
+ dircache = nonNullRepo().lockDirCache();
try {
return mergeTrees(mergeBase(), sourceTrees[0], sourceTrees[1],
@@ -315,7 +340,7 @@ public class ResolveMerger extends ThreeWayMerger {
// of a non-empty directory, for which delete() would fail.
for (int i = toBeDeleted.size() - 1; i >= 0; i--) {
String fileName = toBeDeleted.get(i);
- File f = new File(db.getWorkTree(), fileName);
+ File f = new File(nonNullRepo().getWorkTree(), fileName);
if (!f.delete())
if (!f.isDirectory())
failingPaths.put(fileName,
@@ -348,7 +373,7 @@ public class ResolveMerger extends ThreeWayMerger {
return;
}
- DirCache dc = db.readDirCache();
+ DirCache dc = nonNullRepo().readDirCache();
Iterator<String> mpathsIt=modifiedFiles.iterator();
while(mpathsIt.hasNext()) {
String mpath=mpathsIt.next();
@@ -785,8 +810,8 @@ public class ResolveMerger extends ThreeWayMerger {
*/
private File writeMergedFile(MergeResult<RawText> result)
throws FileNotFoundException, IOException {
- File workTree = db.getWorkTree();
- FS fs = db.getFS();
+ File workTree = nonNullRepo().getWorkTree();
+ FS fs = nonNullRepo().getFS();
File of = new File(workTree, tw.getPathString());
File parentFolder = of.getParentFile();
if (!fs.exists(parentFolder))
@@ -802,7 +827,7 @@ public class ResolveMerger extends ThreeWayMerger {
private ObjectId insertMergeResult(MergeResult<RawText> result)
throws IOException {
TemporaryBuffer.LocalFile buf = new TemporaryBuffer.LocalFile(
- db.getDirectory(), 10 << 20);
+ db != null ? nonNullRepo().getDirectory() : null, 10 << 20);
try {
new MergeFormatter().formatMerge(buf, result,
Arrays.asList(commitNames), CHARACTER_ENCODING);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyOneSided.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyOneSided.java
index 12d6c6b413..2224dbc448 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyOneSided.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyOneSided.java
@@ -46,7 +46,9 @@ package org.eclipse.jgit.merge;
import java.io.IOException;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.Repository;
/**
@@ -89,6 +91,11 @@ public class StrategyOneSided extends MergeStrategy {
return new OneSide(db, treeIndex);
}
+ @Override
+ public Merger newMerger(final ObjectInserter inserter, final Config config) {
+ return new OneSide(inserter, treeIndex);
+ }
+
static class OneSide extends Merger {
private final int treeIndex;
@@ -97,6 +104,11 @@ public class StrategyOneSided extends MergeStrategy {
treeIndex = index;
}
+ protected OneSide(final ObjectInserter inserter, final int index) {
+ super(inserter);
+ treeIndex = index;
+ }
+
@Override
protected boolean mergeImpl() throws IOException {
return treeIndex < sourceTrees.length;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyRecursive.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyRecursive.java
index 22e608ec9d..56128dd93e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyRecursive.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyRecursive.java
@@ -43,6 +43,8 @@
package org.eclipse.jgit.merge;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.Repository;
/**
@@ -63,6 +65,11 @@ public class StrategyRecursive extends StrategyResolve {
}
@Override
+ public ThreeWayMerger newMerger(ObjectInserter inserter, Config config) {
+ return new RecursiveMerger(inserter, config);
+ }
+
+ @Override
public String getName() {
return "recursive"; //$NON-NLS-1$
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyResolve.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyResolve.java
index 07368e5746..17044b53ae 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyResolve.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategyResolve.java
@@ -43,6 +43,8 @@
*/
package org.eclipse.jgit.merge;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.Repository;
/**
@@ -60,8 +62,16 @@ public class StrategyResolve extends ThreeWayMergeStrategy {
return new ResolveMerger(db, inCore);
}
+ /**
+ * @since 4.8
+ */
+ @Override
+ public ThreeWayMerger newMerger(ObjectInserter inserter, Config config) {
+ return new ResolveMerger(inserter, config);
+ }
+
@Override
public String getName() {
return "resolve"; //$NON-NLS-1$
}
-} \ No newline at end of file
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java
index ec903c139d..cd427bd8fb 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/StrategySimpleTwoWayInCore.java
@@ -49,6 +49,7 @@ import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.errors.UnmergedPathException;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
@@ -89,6 +90,14 @@ public class StrategySimpleTwoWayInCore extends ThreeWayMergeStrategy {
return newMerger(db);
}
+ /**
+ * @since 4.8
+ */
+ @Override
+ public ThreeWayMerger newMerger(ObjectInserter inserter, Config config) {
+ return new InCoreMerger(inserter);
+ }
+
private static class InCoreMerger extends ThreeWayMerger {
private static final int T_BASE = 0;
@@ -110,6 +119,12 @@ public class StrategySimpleTwoWayInCore extends ThreeWayMergeStrategy {
cache = DirCache.newInCore();
}
+ InCoreMerger(final ObjectInserter inserter) {
+ super(inserter);
+ tw = new NameConflictTreeWalk(null, reader);
+ cache = DirCache.newInCore();
+ }
+
@Override
protected boolean mergeImpl() throws IOException {
tw.addTree(mergeBase());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ThreeWayMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ThreeWayMerger.java
index fbedaef865..b3ef0fb3e4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ThreeWayMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ThreeWayMerger.java
@@ -50,6 +50,7 @@ import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
@@ -85,6 +86,17 @@ public abstract class ThreeWayMerger extends Merger {
}
/**
+ * Create a new in-core merge instance from an inserter.
+ *
+ * @param inserter
+ * the inserter to write objects to.
+ * @since 4.8
+ */
+ protected ThreeWayMerger(ObjectInserter inserter) {
+ super(inserter);
+ }
+
+ /**
* Set the common ancestor tree.
*
* @param id
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java
index 5d7e72dd29..73ce9854e0 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/MergeBaseGenerator.java
@@ -69,26 +69,21 @@ import org.eclipse.jgit.internal.JGitText;
*/
class MergeBaseGenerator extends Generator {
private static final int PARSED = RevWalk.PARSED;
-
private static final int IN_PENDING = RevWalk.SEEN;
-
private static final int POPPED = RevWalk.TEMP_MARK;
-
private static final int MERGE_BASE = RevWalk.REWRITE;
private final RevWalk walker;
-
private final DateRevQueue pending;
private int branchMask;
-
private int recarryTest;
-
private int recarryMask;
-
private int mergeBaseAncestor = -1;
private LinkedList<RevCommit> ret = new LinkedList<>();
+ private CarryStack stack;
+
MergeBaseGenerator(final RevWalk w) {
walker = w;
pending = new DateRevQueue();
@@ -202,29 +197,56 @@ class MergeBaseGenerator extends Generator {
return null;
}
- private void carryOntoHistory(RevCommit c, final int carry) {
+ private void carryOntoHistory(RevCommit c, int carry) {
+ stack = null;
for (;;) {
- final RevCommit[] pList = c.parents;
- if (pList == null)
- return;
- final int n = pList.length;
- if (n == 0)
- return;
-
- for (int i = 1; i < n; i++) {
- final RevCommit p = pList[i];
- if (!carryOntoOne(p, carry))
- carryOntoHistory(p, carry);
+ carryOntoHistoryInnerLoop(c, carry);
+ if (stack == null) {
+ break;
+ }
+ c = stack.c;
+ carry = stack.carry;
+ stack = stack.prev;
+ }
+ }
+
+ private void carryOntoHistoryInnerLoop(RevCommit c, int carry) {
+ for (;;) {
+ RevCommit[] parents = c.parents;
+ if (parents == null || parents.length == 0) {
+ break;
}
- c = pList[0];
- if (carryOntoOne(c, carry))
+ int e = parents.length - 1;
+ for (int i = 0; i < e; i++) {
+ RevCommit p = parents[i];
+ if (carryOntoOne(p, carry) == CONTINUE) {
+ // Walking p will be required, buffer p on stack.
+ stack = new CarryStack(stack, p, carry);
+ }
+ // For other results from carryOntoOne:
+ // HAVE_ALL: p has all bits, do nothing to skip that path.
+ // CONTINUE_ON_STACK: callee pushed StackElement for p.
+ }
+
+ c = parents[e];
+ if (carryOntoOne(c, carry) != CONTINUE) {
break;
+ }
}
}
- private boolean carryOntoOne(final RevCommit p, final int carry) {
- final boolean haveAll = (p.flags & carry) == carry;
+ private static final int CONTINUE = 0;
+ private static final int HAVE_ALL = 1;
+ private static final int CONTINUE_ON_STACK = 2;
+
+ private int carryOntoOne(RevCommit p, int carry) {
+ // If we already had all carried flags, our parents do too.
+ // Return HAVE_ALL to stop caller from running down this leg
+ // of the revision graph any further.
+ //
+ // Otherwise return CONTINUE to ask the caller to walk history.
+ int rc = (p.flags & carry) == carry ? HAVE_ALL : CONTINUE;
p.flags |= carry;
if ((p.flags & recarryMask) == recarryTest) {
@@ -232,17 +254,23 @@ class MergeBaseGenerator extends Generator {
// voted to be one. Inject ourselves back at the front of the
// pending queue and tell all of our ancestors they are within
// the merge base now.
- //
p.flags &= ~POPPED;
pending.add(p);
- carryOntoHistory(p, branchMask | MERGE_BASE);
- return true;
+ stack = new CarryStack(stack, p, branchMask | MERGE_BASE);
+ return CONTINUE_ON_STACK;
}
+ return rc;
+ }
- // If we already had all carried flags, our parents do too.
- // Return true to stop the caller from running down this leg
- // of the revision graph any further.
- //
- return haveAll;
+ private static class CarryStack {
+ final CarryStack prev;
+ final RevCommit c;
+ final int carry;
+
+ CarryStack(CarryStack prev, RevCommit c, int carry) {
+ this.prev = prev;
+ this.c = c;
+ this.carry = carry;
+ }
}
}
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 37d70e3a14..0920f21563 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
@@ -58,6 +58,7 @@ import org.eclipse.jgit.internal.storage.pack.PackWriter;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
@@ -84,6 +85,8 @@ import org.eclipse.jgit.storage.pack.PackConfig;
public class BundleWriter {
private final Repository db;
+ private final ObjectReader reader;
+
private final Map<String, ObjectId> include;
private final Set<RevCommit> assume;
@@ -100,8 +103,26 @@ public class BundleWriter {
* @param repo
* repository where objects are stored.
*/
- public BundleWriter(final Repository repo) {
+ public BundleWriter(Repository repo) {
db = repo;
+ reader = null;
+ include = new TreeMap<>();
+ assume = new HashSet<>();
+ tagTargets = new HashSet<>();
+ }
+
+ /**
+ * Create a writer for a bundle.
+ *
+ * @param or
+ * reader for reading objects. Will be closed at the end of {@link
+ * #writeBundle(ProgressMonitor, OutputStream)}, but readers may be
+ * reused after closing.
+ * @since 4.8
+ */
+ public BundleWriter(ObjectReader or) {
+ db = null;
+ reader = or;
include = new TreeMap<>();
assume = new HashSet<>();
tagTargets = new HashSet<>();
@@ -112,7 +133,8 @@ public class BundleWriter {
*
* @param pc
* configuration controlling packing parameters. If null the
- * source repository's settings will be used.
+ * source repository's settings will be used, or the default
+ * settings if constructed without a repo.
*/
public void setPackConfig(PackConfig pc) {
this.packConfig = pc;
@@ -196,10 +218,7 @@ public class BundleWriter {
*/
public void writeBundle(ProgressMonitor monitor, OutputStream os)
throws IOException {
- PackConfig pc = packConfig;
- if (pc == null)
- pc = new PackConfig(db);
- try (PackWriter packWriter = new PackWriter(pc, db.newObjectReader())) {
+ try (PackWriter packWriter = newPackWriter()) {
packWriter.setObjectCountCallback(callback);
final HashSet<ObjectId> inc = new HashSet<>();
@@ -242,6 +261,14 @@ public class BundleWriter {
}
}
+ private PackWriter newPackWriter() {
+ PackConfig pc = packConfig;
+ if (pc == null) {
+ pc = db != null ? new PackConfig(db) : new PackConfig();
+ }
+ return new PackWriter(pc, reader != null ? reader : db.newObjectReader());
+ }
+
/**
* Set the {@link ObjectCountCallback}.
* <p>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
index 58fdd25745..17af0b9838 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -58,6 +58,7 @@ import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK;
+import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
@@ -235,7 +236,7 @@ public class UploadPack {
private InputStream rawIn;
- private OutputStream rawOut;
+ private ResponseBufferedOutputStream rawOut;
private PacketLineIn pckIn;
@@ -644,11 +645,10 @@ public class UploadPack {
* other network connections this should be null.
* @throws IOException
*/
- public void upload(final InputStream input, final OutputStream output,
+ public void upload(final InputStream input, OutputStream output,
final OutputStream messages) throws IOException {
try {
rawIn = input;
- rawOut = output;
if (messages != null)
msgOut = messages;
@@ -656,11 +656,17 @@ public class UploadPack {
final Thread caller = Thread.currentThread();
timer = new InterruptTimer(caller.getName() + "-Timer"); //$NON-NLS-1$
TimeoutInputStream i = new TimeoutInputStream(rawIn, timer);
- TimeoutOutputStream o = new TimeoutOutputStream(rawOut, timer);
+ @SuppressWarnings("resource")
+ TimeoutOutputStream o = new TimeoutOutputStream(output, timer);
i.setTimeout(timeout * 1000);
o.setTimeout(timeout * 1000);
rawIn = i;
- rawOut = o;
+ output = o;
+ }
+
+ rawOut = new ResponseBufferedOutputStream(output);
+ if (biDirectionalPipe) {
+ rawOut.stopBuffering();
}
pckIn = new PacketLineIn(rawIn);
@@ -714,6 +720,8 @@ public class UploadPack {
private void service() throws IOException {
boolean sendPack;
+ // If it's a non-bidi request, we need to read the entire request before
+ // writing a response. Buffer the response until then.
try {
if (biDirectionalPipe)
sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut));
@@ -769,6 +777,8 @@ public class UploadPack {
throw new UploadPackInternalServerErrorException(err);
}
throw err;
+ } finally {
+ rawOut.stopBuffering();
}
if (sendPack)
@@ -1513,7 +1523,7 @@ public class UploadPack {
walk.reset();
ObjectWalk ow = rw.toObjectWalkWithSameObjects();
- pw.preparePack(pm, ow, wantAll, commonBase);
+ pw.preparePack(pm, ow, wantAll, commonBase, PackWriter.NONE);
rw = ow;
}
@@ -1572,4 +1582,47 @@ public class UploadPack {
adv.addSymref(Constants.HEAD, head.getLeaf().getName());
}
}
+
+ private static class ResponseBufferedOutputStream extends OutputStream {
+ private final OutputStream rawOut;
+
+ private OutputStream out;
+
+ ResponseBufferedOutputStream(OutputStream rawOut) {
+ this.rawOut = rawOut;
+ this.out = new ByteArrayOutputStream();
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ out.write(b);
+ }
+
+ @Override
+ public void write(byte b[]) throws IOException {
+ out.write(b);
+ }
+
+ @Override
+ public void write(byte b[], int off, int len) throws IOException {
+ out.write(b, off, len);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ out.flush();
+ }
+
+ @Override
+ public void close() throws IOException {
+ out.close();
+ }
+
+ void stopBuffering() throws IOException {
+ if (out != rawOut) {
+ ((ByteArrayOutputStream) out).writeTo(rawOut);
+ out = rawOut;
+ }
+ }
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
index c0b29ef930..59cf7989d4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
@@ -45,6 +45,7 @@ package org.eclipse.jgit.treewalk;
import java.io.IOException;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.lib.FileMode;
@@ -110,7 +111,7 @@ public class NameConflictTreeWalk extends TreeWalk {
* the reader the walker will obtain tree data from.
* @since 4.3
*/
- public NameConflictTreeWalk(Repository repo, final ObjectReader or) {
+ public NameConflictTreeWalk(@Nullable Repository repo, final ObjectReader or) {
super(repo, or);
}
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 6b537a4775..ed5b87d5ca 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -516,7 +516,13 @@ public abstract class FS {
if (env != null) {
pb.environment().putAll(env);
}
- Process p = pb.start();
+ Process p;
+ try {
+ p = pb.start();
+ } catch (IOException e) {
+ // Process failed to start
+ throw new CommandFailedException(-1, e.getMessage(), e);
+ }
p.getOutputStream().close();
GobblerThread gobbler = new GobblerThread(p, command, dir);
gobbler.start();
@@ -877,7 +883,7 @@ public abstract class FS {
}
/**
- * See {@link FileUtils#relativize(String, String)}.
+ * See {@link FileUtils#relativizePath(String, String, String, boolean)}.
*
* @param base
* The path against which <code>other</code> should be
@@ -886,11 +892,11 @@ public abstract class FS {
* The path that will be made relative to <code>base</code>.
* @return A relative path that, when resolved against <code>base</code>,
* will yield the original <code>other</code>.
- * @see FileUtils#relativize(String, String)
+ * @see FileUtils#relativizePath(String, String, String, boolean)
* @since 3.7
*/
public String relativize(String base, String other) {
- return FileUtils.relativize(base, other);
+ return FileUtils.relativizePath(base, other, File.separator, this.isCaseSensitive());
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
index 1f20e9700d..76dbb8756e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java
@@ -468,10 +468,71 @@ public class FileUtils {
throw new IOException(JGitText.get().cannotCreateTempDir);
}
+
+ /**
+ * @deprecated Use the more-clearly-named
+ * {@link FileUtils#relativizeNativePath(String, String)}
+ * instead, or directly call
+ * {@link FileUtils#relativizePath(String, String, String, boolean)}
+ *
+ * Expresses <code>other</code> as a relative file path from
+ * <code>base</code>. File-separator and case sensitivity are
+ * based on the current file system.
+ *
+ * See also
+ * {@link FileUtils#relativizePath(String, String, String, boolean)}.
+ *
+ * @param base
+ * Base path
+ * @param other
+ * Destination path
+ * @return Relative path from <code>base</code> to <code>other</code>
+ * @since 3.7
+ */
+ @Deprecated
+ public static String relativize(String base, String other) {
+ return relativizeNativePath(base, other);
+ }
+
+ /**
+ * Expresses <code>other</code> as a relative file path from <code>base</code>.
+ * File-separator and case sensitivity are based on the current file system.
+ *
+ * See also {@link FileUtils#relativizePath(String, String, String, boolean)}.
+ *
+ * @param base
+ * Base path
+ * @param other
+ * Destination path
+ * @return Relative path from <code>base</code> to <code>other</code>
+ * @since 4.8
+ */
+ public static String relativizeNativePath(String base, String other) {
+ return FS.DETECTED.relativize(base, other);
+ }
+
+ /**
+ * Expresses <code>other</code> as a relative file path from <code>base</code>.
+ * File-separator and case sensitivity are based on Git's internal representation of files (which matches Unix).
+ *
+ * See also {@link FileUtils#relativizePath(String, String, String, boolean)}.
+ *
+ * @param base
+ * Base path
+ * @param other
+ * Destination path
+ * @return Relative path from <code>base</code> to <code>other</code>
+ * @since 4.8
+ */
+ public static String relativizeGitPath(String base, String other) {
+ return relativizePath(base, other, "/", false); //$NON-NLS-1$
+ }
+
+
/**
- * This will try and make a given path relative to another.
+ * Expresses <code>other</code> as a relative file path from <code>base</code>
* <p>
- * For example, if this is called with the two following paths :
+ * For example, if called with the two following paths :
*
* <pre>
* <code>base = "c:\\Users\\jdoe\\eclipse\\git\\project"</code>
@@ -480,9 +541,7 @@ public class FileUtils {
*
* This will return "..\\another_project\\pom.xml".
* </p>
- * <p>
- * This method uses {@link File#separator} to split the paths into segments.
- * </p>
+ *
* <p>
* <b>Note</b> that this will return the empty String if <code>base</code>
* and <code>other</code> are equal.
@@ -494,29 +553,32 @@ public class FileUtils {
* folder and not a file.
* @param other
* The path that will be made relative to <code>base</code>.
+ * @param dirSeparator
+ * A string that separates components of the path. In practice, this is "/" or "\\".
+ * @param caseSensitive
+ * Whether to consider differently-cased directory names as distinct
* @return A relative path that, when resolved against <code>base</code>,
* will yield the original <code>other</code>.
- * @since 3.7
+ * @since 4.8
*/
- public static String relativize(String base, String other) {
+ public static String relativizePath(String base, String other, String dirSeparator, boolean caseSensitive) {
if (base.equals(other))
return ""; //$NON-NLS-1$
- final boolean ignoreCase = !FS.DETECTED.isCaseSensitive();
- final String[] baseSegments = base.split(Pattern.quote(File.separator));
+ final String[] baseSegments = base.split(Pattern.quote(dirSeparator));
final String[] otherSegments = other.split(Pattern
- .quote(File.separator));
+ .quote(dirSeparator));
int commonPrefix = 0;
while (commonPrefix < baseSegments.length
&& commonPrefix < otherSegments.length) {
- if (ignoreCase
+ if (caseSensitive
&& baseSegments[commonPrefix]
- .equalsIgnoreCase(otherSegments[commonPrefix]))
+ .equals(otherSegments[commonPrefix]))
commonPrefix++;
- else if (!ignoreCase
+ else if (!caseSensitive
&& baseSegments[commonPrefix]
- .equals(otherSegments[commonPrefix]))
+ .equalsIgnoreCase(otherSegments[commonPrefix]))
commonPrefix++;
else
break;
@@ -524,11 +586,11 @@ public class FileUtils {
final StringBuilder builder = new StringBuilder();
for (int i = commonPrefix; i < baseSegments.length; i++)
- builder.append("..").append(File.separator); //$NON-NLS-1$
+ builder.append("..").append(dirSeparator); //$NON-NLS-1$
for (int i = commonPrefix; i < otherSegments.length; i++) {
builder.append(otherSegments[i]);
if (i < otherSegments.length - 1)
- builder.append(File.separator);
+ builder.append(dirSeparator);
}
return builder.toString();
}