diff options
author | Matthias Sohn <matthias.sohn@sap.com> | 2021-09-09 14:41:30 +0200 |
---|---|---|
committer | Matthias Sohn <matthias.sohn@sap.com> | 2021-09-13 23:53:34 +0200 |
commit | a19494b735528b6eb678ac9dbc6a92b8db8560ab (patch) | |
tree | 29df569134d27876ad3b32a8f3f6daf4dffde5b5 /org.eclipse.jgit | |
parent | b9653ccdad437c5449f8eaf0c4e9cfdd2afe4519 (diff) | |
parent | a3a8de310847963bd8fadba33de17abd974ae710 (diff) | |
download | jgit-a19494b735528b6eb678ac9dbc6a92b8db8560ab.tar.gz jgit-a19494b735528b6eb678ac9dbc6a92b8db8560ab.zip |
Merge branch 'master' into next
* master: (38 commits)
Revert "DFS block cache: Refactor to enable parallel index loading"
GitServlet: allow to override default error handlers
Silence API error for new interface method ProtocolV2Hook#onObjectInfo
transport: add object-info capability
Ignore IllegalStateException if JVM is already shutting down
Update orbit to R20210825222808 for 2021-09
Update spotbugs-maven-plugin to 4.3.0
Update ant to 1.10.11 also in pom.xml
DFS block cache: add additional stats to DfsReaderIoStats
Update Orbit to S20210817231813
[gpg] Better GPG home directory determination
FS: cleanup use of final modifier
Ensure FS#searchPath only selects executable files
RevWalk: getMergedInto's result is wrong on the second call
DFS block cache: Refactor to enable parallel index loading
[test] Create keystore with the keytool of the running JDK
[gpg] Update to Bouncy Castle 1.69
[test] Create keystore with the keytool of the running JDK
[sshd] Minor code clean-up
Support commit.template config property
...
Change-Id: I9f99e9a513a23c0c0d252334e79c351512d7355e
Diffstat (limited to 'org.eclipse.jgit')
18 files changed, 498 insertions, 36 deletions
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters index 14c505de01..83a83817f4 100644 --- a/org.eclipse.jgit/.settings/.api_filters +++ b/org.eclipse.jgit/.settings/.api_filters @@ -8,6 +8,14 @@ </message_arguments> </filter> </resource> + <resource path="src/org/eclipse/jgit/transport/ProtocolV2Hook.java" type="org.eclipse.jgit.transport.ProtocolV2Hook"> + <filter id="404000815"> + <message_arguments> + <message_argument value="org.eclipse.jgit.transport.ProtocolV2Hook"/> + <message_argument value="onObjectInfo(ObjectInfoRequest)"/> + </message_arguments> + </filter> + </resource> <resource path="src/org/eclipse/jgit/transport/SshConstants.java" type="org.eclipse.jgit.transport.SshConstants"> <filter id="1142947843"> <message_arguments> 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 33e7452d96..3acceab098 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -365,6 +365,7 @@ invalidBooleanValue=Invalid boolean value: {0}.{1}={2} invalidChannel=Invalid channel {0} invalidCommitParentNumber=Invalid commit parent number invalidDepth=Invalid depth: {0} +invalidEncoding=Invalid encoding from git config i18n.commitEncoding: {0} invalidEncryption=Invalid encryption invalidExpandWildcard=ExpandFromSource on a refspec that can have mismatched wildcards does not make sense. invalidFilter=Invalid filter: {0} 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 cf7bc1f263..3aa711455b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java @@ -173,7 +173,11 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> { Repository repository = init(); FetchResult fetchResult = null; Thread cleanupHook = new Thread(() -> cleanup()); - Runtime.getRuntime().addShutdownHook(cleanupHook); + try { + Runtime.getRuntime().addShutdownHook(cleanupHook); + } catch (IllegalStateException e) { + // ignore - the VM is already shutting down + } try { fetchResult = fetch(repository, u); } catch (IOException ioe) { @@ -197,7 +201,11 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> { cleanup(); throw e; } finally { - Runtime.getRuntime().removeShutdownHook(cleanupHook); + try { + Runtime.getRuntime().removeShutdownHook(cleanupHook); + } catch (IllegalStateException e) { + // ignore - the VM is already shutting down + } } if (!noCheckout) { try { 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 8622e0a1a8..76340dabb3 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -393,6 +393,7 @@ public class JGitText extends TranslationBundle { /***/ public String invalidChannel; /***/ public String invalidCommitParentNumber; /***/ public String invalidDepth; + /***/ public String invalidEncoding; /***/ public String invalidEncryption; /***/ public String invalidExpandWildcard; /***/ public String invalidFilter; 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 96ca690c1c..9be3df3b17 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 @@ -25,6 +25,7 @@ import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.text.MessageFormat; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.zip.CRC32; import java.util.zip.DataFormatException; import java.util.zip.Inflater; @@ -162,10 +163,17 @@ public final class DfsPackFile extends BlockBasedFile { try { DfsStreamKey idxKey = desc.getStreamKey(INDEX); + AtomicBoolean cacheHit = new AtomicBoolean(true); DfsBlockCache.Ref<PackIndex> idxref = cache.getOrLoadRef( idxKey, REF_POSITION, - () -> loadPackIndex(ctx, idxKey)); + () -> { + cacheHit.set(false); + return loadPackIndex(ctx, idxKey); + }); + if (cacheHit.get()) { + ctx.stats.idxCacheHit++; + } PackIndex idx = idxref.get(); if (index == null && idx != null) { index = idx; @@ -200,10 +208,17 @@ public final class DfsPackFile extends BlockBasedFile { PackIndex idx = idx(ctx); PackReverseIndex revidx = getReverseIdx(ctx); DfsStreamKey bitmapKey = desc.getStreamKey(BITMAP_INDEX); + AtomicBoolean cacheHit = new AtomicBoolean(true); DfsBlockCache.Ref<PackBitmapIndex> idxref = cache.getOrLoadRef( bitmapKey, REF_POSITION, - () -> loadBitmapIndex(ctx, bitmapKey, idx, revidx)); + () -> { + cacheHit.set(false); + return loadBitmapIndex(ctx, bitmapKey, idx, revidx); + }); + if (cacheHit.get()) { + ctx.stats.bitmapCacheHit++; + } PackBitmapIndex bmidx = idxref.get(); if (bitmapIndex == null && bmidx != null) { bitmapIndex = bmidx; @@ -225,10 +240,17 @@ public final class DfsPackFile extends BlockBasedFile { PackIndex idx = idx(ctx); DfsStreamKey revKey = new DfsStreamKey.ForReverseIndex( desc.getStreamKey(INDEX)); + AtomicBoolean cacheHit = new AtomicBoolean(true); DfsBlockCache.Ref<PackReverseIndex> revref = cache.getOrLoadRef( revKey, REF_POSITION, - () -> loadReverseIdx(revKey, idx)); + () -> { + cacheHit.set(false); + return loadReverseIdx(ctx, revKey, idx); + }); + if (cacheHit.get()) { + ctx.stats.ridxCacheHit++; + } PackReverseIndex revidx = revref.get(); if (reverseIndex == null && revidx != null) { reverseIndex = revidx; @@ -1031,9 +1053,12 @@ public final class DfsPackFile extends BlockBasedFile { } private DfsBlockCache.Ref<PackReverseIndex> loadReverseIdx( - DfsStreamKey revKey, PackIndex idx) { + DfsReader ctx, DfsStreamKey revKey, PackIndex idx) { + ctx.stats.readReverseIdx++; + long start = System.nanoTime(); PackReverseIndex revidx = new PackReverseIndex(idx); reverseIndex = revidx; + ctx.stats.readReverseIdxMicros += elapsedMicros(start); return new DfsBlockCache.Ref<>( revKey, REF_POSITION, @@ -1064,8 +1089,8 @@ public final class DfsPackFile extends BlockBasedFile { bmidx = PackBitmapIndex.read(in, idx, revidx); } finally { size = rc.position(); - ctx.stats.readIdxBytes += size; - ctx.stats.readIdxMicros += elapsedMicros(start); + ctx.stats.readBitmapIdxBytes += size; + ctx.stats.readBitmapIdxMicros += elapsedMicros(start); } bitmapIndex = bmidx; return new DfsBlockCache.Ref<>( 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 index b7a71969b8..5c47425013 100644 --- 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 @@ -19,18 +19,39 @@ public class DfsReaderIoStats { /** Number of times the reader explicitly called scanPacks. */ long scanPacks; + /** Total number of cache hits for pack indexes. */ + long idxCacheHit; + + /** Total number of cache hits for reverse indexes. */ + long ridxCacheHit; + + /** Total number of cache hits for bitmap indexes. */ + long bitmapCacheHit; + /** 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. */ + /** Total number of reverse indexes added into memory. */ + long readReverseIdx; + + /** Total number of bytes read from pack indexes. */ long readIdxBytes; - /** Total microseconds spent reading pack or bitmap indexes. */ + /** Total microseconds spent reading pack indexes. */ long readIdxMicros; + /** Total microseconds spent creating reverse indexes. */ + long readReverseIdxMicros; + + /** Total number of bytes read from bitmap indexes. */ + long readBitmapIdxBytes; + + /** Total microseconds spent reading bitmap indexes. */ + long readBitmapIdxMicros; + /** Total number of block cache hits. */ long blockCacheHit; @@ -75,6 +96,33 @@ public class DfsReaderIoStats { } /** + * Get total number of pack index cache hits. + * + * @return total number of pack index cache hits. + */ + public long getPackIndexCacheHits() { + return stats.idxCacheHit; + } + + /** + * Get total number of reverse index cache hits. + * + * @return total number of reverse index cache hits. + */ + public long getReverseIndexCacheHits() { + return stats.ridxCacheHit; + } + + /** + * Get total number of bitmap index cache hits. + * + * @return total number of bitmap index cache hits. + */ + public long getBitmapIndexCacheHits() { + return stats.bitmapCacheHit; + } + + /** * Get total number of complete pack indexes read into memory. * * @return total number of complete pack indexes read into memory. @@ -84,6 +132,15 @@ public class DfsReaderIoStats { } /** + * Get total number of times the reverse index was computed. + * + * @return total number of reverse index was computed. + */ + public long getReadReverseIndexCount() { + return stats.readReverseIdx; + } + + /** * Get total number of complete bitmap indexes read into memory. * * @return total number of complete bitmap indexes read into memory. @@ -93,24 +150,51 @@ public class DfsReaderIoStats { } /** - * Get total number of bytes read from indexes. + * Get total number of bytes read from pack indexes. * - * @return total number of bytes read from indexes. + * @return total number of bytes read from pack indexes. */ public long getReadIndexBytes() { return stats.readIdxBytes; } /** - * Get total microseconds spent reading pack or bitmap indexes. + * Get total microseconds spent reading pack indexes. * - * @return total microseconds spent reading pack or bitmap indexes. + * @return total microseconds spent reading pack indexes. */ public long getReadIndexMicros() { return stats.readIdxMicros; } /** + * Get total microseconds spent creating reverse indexes. + * + * @return total microseconds spent creating reverse indexes. + */ + public long getReadReverseIndexMicros() { + return stats.readReverseIdxMicros; + } + + /** + * Get total number of bytes read from bitmap indexes. + * + * @return total number of bytes read from bitmap indexes. + */ + public long getReadBitmapIndexBytes() { + return stats.readBitmapIdxBytes; + } + + /** + * Get total microseconds spent reading bitmap indexes. + * + * @return total microseconds spent reading bitmap indexes. + */ + public long getReadBitmapIndexMicros() { + return stats.readBitmapIdxMicros; + } + + /** * Get total number of block cache hits. * * @return total number of block cache hits. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitConfig.java new file mode 100644 index 0000000000..e4e7cd6e03 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitConfig.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2020 Julian Ruppel <julian.ruppel@sap.com> + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.eclipse.jgit.lib; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.StandardCharsets; +import java.nio.charset.UnsupportedCharsetException; +import java.text.MessageFormat; +import org.eclipse.jgit.annotations.Nullable; +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.Config.SectionParser; +import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.IO; +import org.eclipse.jgit.util.RawParseUtils; + +/** + * The standard "commit" configuration parameters. + * + * @since 5.13 + */ +public class CommitConfig { + /** + * Key for {@link Config#get(SectionParser)}. + */ + public static final Config.SectionParser<CommitConfig> KEY = CommitConfig::new; + + private final static Charset DEFAULT_COMMIT_MESSAGE_ENCODING = StandardCharsets.UTF_8; + + private String i18nCommitEncoding; + + private String commitTemplatePath; + + private CommitConfig(Config rc) { + commitTemplatePath = rc.getString(ConfigConstants.CONFIG_COMMIT_SECTION, + null, ConfigConstants.CONFIG_KEY_COMMIT_TEMPLATE); + i18nCommitEncoding = rc.getString(ConfigConstants.CONFIG_SECTION_I18N, + null, ConfigConstants.CONFIG_KEY_COMMIT_ENCODING); + } + + /** + * Get the path to the commit template as defined in the git + * {@code commit.template} property. + * + * @return the path to commit template or {@code null} if not present. + */ + @Nullable + public String getCommitTemplatePath() { + return commitTemplatePath; + } + + /** + * Get the encoding of the commit as defined in the git + * {@code i18n.commitEncoding} property. + * + * @return the encoding or {@code null} if not present. + */ + @Nullable + public String getCommitEncoding() { + return i18nCommitEncoding; + } + + /** + * Get the content to the commit template as defined in + * {@code commit.template}. If no {@code i18n.commitEncoding} is specified, + * UTF-8 fallback is used. + * + * @return content of the commit template or {@code null} if not present. + * @throws IOException + * if the template file can not be read + * @throws FileNotFoundException + * if the template file does not exists + * @throws ConfigInvalidException + * if a {@code commitEncoding} is specified and is invalid + */ + @Nullable + public String getCommitTemplateContent() + throws FileNotFoundException, IOException, ConfigInvalidException { + + if (commitTemplatePath == null) { + return null; + } + + File commitTemplateFile; + if (commitTemplatePath.startsWith("~/")) { //$NON-NLS-1$ + commitTemplateFile = FS.DETECTED.resolve(FS.DETECTED.userHome(), + commitTemplatePath.substring(2)); + } else { + commitTemplateFile = FS.DETECTED.resolve(null, commitTemplatePath); + } + + Charset commitMessageEncoding = getEncoding(); + return RawParseUtils.decode(commitMessageEncoding, + IO.readFully(commitTemplateFile)); + + } + + private Charset getEncoding() throws ConfigInvalidException { + Charset commitMessageEncoding = DEFAULT_COMMIT_MESSAGE_ENCODING; + + if (i18nCommitEncoding == null) { + return null; + } + + try { + commitMessageEncoding = Charset.forName(i18nCommitEncoding); + } catch (IllegalCharsetNameException | UnsupportedCharsetException e) { + throw new ConfigInvalidException(MessageFormat.format( + JGitText.get().invalidEncoding, i18nCommitEncoding), e); + } + + return commitMessageEncoding; + } +}
\ No newline at end of file diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java index b6f4798dce..24eebc6a19 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java @@ -125,6 +125,13 @@ public final class ConfigConstants { public static final String CONFIG_COMMIT_SECTION = "commit"; /** + * The "template" key + * + * @since 5.13 + */ + public static final String CONFIG_KEY_COMMIT_TEMPLATE = "template"; + + /** * The "tag" section * * @since 5.11 @@ -518,6 +525,13 @@ public final class ConfigConstants { public static final String CONFIG_SECTION_I18N = "i18n"; /** + * The "commitEncoding" key + * + * @since 5.13 + */ + public static final String CONFIG_KEY_COMMIT_ENCODING = "commitEncoding"; + + /** * The "logOutputEncoding" key * * @since 5.2 diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java index 5d5ba12baa..d5b3643af9 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java @@ -527,10 +527,12 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { private List<Ref> getMergedInto(RevCommit needle, Collection<Ref> haystacks, Enum returnStrategy, ProgressMonitor monitor) throws IOException { List<Ref> result = new ArrayList<>(); + List<RevCommit> uninteresting = new ArrayList<>(); RevFilter oldRF = filter; TreeFilter oldTF = treeFilter; try { finishDelayedFreeFlags(); + reset(~freeFlags & APP_FLAGS); filter = RevFilter.ALL; treeFilter = TreeFilter.ALL; for (Ref r: haystacks) { @@ -559,13 +561,14 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable { } if(!commitFound){ markUninteresting(c); + uninteresting.add(c); if (returnStrategy == GetMergedIntoStrategy.RETURN_ON_FIRST_NOT_FOUND) { return result; } } } } finally { - reset(~freeFlags & APP_FLAGS); + roots.addAll(uninteresting); filter = oldRF; treeFilter = oldTF; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java index c5e52bef98..aaa9308ac3 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java @@ -255,6 +255,13 @@ public final class GitProtocolConstants { public static final String COMMAND_FETCH = "fetch"; //$NON-NLS-1$ /** + * The server supports the object-info capability. + * + * @since 5.13 + */ + public static final String COMMAND_OBJECT_INFO = "object-info"; //$NON-NLS-1$ + + /** * HTTP header to set by clients to request a specific git protocol version * in the HTTP transport. * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ObjectInfoRequest.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ObjectInfoRequest.java new file mode 100644 index 0000000000..86a2716675 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ObjectInfoRequest.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2021, Google LLC. and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +package org.eclipse.jgit.transport; + +import java.util.Collections; +import java.util.List; + +import org.eclipse.jgit.lib.ObjectId; + +/** + * object-info request. + * + * <p> + * This is the parsed request for an object-info call, used as input to + * {@link ProtocolV2Hook}. + * + * @see <a href= + * "https://www.kernel.org/pub/software/scm/git/docs/technical/protocol-v2.html#_object_info">object-info + * documentation</a> + * + * @since 5.13 + */ +public final class ObjectInfoRequest { + private final List<ObjectId> objectIDs; + + private ObjectInfoRequest(List<ObjectId> objectIDs) { + this.objectIDs = objectIDs; + } + + /** @return object IDs that the client requested. */ + public List<ObjectId> getObjectIDs() { + return this.objectIDs; + } + + /** @return A builder of {@link ObjectInfoRequest}. */ + public static Builder builder() { + return new Builder(); + } + + /** A builder for {@link ObjectInfoRequest}. */ + public static final class Builder { + private List<ObjectId> objectIDs = Collections.emptyList(); + + private Builder() { + } + + /** + * @param value + * @return the Builder + */ + public Builder setObjectIDs(List<ObjectId> value) { + objectIDs = value; + return this; + } + + /** @return ObjectInfoRequest */ + public ObjectInfoRequest build() { + return new ObjectInfoRequest( + Collections.unmodifiableList(objectIDs)); + } + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Hook.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Hook.java index bfa7490665..d7626df3fa 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Hook.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Hook.java @@ -55,4 +55,16 @@ public interface ProtocolV2Hook { throws ServiceMayNotContinueException { // Do nothing by default } + + /** + * @param req + * the object-info request + * @throws ServiceMayNotContinueException + * abort; the message will be sent to the user + * @since 5.13 + */ + default void onObjectInfo(ObjectInfoRequest req) + throws ServiceMayNotContinueException { + // Do nothing by default + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2HookChain.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2HookChain.java index 4cf8db6f5e..7b3a4cea06 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2HookChain.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2HookChain.java @@ -71,6 +71,14 @@ public class ProtocolV2HookChain implements ProtocolV2Hook { } } + @Override + public void onObjectInfo(ObjectInfoRequest req) + throws ServiceMayNotContinueException { + for (ProtocolV2Hook hook : hooks) { + hook.onObjectInfo(req); + } + } + private ProtocolV2HookChain(List<? extends ProtocolV2Hook> hooks) { this.hooks = Collections.unmodifiableList(hooks); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java index 92f0133f5a..6cec4b9a3f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java @@ -28,6 +28,7 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; +import org.eclipse.jgit.errors.InvalidObjectIdException; import org.eclipse.jgit.errors.PackProtocolException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.ObjectId; @@ -248,4 +249,38 @@ final class ProtocolV2Parser { return builder.setRefPrefixes(prefixes).build(); } + ObjectInfoRequest parseObjectInfoRequest(PacketLineIn pckIn) + throws PackProtocolException, IOException { + ObjectInfoRequest.Builder builder = ObjectInfoRequest.builder(); + List<ObjectId> objectIDs = new ArrayList<>(); + + String line = pckIn.readString(); + + if (PacketLineIn.isEnd(line)) { + return builder.build(); + } + + if (!line.equals("size")) { //$NON-NLS-1$ + throw new PackProtocolException(MessageFormat + .format(JGitText.get().unexpectedPacketLine, line)); + } + + for (String line2 : pckIn.readStrings()) { + if (!line2.startsWith("oid ")) { //$NON-NLS-1$ + throw new PackProtocolException(MessageFormat + .format(JGitText.get().unexpectedPacketLine, line2)); + } + + String oidStr = line2.substring("oid ".length()); //$NON-NLS-1$ + + try { + objectIDs.add(ObjectId.fromString(oidStr)); + } catch (InvalidObjectIdException e) { + throw new PackProtocolException(MessageFormat + .format(JGitText.get().invalidObject, oidStr), e); + } + } + + return builder.setObjectIDs(objectIDs).build(); + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java index 79f60c3202..58f8895e00 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java @@ -29,6 +29,7 @@ import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.UncheckedIOException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; @@ -37,6 +38,8 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.errors.InvalidObjectIdException; @@ -441,7 +444,7 @@ public class ReceivePack { */ public void setAdvertisedRefs(Map<String, Ref> allRefs, Set<ObjectId> additionalHaves) { - refs = allRefs != null ? allRefs : db.getAllRefs(); + refs = allRefs != null ? allRefs : getAllRefs(); refs = refFilter.filter(refs); advertisedHaves.clear(); @@ -1296,6 +1299,21 @@ public class ReceivePack { } /** + * Extract the full list of refs from the ref-db. + * + * @return Map of all refname/ref + */ + private Map<String, Ref> getAllRefs() { + try { + return db.getRefDatabase().getRefs().stream() + .collect(Collectors.toMap(Ref::getName, + Function.identity())); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + /** * Receive a list of commands from the input. * * @throws java.io.IOException diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java index da97f1e580..02be434887 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java @@ -123,6 +123,7 @@ public class TransferConfig { private final boolean advertiseSidebandAll; private final boolean advertiseWaitForDone; + private final boolean advertiseObjectInfo; final @Nullable ProtocolVersion protocolVersion; final String[] hideRefs; @@ -211,6 +212,8 @@ public class TransferConfig { "advertisesidebandall", false); advertiseWaitForDone = rc.getBoolean("uploadpack", "advertisewaitfordone", false); + advertiseObjectInfo = rc.getBoolean("uploadpack", + "advertiseobjectinfo", false); } /** @@ -318,6 +321,14 @@ public class TransferConfig { } /** + * @return true to advertise object-info to all clients + * @since 5.13 + */ + public boolean isAdvertiseObjectInfo() { + return advertiseObjectInfo; + } + + /** * Get {@link org.eclipse.jgit.transport.RefFilter} respecting configured * hidden refs. * 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 37a1c1e589..07b348d0e7 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java @@ -17,6 +17,7 @@ import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REF_IN_ import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SERVER_OPTION; import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_FETCH; import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_LS_REFS; +import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_OBJECT_INFO; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_REACHABLE_SHA1_IN_WANT; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_TIP_SHA1_IN_WANT; @@ -1269,6 +1270,32 @@ public class UploadPack { } } + private void objectInfo(PacketLineOut pckOut) throws IOException { + ProtocolV2Parser parser = new ProtocolV2Parser(transferConfig); + ObjectInfoRequest req = parser.parseObjectInfoRequest(pckIn); + + protocolV2Hook.onObjectInfo(req); + + ObjectReader or = getRepository().newObjectReader(); + + // Size is the only attribute currently supported. + pckOut.writeString("size"); //$NON-NLS-1$ + + for (ObjectId oid : req.getObjectIDs()) { + long size; + try { + size = or.getObjectSize(oid, ObjectReader.OBJ_ANY); + } catch (MissingObjectException e) { + throw new PackProtocolException(MessageFormat + .format(JGitText.get().missingObject, oid.name()), e); + } + + pckOut.writeString(oid.getName() + " " + size); //$NON-NLS-1$ + } + + pckOut.end(); + } + /* * Returns true if this is the last command and we should tear down the * connection. @@ -1295,6 +1322,10 @@ public class UploadPack { fetchV2(pckOut); return false; } + if (command.equals("command=" + COMMAND_OBJECT_INFO)) { //$NON-NLS-1$ + objectInfo(pckOut); + return false; + } throw new PackProtocolException(MessageFormat .format(JGitText.get().unknownTransportCommand, command)); } 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 0946f645fb..31a05933c8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java @@ -524,7 +524,7 @@ public abstract class FS { } private static String read(Path p) throws IOException { - final byte[] body = IO.readFully(p.toFile()); + byte[] body = IO.readFully(p.toFile()); return new String(body, 0, body.length, UTF_8); } @@ -766,7 +766,7 @@ public abstract class FS { } private static String getConfigKey(FileStore s) { - final String storeKey; + String storeKey; if (SystemReader.getInstance().isWindows()) { Object attribute = null; try { @@ -1164,7 +1164,7 @@ public abstract class FS { * platform does not require path name translation. */ public File resolve(File dir, String name) { - final File abspn = new File(name); + File abspn = new File(name); if (abspn.isAbsolute()) return abspn; return new File(dir, name); @@ -1257,7 +1257,7 @@ public abstract class FS { } private File defaultUserHomeImpl() { - final String home = AccessController.doPrivileged( + String home = AccessController.doPrivileged( (PrivilegedAction<String>) () -> System.getProperty("user.home") //$NON-NLS-1$ ); if (home == null || home.length() == 0) @@ -1267,7 +1267,8 @@ public abstract class FS { /** * Searches the given path to see if it contains one of the given files. - * Returns the first it finds. Returns null if not found or if path is null. + * Returns the first it finds which is executable. Returns null if not found + * or if path is null. * * @param path * List of paths to search separated by File.pathSeparator @@ -1277,14 +1278,15 @@ public abstract class FS { * @since 3.0 */ protected static File searchPath(String path, String... lookFor) { - if (path == null) + if (path == null) { return null; + } for (String p : path.split(File.pathSeparator)) { for (String command : lookFor) { - final File file = new File(p, command); + File file = new File(p, command); try { - if (file.isFile()) { + if (file.isFile() && file.canExecute()) { return file.getAbsoluteFile(); } } catch (SecurityException e) { @@ -1339,7 +1341,7 @@ public abstract class FS { protected static String readPipe(File dir, String[] command, String encoding, Map<String, String> env) throws CommandFailedException { - final boolean debug = LOG.isDebugEnabled(); + boolean debug = LOG.isDebugEnabled(); try { if (debug) { LOG.debug("readpipe " + Arrays.asList(command) + "," //$NON-NLS-1$ //$NON-NLS-2$ @@ -1806,11 +1808,11 @@ public abstract class FS { * @since 5.0 */ public Entry[] list(File directory, FileModeStrategy fileModeStrategy) { - final File[] all = directory.listFiles(); + File[] all = directory.listFiles(); if (all == null) { return NO_ENTRIES; } - final Entry[] result = new Entry[all.length]; + Entry[] result = new Entry[all.length]; for (int i = 0; i < result.length; i++) { result[i] = new FileEntry(all[i], this, fileModeStrategy); } @@ -1840,10 +1842,9 @@ public abstract class FS { * @since 4.0 */ public ProcessResult runHookIfPresent(Repository repository, - final String hookName, - String[] args) throws JGitInternalException { - return runHookIfPresent(repository, hookName, args, System.out, System.err, - null); + String hookName, String[] args) throws JGitInternalException { + return runHookIfPresent(repository, hookName, args, System.out, + System.err, null); } /** @@ -1875,9 +1876,9 @@ public abstract class FS { * @since 5.11 */ public ProcessResult runHookIfPresent(Repository repository, - final String hookName, - String[] args, OutputStream outRedirect, OutputStream errRedirect, - String stdinArgs) throws JGitInternalException { + String hookName, String[] args, OutputStream outRedirect, + OutputStream errRedirect, String stdinArgs) + throws JGitInternalException { return new ProcessResult(Status.NOT_SUPPORTED); } @@ -1911,7 +1912,7 @@ public abstract class FS { * @since 5.11 */ protected ProcessResult internalRunHookIfPresent(Repository repository, - final String hookName, String[] args, OutputStream outRedirect, + String hookName, String[] args, OutputStream outRedirect, OutputStream errRedirect, String stdinArgs) throws JGitInternalException { File hookFile = findHook(repository, hookName); @@ -2107,7 +2108,7 @@ public abstract class FS { OutputStream outRedirect, OutputStream errRedirect, InputStream inRedirect) throws IOException, InterruptedException { - final ExecutorService executor = Executors.newFixedThreadPool(2); + ExecutorService executor = Executors.newFixedThreadPool(2); Process process = null; // We'll record the first I/O exception that occurs, but keep on trying // to dispose of our open streams and file handles |