diff options
Diffstat (limited to 'org.eclipse.jgit')
34 files changed, 933 insertions, 120 deletions
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters new file mode 100644 index 0000000000..035ed37c10 --- /dev/null +++ b/org.eclipse.jgit/.settings/.api_filters @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<component id="org.eclipse.jgit" version="2"> + <resource path="src/org/eclipse/jgit/lib/ConfigConstants.java" type="org.eclipse.jgit.lib.ConfigConstants"> + <filter id="1141899266"> + <message_arguments> + <message_argument value="5.9"/> + <message_argument value="5.10"/> + <message_argument value="CONFIG_PROTOCOL_SECTION"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/lib/TypedConfigGetter.java" type="org.eclipse.jgit.lib.TypedConfigGetter"> + <filter id="404000815"> + <message_arguments> + <message_argument value="org.eclipse.jgit.lib.TypedConfigGetter"/> + <message_argument value="getPath(Config, String, String, String, FS, File, Path)"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/storage/pack/PackStatistics.java" type="org.eclipse.jgit.storage.pack.PackStatistics$Accumulator"> + <filter id="336658481"> + <message_arguments> + <message_argument value="org.eclipse.jgit.storage.pack.PackStatistics.Accumulator"/> + <message_argument value="notAdvertisedWants"/> + </message_arguments> + </filter> + <filter id="336658481"> + <message_arguments> + <message_argument value="org.eclipse.jgit.storage.pack.PackStatistics.Accumulator"/> + <message_argument value="reachabilityCheckDuration"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/transport/HttpConfig.java" type="org.eclipse.jgit.transport.HttpConfig"> + <filter id="336658481"> + <message_arguments> + <message_argument value="org.eclipse.jgit.transport.HttpConfig"/> + <message_argument value="EXTRA_HEADER"/> + </message_arguments> + </filter> + <filter id="336658481"> + <message_arguments> + <message_argument value="org.eclipse.jgit.transport.HttpConfig"/> + <message_argument value="USER_AGENT"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/transport/TransferConfig.java" type="org.eclipse.jgit.transport.TransferConfig$ProtocolVersion"> + <filter id="1175453698"> + <message_arguments> + <message_argument value="5.9"/> + <message_argument value="5.10"/> + <message_argument value="org.eclipse.jgit.transport.TransferConfig.ProtocolVersion"/> + </message_arguments> + </filter> + </resource> +</component> diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF index 1348bde29e..886e004218 100644 --- a/org.eclipse.jgit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/MANIFEST.MF @@ -3,12 +3,12 @@ Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Automatic-Module-Name: org.eclipse.jgit Bundle-SymbolicName: org.eclipse.jgit -Bundle-Version: 5.9.1.qualifier +Bundle-Version: 5.10.1.qualifier Bundle-Localization: plugin Bundle-Vendor: %Bundle-Vendor Eclipse-ExtensibleAPI: true -Export-Package: org.eclipse.jgit.annotations;version="5.9.1", - org.eclipse.jgit.api;version="5.9.1"; +Export-Package: org.eclipse.jgit.annotations;version="5.10.1", + org.eclipse.jgit.api;version="5.10.1"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.notes, org.eclipse.jgit.dircache, @@ -23,18 +23,18 @@ Export-Package: org.eclipse.jgit.annotations;version="5.9.1", org.eclipse.jgit.revwalk.filter, org.eclipse.jgit.blame, org.eclipse.jgit.merge", - org.eclipse.jgit.api.errors;version="5.9.1"; + org.eclipse.jgit.api.errors;version="5.10.1"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.errors", - org.eclipse.jgit.attributes;version="5.9.1"; + org.eclipse.jgit.attributes;version="5.10.1"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.treewalk", - org.eclipse.jgit.blame;version="5.9.1"; + org.eclipse.jgit.blame;version="5.10.1"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.diff", - org.eclipse.jgit.diff;version="5.9.1"; + org.eclipse.jgit.diff;version="5.10.1"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.attributes, org.eclipse.jgit.revwalk, @@ -42,47 +42,47 @@ Export-Package: org.eclipse.jgit.annotations;version="5.9.1", org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.treewalk, org.eclipse.jgit.util", - org.eclipse.jgit.dircache;version="5.9.1"; + org.eclipse.jgit.dircache;version="5.10.1"; uses:="org.eclipse.jgit.events, org.eclipse.jgit.lib, org.eclipse.jgit.attributes, org.eclipse.jgit.treewalk, org.eclipse.jgit.util", - org.eclipse.jgit.errors;version="5.9.1"; + org.eclipse.jgit.errors;version="5.10.1"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.dircache, org.eclipse.jgit.lib, org.eclipse.jgit.internal.storage.pack", - org.eclipse.jgit.events;version="5.9.1"; + org.eclipse.jgit.events;version="5.10.1"; uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.fnmatch;version="5.9.1", - org.eclipse.jgit.gitrepo;version="5.9.1"; + org.eclipse.jgit.fnmatch;version="5.10.1", + org.eclipse.jgit.gitrepo;version="5.10.1"; uses:="org.xml.sax.helpers, org.eclipse.jgit.api, org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, org.xml.sax", - org.eclipse.jgit.gitrepo.internal;version="5.9.1";x-internal:=true, - org.eclipse.jgit.hooks;version="5.9.1";uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.ignore;version="5.9.1", - org.eclipse.jgit.ignore.internal;version="5.9.1"; + org.eclipse.jgit.gitrepo.internal;version="5.10.1";x-internal:=true, + org.eclipse.jgit.hooks;version="5.10.1";uses:="org.eclipse.jgit.lib", + org.eclipse.jgit.ignore;version="5.10.1", + org.eclipse.jgit.ignore.internal;version="5.10.1"; x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal;version="5.9.1"; + org.eclipse.jgit.internal;version="5.10.1"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.http.test", - org.eclipse.jgit.internal.fsck;version="5.9.1"; + org.eclipse.jgit.internal.fsck;version="5.10.1"; x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal.ketch;version="5.9.1"; + org.eclipse.jgit.internal.ketch;version="5.10.1"; x-friends:="org.eclipse.jgit.junit, org.eclipse.jgit.test, org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.revwalk;version="5.9.1";x-internal:=true, - org.eclipse.jgit.internal.storage.dfs;version="5.9.1"; + org.eclipse.jgit.internal.revwalk;version="5.10.1";x-internal:=true, + org.eclipse.jgit.internal.storage.dfs;version="5.10.1"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.http.server, org.eclipse.jgit.http.test, org.eclipse.jgit.lfs.test", - org.eclipse.jgit.internal.storage.file;version="5.9.1"; + org.eclipse.jgit.internal.storage.file;version="5.10.1"; x-friends:="org.eclipse.jgit.test, org.eclipse.jgit.junit, org.eclipse.jgit.junit.http, @@ -91,35 +91,35 @@ Export-Package: org.eclipse.jgit.annotations;version="5.9.1", org.eclipse.jgit.pgm, org.eclipse.jgit.pgm.test, org.eclipse.jgit.ssh.apache", - org.eclipse.jgit.internal.storage.io;version="5.9.1"; + org.eclipse.jgit.internal.storage.io;version="5.10.1"; x-friends:="org.eclipse.jgit.junit, org.eclipse.jgit.test, org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.pack;version="5.9.1"; + org.eclipse.jgit.internal.storage.pack;version="5.10.1"; x-friends:="org.eclipse.jgit.junit, org.eclipse.jgit.test, org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.reftable;version="5.9.1"; + org.eclipse.jgit.internal.storage.reftable;version="5.10.1"; x-friends:="org.eclipse.jgit.http.test, org.eclipse.jgit.junit, org.eclipse.jgit.test, org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.storage.reftree;version="5.9.1"; + org.eclipse.jgit.internal.storage.reftree;version="5.10.1"; x-friends:="org.eclipse.jgit.junit, org.eclipse.jgit.test, org.eclipse.jgit.pgm", - org.eclipse.jgit.internal.submodule;version="5.9.1";x-internal:=true, - org.eclipse.jgit.internal.transport.connectivity;version="5.9.1"; + org.eclipse.jgit.internal.submodule;version="5.10.1";x-internal:=true, + org.eclipse.jgit.internal.transport.connectivity;version="5.10.1"; x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal.transport.http;version="5.9.1"; + org.eclipse.jgit.internal.transport.http;version="5.10.1"; x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.internal.transport.parser;version="5.9.1"; + org.eclipse.jgit.internal.transport.parser;version="5.10.1"; x-friends:="org.eclipse.jgit.http.server, org.eclipse.jgit.test", - org.eclipse.jgit.internal.transport.ssh;version="5.9.1"; + org.eclipse.jgit.internal.transport.ssh;version="5.10.1"; x-friends:="org.eclipse.jgit.ssh.apache, org.eclipse.jgit.ssh.jsch", - org.eclipse.jgit.lib;version="5.9.1"; + org.eclipse.jgit.lib;version="5.10.1"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.util.sha1, org.eclipse.jgit.dircache, @@ -133,9 +133,10 @@ Export-Package: org.eclipse.jgit.annotations;version="5.9.1", org.eclipse.jgit.util, org.eclipse.jgit.submodule, org.eclipse.jgit.util.time", - org.eclipse.jgit.lib.internal;version="5.9.1"; + org.eclipse.jgit.lib.internal;version="5.10.1"; x-friends:="org.eclipse.jgit.test", - org.eclipse.jgit.merge;version="5.9.1"; + org.eclipse.jgit.logging;version="5.10.1", + org.eclipse.jgit.merge;version="5.10.1"; uses:="org.eclipse.jgit.dircache, org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, @@ -144,40 +145,40 @@ Export-Package: org.eclipse.jgit.annotations;version="5.9.1", org.eclipse.jgit.util, org.eclipse.jgit.api, org.eclipse.jgit.attributes", - org.eclipse.jgit.nls;version="5.9.1", - org.eclipse.jgit.notes;version="5.9.1"; + org.eclipse.jgit.nls;version="5.10.1", + org.eclipse.jgit.notes;version="5.10.1"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk, org.eclipse.jgit.merge", - org.eclipse.jgit.patch;version="5.9.1"; + org.eclipse.jgit.patch;version="5.10.1"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.diff", - org.eclipse.jgit.revplot;version="5.9.1"; + org.eclipse.jgit.revplot;version="5.10.1"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.revwalk", - org.eclipse.jgit.revwalk;version="5.9.1"; + org.eclipse.jgit.revwalk;version="5.10.1"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.diff, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.revwalk.filter, org.eclipse.jgit.treewalk", - org.eclipse.jgit.revwalk.filter;version="5.9.1"; + org.eclipse.jgit.revwalk.filter;version="5.10.1"; uses:="org.eclipse.jgit.revwalk, org.eclipse.jgit.lib, org.eclipse.jgit.util", - org.eclipse.jgit.storage.file;version="5.9.1"; + org.eclipse.jgit.storage.file;version="5.10.1"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.util", - org.eclipse.jgit.storage.pack;version="5.9.1"; + org.eclipse.jgit.storage.pack;version="5.10.1"; uses:="org.eclipse.jgit.lib", - org.eclipse.jgit.submodule;version="5.9.1"; + org.eclipse.jgit.submodule;version="5.10.1"; uses:="org.eclipse.jgit.lib, org.eclipse.jgit.diff, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.treewalk, org.eclipse.jgit.util", - org.eclipse.jgit.transport;version="5.9.1"; + org.eclipse.jgit.transport;version="5.10.1"; uses:="javax.crypto, org.eclipse.jgit.util.io, org.eclipse.jgit.lib, @@ -190,21 +191,21 @@ Export-Package: org.eclipse.jgit.annotations;version="5.9.1", org.eclipse.jgit.transport.resolver, org.eclipse.jgit.storage.pack, org.eclipse.jgit.errors", - org.eclipse.jgit.transport.http;version="5.9.1"; + org.eclipse.jgit.transport.http;version="5.10.1"; uses:="javax.net.ssl", - org.eclipse.jgit.transport.resolver;version="5.9.1"; + org.eclipse.jgit.transport.resolver;version="5.10.1"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.lib", - org.eclipse.jgit.treewalk;version="5.9.1"; + org.eclipse.jgit.treewalk;version="5.10.1"; uses:="org.eclipse.jgit.dircache, org.eclipse.jgit.lib, org.eclipse.jgit.attributes, org.eclipse.jgit.revwalk, org.eclipse.jgit.treewalk.filter, org.eclipse.jgit.util", - org.eclipse.jgit.treewalk.filter;version="5.9.1"; + org.eclipse.jgit.treewalk.filter;version="5.10.1"; uses:="org.eclipse.jgit.treewalk", - org.eclipse.jgit.util;version="5.9.1"; + org.eclipse.jgit.util;version="5.10.1"; uses:="org.eclipse.jgit.transport, org.eclipse.jgit.hooks, org.eclipse.jgit.revwalk, @@ -217,12 +218,12 @@ Export-Package: org.eclipse.jgit.annotations;version="5.9.1", org.eclipse.jgit.treewalk, javax.net.ssl, org.eclipse.jgit.util.time", - org.eclipse.jgit.util.io;version="5.9.1"; + org.eclipse.jgit.util.io;version="5.10.1"; uses:="org.eclipse.jgit.attributes, org.eclipse.jgit.lib, org.eclipse.jgit.treewalk", - org.eclipse.jgit.util.sha1;version="5.9.1", - org.eclipse.jgit.util.time;version="5.9.1" + org.eclipse.jgit.util.sha1;version="5.10.1", + org.eclipse.jgit.util.time;version="5.10.1" Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)", javax.crypto, diff --git a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF index c561b7ede1..d6f1f52579 100644 --- a/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/SOURCE-MANIFEST.MF @@ -3,5 +3,5 @@ Bundle-ManifestVersion: 2 Bundle-Name: org.eclipse.jgit - Sources Bundle-SymbolicName: org.eclipse.jgit.source Bundle-Vendor: Eclipse.org - JGit -Bundle-Version: 5.9.1.qualifier -Eclipse-SourceBundle: org.eclipse.jgit;version="5.9.1.qualifier";roots="." +Bundle-Version: 5.10.1.qualifier +Eclipse-SourceBundle: org.eclipse.jgit;version="5.10.1.qualifier";roots="." diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml index c899dfd293..4f17be524f 100644 --- a/org.eclipse.jgit/pom.xml +++ b/org.eclipse.jgit/pom.xml @@ -20,7 +20,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>5.9.1-SNAPSHOT</version> + <version>5.10.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 c5e7dc3efa..c52e307814 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -348,6 +348,9 @@ invalidFilter=Invalid filter: {0} invalidGitdirRef = Invalid .git reference in file ''{0}'' invalidGitModules=Invalid .gitmodules file invalidGitType=invalid git type: {0} +invalidHeaderFormat=Invalid header from git config http.extraHeader ignored: no colon or empty key in header ''{0}'' +invalidHeaderKey=Invalid header from git config http.extraHeader ignored: key contains illegal characters; see RFC 7230: ''{0}'' +invalidHeaderValue=Invalid header from git config http.extraHeader ignored: value should be 7bit-ASCII characters only: ''{0}'' invalidHexString=Invalid hex string: {0} invalidHomeDirectory=Invalid home directory: {0} invalidHooksPath=Invalid git config core.hooksPath = {0} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java index 0dc5d5e7f7..847ab0a9a8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> - * Copyright (C) 2011, Matthias Sohn <matthias.sohn@sap.com> and others + * Copyright (C) 2011, 2020 Matthias Sohn <matthias.sohn@sap.com> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -503,6 +503,11 @@ public class CheckoutCommand extends GitCommand<Ref> { editor.add(new PathEdit(path) { @Override public void apply(DirCacheEntry ent) { + if (ent.getStage() != DirCacheEntry.STAGE_0) { + // A checkout on a conflicting file stages the checked + // out file and resolves the conflict. + ent.setStage(DirCacheEntry.STAGE_0); + } ent.setObjectId(blobId); ent.setFileMode(mode); checkoutPath(ent, r, 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 30d7f9adc4..aba86fc361 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java @@ -413,6 +413,10 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> { return null; } + if (idHEAD != null && idHEAD.isSymbolic()) { + return idHEAD.getTarget(); + } + Ref master = result.getAdvertisedRef(Constants.R_HEADS + Constants.MASTER); ObjectId objectId = master != null ? master.getObjectId() : null; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java index 8d427385d4..6a9fbd4f63 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java @@ -9,6 +9,7 @@ */ package org.eclipse.jgit.api; +import static org.eclipse.jgit.lib.Constants.R_REFS; import static org.eclipse.jgit.lib.Constants.R_TAGS; import java.io.IOException; @@ -73,6 +74,11 @@ public class DescribeCommand extends GitCommand<String> { private List<FileNameMatcher> matchers = new ArrayList<>(); /** + * Whether to use all refs in the refs/ namespace + */ + private boolean useAll; + + /** * Whether to use all tags (incl. lightweight) or not. */ private boolean useTags; @@ -153,6 +159,22 @@ public class DescribeCommand extends GitCommand<String> { } /** + * Instead of using only the annotated tags, use any ref found in refs/ + * namespace. This option enables matching any known branch, + * remote-tracking branch, or lightweight tag. + * + * @param all + * <code>true</code> enables matching any ref found in refs/ + * like setting option --all in c git + * @return {@code this} + * @since 5.10 + */ + public DescribeCommand setAll(boolean all) { + this.useAll = all; + return this; + } + + /** * Instead of using only the annotated tags, use any tag found in refs/tags * namespace. This option enables matching lightweight (non-annotated) tags * or not. @@ -186,7 +208,7 @@ public class DescribeCommand extends GitCommand<String> { private String longDescription(Ref tag, int depth, ObjectId tip) throws IOException { return String.format( - "%s-%d-g%s", tag.getName().substring(R_TAGS.length()), //$NON-NLS-1$ + "%s-%d-g%s", formatRefName(tag.getName()), //$NON-NLS-1$ Integer.valueOf(depth), w.getObjectReader().abbreviate(tip) .name()); } @@ -244,8 +266,7 @@ public class DescribeCommand extends GitCommand<String> { for (FileNameMatcher matcher : matchers) { Stream<Ref> m = tags.stream().filter( tag -> { - matcher.append( - tag.getName().substring(R_TAGS.length())); + matcher.append(formatRefName(tag.getName())); boolean result = matcher.isMatch(); matcher.reset(); return result; @@ -283,7 +304,7 @@ public class DescribeCommand extends GitCommand<String> { } Collection<Ref> tagList = repo.getRefDatabase() - .getRefsByPrefix(R_TAGS); + .getRefsByPrefix(useAll ? R_REFS : R_TAGS); Map<ObjectId, List<Ref>> tags = tagList.stream() .filter(this::filterLightweightTags) .collect(Collectors.groupingBy(this::getObjectIdFromRef)); @@ -336,7 +357,7 @@ public class DescribeCommand extends GitCommand<String> { Optional<Ref> bestMatch = getBestMatch(tags.get(target)); if (bestMatch.isPresent()) { return longDesc ? longDescription(bestMatch.get(), 0, target) : - bestMatch.get().getName().substring(R_TAGS.length()); + formatRefName(bestMatch.get().getName()); } w.markStart(target); @@ -408,6 +429,16 @@ public class DescribeCommand extends GitCommand<String> { } /** + * Removes the refs/ or refs/tags prefix from tag names + * @param name the name of the tag + * @return the tag name with its prefix removed + */ + private String formatRefName(String name) { + return name.startsWith(R_TAGS) ? name.substring(R_TAGS.length()) : + name.substring(R_REFS.length()); + } + + /** * Whether we use lightweight tags or not for describe Candidates * * @param ref @@ -419,7 +450,7 @@ public class DescribeCommand extends GitCommand<String> { private boolean filterLightweightTags(Ref ref) { ObjectId id = ref.getObjectId(); try { - return this.useTags || (id != null && (w.parseTag(id) != null)); + return this.useAll || this.useTags || (id != null && (w.parseTag(id) != null)); } catch (IOException e) { return false; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleDeinitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleDeinitCommand.java index ac17d3a642..f4b8ac2e07 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleDeinitCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleDeinitCommand.java @@ -20,8 +20,10 @@ import java.util.Collections; import java.util.List; import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.api.errors.InvalidConfigurationException; import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.api.errors.NoHeadException; +import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.ObjectId; @@ -119,6 +121,8 @@ public class SubmoduleDeinitCommand } } return results; + } catch (ConfigInvalidException e) { + throw new InvalidConfigurationException(e.getMessage(), e); } catch (IOException e) { throw new JGitInternalException(e.getMessage(), e); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java index 73d2807eaf..8c342e267d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEditor.java @@ -1,6 +1,6 @@ /* - * Copyright (C) 2008-2009, Google Inc. - * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others + * Copyright (C) 2008, 2009, Google Inc. + * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -139,10 +139,28 @@ public class DirCacheEditor extends BaseDirCacheEditor { : eIdx; fastAdd(ent); } else { - // Apply to all entries of the current path (different stages) lastIdx = cache.nextEntry(eIdx); - for (int i = eIdx; i < lastIdx; i++) { - final DirCacheEntry ent = cache.getEntry(i); + if (lastIdx > eIdx + 1) { + // Apply to all entries of the current path (different + // stages). If any apply() resets the stage to STAGE_0, take + // only that entry and omit all others. + DirCacheEntry[] tmp = new DirCacheEntry[lastIdx - eIdx]; + int n = 0; + for (int i = eIdx; i < lastIdx; i++) { + DirCacheEntry ent = cache.getEntry(i); + e.apply(ent); + if (ent.getStage() == DirCacheEntry.STAGE_0) { + fastAdd(ent); + n = 0; + break; + } + tmp[n++] = ent; + } + for (int i = 0; i < n; i++) { + fastAdd(tmp[i]); + } + } else { + DirCacheEntry ent = cache.getEntry(eIdx); e.apply(ent); fastAdd(ent); } @@ -257,7 +275,9 @@ public class DirCacheEditor extends BaseDirCacheEditor { * {@link #apply(DirCacheEntry)} method. The editor will invoke apply once * for each record in the index which matches the path name. If there are * multiple records (for example in stages 1, 2 and 3), the edit instance - * will be called multiple times, once for each stage. + * will be called multiple times, once for each stage. If any of these calls + * resets the stage to 0, only this entry will be taken and entries for + * other stages are discarded. */ public abstract static class PathEdit { final byte[] path; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java index dcb84825fe..67edf50f44 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java @@ -542,6 +542,24 @@ public class DirCacheEntry { } /** + * Sets the stage of an entry. + * + * @param stage + * to set, in the range [0..3] + * @throws IllegalArgumentException + * if the stage is outside the range [0..3] + * @since 5.10 + */ + public void setStage(int stage) { + if ((stage & ~0x3) != 0) { + throw new IllegalArgumentException( + "Invalid stage, must be in range [0..3]"); //$NON-NLS-1$ + } + byte flags = info[infoOffset + P_FLAGS]; + info[infoOffset + P_FLAGS] = (byte) ((flags & 0xCF) | (stage << 4)); + } + + /** * Returns whether this entry should be skipped from the working tree. * * @return true if this entry should be skipepd. 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 814811d131..d12a243a8e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -376,6 +376,9 @@ public class JGitText extends TranslationBundle { /***/ public String invalidGitdirRef; /***/ public String invalidGitModules; /***/ public String invalidGitType; + /***/ public String invalidHeaderFormat; + /***/ public String invalidHeaderKey; + /***/ public String invalidHeaderValue; /***/ public String invalidHexString; /***/ public String invalidHomeDirectory; /***/ public String invalidHooksPath; 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 c18a1895c3..1f2fe1057f 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 @@ -985,7 +985,8 @@ public class GC { if (base == null || !n.startsWith(base)) { try { Path delete = packDir.resolve(n); - Files.delete(delete); + FileUtils.delete(delete.toFile(), + FileUtils.RETRY | FileUtils.SKIP_MISSING); LOG.warn(JGitText.get().deletedOrphanInPackDir, delete); } catch (IOException e) { LOG.error(e.getMessage(), e); @@ -1173,6 +1174,7 @@ public class GC { // create temporary files String id = pw.computeName().getName(); File packdir = repo.getObjectDatabase().getPackDirectory(); + packdir.mkdirs(); tmpPack = File.createTempFile("gc_", ".pack_tmp", packdir); //$NON-NLS-1$ //$NON-NLS-2$ final String tmpBase = tmpPack.getName() .substring(0, tmpPack.getName().lastIndexOf('.')); 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 265b71dd2a..d32182864a 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 @@ -580,7 +580,7 @@ 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); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java index a51593b509..a369026c97 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java @@ -20,6 +20,9 @@ package org.eclipse.jgit.lib; import static java.nio.charset.StandardCharsets.UTF_8; +import java.io.File; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; @@ -29,6 +32,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.events.ConfigChangedEvent; import org.eclipse.jgit.events.ConfigChangedListener; @@ -36,6 +40,7 @@ import org.eclipse.jgit.events.ListenerHandle; import org.eclipse.jgit.events.ListenerList; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.transport.RefSpec; +import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.RawParseUtils; /** @@ -475,6 +480,37 @@ public class Config { } /** + * Parse a string value and treat it as a file path, replacing a ~/ prefix + * by the user's home directory. + * <p> + * <b>Note:</b> this may throw {@link InvalidPathException} if the string is + * not a valid path. + * </p> + * + * @param section + * section the key is in. + * @param subsection + * subsection the key is in, or null if not in a subsection. + * @param name + * the key name. + * @param fs + * to use to convert the string into a path. + * @param resolveAgainst + * directory to resolve the path against if it is a relative + * path; {@code null} to use the Java process's current + * directory. + * @param defaultValue + * to return if no value was present + * @return the {@link Path}, or {@code defaultValue} if not set + * @since 5.10 + */ + public Path getPath(String section, String subsection, String name, + @NonNull FS fs, File resolveAgainst, Path defaultValue) { + return typedGetter.getPath(this, section, subsection, name, fs, + resolveAgainst, defaultValue); + } + + /** * Parse a list of {@link org.eclipse.jgit.transport.RefSpec}s from the * configuration. * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java index dd4be345e7..0f2f6cff8a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TypedConfigGetter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017, Thomas Wolf <thomas.wolf@paranor.ch> and others + * Copyright (C) 2017, 2020 Thomas Wolf <thomas.wolf@paranor.ch> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -10,22 +10,26 @@ package org.eclipse.jgit.lib; +import java.io.File; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; import java.util.List; import java.util.concurrent.TimeUnit; import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.transport.RefSpec; +import org.eclipse.jgit.util.FS; /** - * Something that knows how to convert plain strings from a git - * {@link org.eclipse.jgit.lib.Config} to typed values. + * Something that knows how to convert plain strings from a git {@link Config} + * to typed values. * * @since 4.9 */ public interface TypedConfigGetter { /** - * Get a boolean value from a git {@link org.eclipse.jgit.lib.Config}. + * Get a boolean value from a git {@link Config}. * * @param config * to get the value from @@ -44,7 +48,7 @@ public interface TypedConfigGetter { String name, boolean defaultValue); /** - * Parse an enumeration from a git {@link org.eclipse.jgit.lib.Config}. + * Parse an enumeration from a git {@link Config}. * * @param config * to get the value from @@ -65,7 +69,7 @@ public interface TypedConfigGetter { String subsection, String name, T defaultValue); /** - * Obtain an integer value from a git {@link org.eclipse.jgit.lib.Config}. + * Obtain an integer value from a git {@link Config}. * * @param config * to get the value from @@ -83,7 +87,7 @@ public interface TypedConfigGetter { int defaultValue); /** - * Obtain a long value from a git {@link org.eclipse.jgit.lib.Config}. + * Obtain a long value from a git {@link Config}. * * @param config * to get the value from @@ -102,7 +106,7 @@ public interface TypedConfigGetter { /** * Parse a numerical time unit, such as "1 minute", from a git - * {@link org.eclipse.jgit.lib.Config}. + * {@link Config}. * * @param config * to get the value from @@ -124,10 +128,50 @@ public interface TypedConfigGetter { long getTimeUnit(Config config, String section, String subsection, String name, long defaultValue, TimeUnit wantUnit); + /** + * Parse a string value from a git {@link Config} and treat it as a file + * path, replacing a ~/ prefix by the user's home directory. + * <p> + * <b>Note:</b> this may throw {@link InvalidPathException} if the string is + * not a valid path. + * </p> + * + * @param config + * to get the path from. + * @param section + * section the key is in. + * @param subsection + * subsection the key is in, or null if not in a subsection. + * @param name + * the key name. + * @param fs + * to use to convert the string into a path. + * @param resolveAgainst + * directory to resolve the path against if it is a relative + * path. + * @param defaultValue + * to return if no value was present + * @return the {@link Path}, or {@code defaultValue} if not set + * @since 5.10 + */ + default Path getPath(Config config, String section, String subsection, + String name, @NonNull FS fs, File resolveAgainst, + Path defaultValue) { + String value = config.getString(section, subsection, name); + if (value == null) { + return defaultValue; + } + File file; + if (value.startsWith("~/")) { //$NON-NLS-1$ + file = fs.resolve(fs.userHome(), value.substring(2)); + } else { + file = fs.resolve(resolveAgainst, value); + } + return file.toPath(); + } /** - * Parse a list of {@link org.eclipse.jgit.transport.RefSpec}s from a git - * {@link org.eclipse.jgit.lib.Config}. + * Parse a list of {@link RefSpec}s from a git {@link Config}. * * @param config * to get the list from diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/logging/PerformanceLogContext.java b/org.eclipse.jgit/src/org/eclipse/jgit/logging/PerformanceLogContext.java new file mode 100644 index 0000000000..fab0dd102a --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/logging/PerformanceLogContext.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2020, Google LLC and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.eclipse.jgit.logging; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Singleton that collects performance logs. + * + * @since 5.10 + */ +public class PerformanceLogContext { + /** Singleton instance that stores the statistics. */ + private static final PerformanceLogContext INSTANCE = new PerformanceLogContext(); + + /** List that stores events as performance logs. */ + private static final ThreadLocal<List<PerformanceLogRecord>> eventRecords = ThreadLocal + .withInitial(ArrayList::new); + + private PerformanceLogContext() { + } + + /** + * Get the instance of the context. + * + * @return instance of performance log context. + */ + public static PerformanceLogContext getInstance() { + return INSTANCE; + } + + /** + * Get the unmodifiable list of events as performance records. + * + * @return unmodifiable list of events as performance logs. + */ + public List<PerformanceLogRecord> getEventRecords() { + return Collections.unmodifiableList(eventRecords.get()); + } + + /** + * Adds a performance log record to the current list of events. + * + * @param record + * performance log record that is going to be added. + */ + public void addEvent(PerformanceLogRecord record) { + eventRecords.get().add(record); + } + + /** + * Removes all of the existing records from the current list of events. + */ + public void cleanEvents() { + eventRecords.remove(); + } +}
\ No newline at end of file diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/logging/PerformanceLogRecord.java b/org.eclipse.jgit/src/org/eclipse/jgit/logging/PerformanceLogRecord.java new file mode 100644 index 0000000000..3dc5f20156 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/logging/PerformanceLogRecord.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020, Google LLC and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.eclipse.jgit.logging; + +/** + * Class to register a performance log record. + * + * @since 5.10 + */ +public class PerformanceLogRecord { + /** Name of the recorded event. */ + private String name; + + /** Duration of the recorded event in milliseconds. */ + private long durationMs; + + /** + * Create a new performance log record for an event. + * + * @param name + * name of the event. + * @param durationMs + * duration in milliseconds of the event. + */ + public PerformanceLogRecord(String name, long durationMs) { + this.name = name; + this.durationMs = durationMs; + } + + /** + * Get the name of the recorded event. + * + * @return name of the recorded event. + */ + public String getName() { + return name; + } + + /** + * Get the duration in milliseconds of the recorded event. + * + * @return duration in milliseconds of the recorded event. + */ + public long getDurationMs() { + return durationMs; + } +}
\ No newline at end of file diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java index 645da0a068..4d18337fae 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java @@ -188,6 +188,13 @@ public class PackStatistics { public long haves; /** + * The count of wants that were not advertised by the server. + * + * @since 5.10 + */ + public long notAdvertisedWants; + + /** * Time in ms spent in the negotiation phase. For non-bidirectional * transports (e.g., HTTP), this is only for the final request that * sends back the pack file. @@ -266,8 +273,16 @@ public class PackStatistics { /** Time in ms spent writing the pack. */ public long timeWriting; + /** Time in ms spent checking reachability. + * + * @since 5.10 + */ + public long reachabilityCheckDuration; + /** Number of trees traversed in the walk when writing the pack. - * @since 5.4*/ + * + * @since 5.4 + */ public long treesTraversed; /** @@ -349,6 +364,16 @@ public class PackStatistics { } /** + * Get the count of client wants that were not advertised by the server. + * + * @return count of client wants that were not advertised by the server. + * @since 5.10 + */ + public long getNotAdvertisedWants() { + return statistics.notAdvertisedWants; + } + + /** * Time in ms spent in the negotiation phase. For non-bidirectional * transports (e.g., HTTP), this is only for the final request that sends * back the pack file. @@ -604,6 +629,18 @@ public class PackStatistics { } /** + * Get time in milliseconds spent checking if the client has access to the + * commits they are requesting. + * + * @return time in milliseconds spent checking if the client has access to the + * commits they are requesting. + * @since 5.10 + */ + public long getReachabilityCheckDuration() { + return statistics.reachabilityCheckDuration; + } + + /** * @return number of trees traversed in the walk when writing the pack. * @since 5.4 */ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java index 0ba5eb542d..bf77021b0c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java @@ -657,8 +657,11 @@ public class SubmoduleWalk implements AutoCloseable { * * @since 4.10 * @return name + * @throws ConfigInvalidException + * @throws IOException */ - public String getModuleName() { + public String getModuleName() throws IOException, ConfigInvalidException { + lazyLoadModulesConfig(); return getModuleName(path); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java index 1417faee80..3a36398629 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java @@ -21,8 +21,11 @@ import java.io.InputStream; import java.io.OutputStream; import java.text.MessageFormat; import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.Map; import java.util.Set; import org.eclipse.jgit.errors.InvalidObjectIdException; @@ -35,6 +38,7 @@ import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectIdRef; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.SymbolicRef; import org.eclipse.jgit.util.io.InterruptTimer; import org.eclipse.jgit.util.io.TimeoutInputStream; import org.eclipse.jgit.util.io.TimeoutOutputStream; @@ -49,6 +53,8 @@ import org.eclipse.jgit.util.io.TimeoutOutputStream; */ abstract class BasePackConnection extends BaseConnection { + protected static final String CAPABILITY_SYMREF_PREFIX = "symref="; //$NON-NLS-1$ + /** The repository this transport fetches into, or pushes out of. */ protected final Repository local; @@ -228,10 +234,109 @@ abstract class BasePackConnection extends BaseConnection { throw duplicateAdvertisement(name); } } + updateWithSymRefs(avail, extractSymRefsFromCapabilities(remoteCapablities)); available(avail); } /** + * Finds values in the given capabilities of the form: + * + * <pre> + * symref=<em>source</em>:<em>target</em> + * </pre> + * + * And returns a Map of source->target entries. + * + * @param capabilities + * the capabilities lines + * @return a Map of the symref entries from capabilities + * @throws NullPointerException + * if capabilities, or any entry in it, is null + */ + static Map<String, String> extractSymRefsFromCapabilities(Collection<String> capabilities) { + final Map<String, String> symRefs = new LinkedHashMap<>(); + for (String option : capabilities) { + if (option.startsWith(CAPABILITY_SYMREF_PREFIX)) { + String[] symRef = option + .substring(CAPABILITY_SYMREF_PREFIX.length()) + .split(":", 2); //$NON-NLS-1$ + if (symRef.length == 2) { + symRefs.put(symRef[0], symRef[1]); + } + } + } + return symRefs; + } + + /** + * Updates the given refMap with {@link SymbolicRef}s defined by the given + * symRefs. + * <p> + * For each entry, symRef, in symRefs, whose value is a key in refMap, adds + * a new entry to refMap with that same key and value of a new + * {@link SymbolicRef} with source=symRef.key and + * target=refMap.get(symRef.value), then removes that entry from symRefs. + * <p> + * If refMap already contains an entry for symRef.key, it is replaced. + * </p> + * </p> + * <p> + * For example, given: + * </p> + * + * <pre> + * refMap.put("refs/heads/main", ref); + * symRefs.put("HEAD", "refs/heads/main"); + * </pre> + * + * then: + * + * <pre> + * updateWithSymRefs(refMap, symRefs); + * </pre> + * + * has the <em>effect</em> of: + * + * <pre> + * refMap.put("HEAD", + * new SymbolicRef("HEAD", refMap.get(symRefs.remove("HEAD")))) + * </pre> + * <p> + * Any entry in symRefs whose value is not a key in refMap is ignored. Any + * circular symRefs are ignored. + * </p> + * <p> + * Upon completion, symRefs will contain only any unresolvable entries. + * </p> + * + * @param refMap + * a non-null, modifiable, Map to update, and the provider of + * symref targets. + * @param symRefs + * a non-null, modifiable, Map of symrefs. + * @throws NullPointerException + * if refMap or symRefs is null + */ + static void updateWithSymRefs(Map<String, Ref> refMap, Map<String, String> symRefs) { + boolean haveNewRefMapEntries = !refMap.isEmpty(); + while (!symRefs.isEmpty() && haveNewRefMapEntries) { + haveNewRefMapEntries = false; + final Iterator<Map.Entry<String, String>> iterator = symRefs.entrySet().iterator(); + while (iterator.hasNext()) { + final Map.Entry<String, String> symRef = iterator.next(); + if (!symRefs.containsKey(symRef.getValue())) { // defer forward reference + final Ref r = refMap.get(symRef.getValue()); + if (r != null) { + refMap.put(symRef.getKey(), new SymbolicRef(symRef.getKey(), r)); + haveNewRefMapEntries = true; + iterator.remove(); + } + } + } + } + } + + /** * Create an exception to indicate problems finding a remote repository. The * caller is expected to throw the returned exception. * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java index 79cba80e11..dc82f46197 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java @@ -14,9 +14,13 @@ package org.eclipse.jgit.transport; import java.io.IOException; import java.net.URISyntaxException; import java.text.MessageFormat; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Set; import java.util.function.Supplier; +import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.Config; @@ -56,6 +60,20 @@ public class HttpConfig { public static final String SSL_VERIFY_KEY = "sslVerify"; //$NON-NLS-1$ /** + * git config key for the "userAgent" setting. + * + * @since 5.10 + */ + public static final String USER_AGENT = "userAgent"; //$NON-NLS-1$ + + /** + * git config key for the "extraHeader" setting. + * + * @since 5.10 + */ + public static final String EXTRA_HEADER = "extraHeader"; //$NON-NLS-1$ + + /** * git config key for the "cookieFile" setting. * * @since 5.4 @@ -103,6 +121,8 @@ public class HttpConfig { } }).get().intValue(); + private static final String ENV_HTTP_USER_AGENT = "GIT_HTTP_USER_AGENT"; //$NON-NLS-1$ + /** * Config values for http.followRedirect. */ @@ -143,6 +163,10 @@ public class HttpConfig { private int maxRedirects; + private String userAgent; + + private List<String> extraHeaders; + private String cookieFile; private boolean saveCookies; @@ -186,6 +210,27 @@ public class HttpConfig { } /** + * Get the "http.userAgent" setting + * + * @return the value of the "http.userAgent" setting + * @since 5.10 + */ + public String getUserAgent() { + return userAgent; + } + + /** + * Get the "http.extraHeader" setting + * + * @return the value of the "http.extraHeader" setting + * @since 5.10 + */ + @NonNull + public List<String> getExtraHeaders() { + return extraHeaders == null ? Collections.emptyList() : extraHeaders; + } + + /** * Get the "http.cookieFile" setting * * @return the value of the "http.cookieFile" setting @@ -265,11 +310,25 @@ public class HttpConfig { if (redirectLimit < 0) { redirectLimit = MAX_REDIRECTS; } + String agent = config.getString(HTTP, null, USER_AGENT); + if (agent != null) { + agent = UserAgent.clean(agent); + } + userAgent = agent; + String[] headers = config.getStringList(HTTP, null, EXTRA_HEADER); + // https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpextraHeader + // "an empty value will reset the extra headers to the empty list." + int start = findLastEmpty(headers) + 1; + if (start > 0) { + headers = Arrays.copyOfRange(headers, start, headers.length); + } + extraHeaders = Arrays.asList(headers); cookieFile = config.getString(HTTP, null, COOKIE_FILE_KEY); saveCookies = config.getBoolean(HTTP, SAVE_COOKIES_KEY, false); cookieFileCacheLimit = config.getInt(HTTP, COOKIE_FILE_CACHE_LIMIT_KEY, DEFAULT_COOKIE_FILE_CACHE_LIMIT); String match = findMatch(config.getSubsections(HTTP), uri); + if (match != null) { // Override with more specific items postBufferSize = config.getInt(HTTP, match, POST_BUFFER_KEY, @@ -283,6 +342,22 @@ public class HttpConfig { if (newMaxRedirects >= 0) { redirectLimit = newMaxRedirects; } + String uriSpecificUserAgent = config.getString(HTTP, match, + USER_AGENT); + if (uriSpecificUserAgent != null) { + userAgent = UserAgent.clean(uriSpecificUserAgent); + } + String[] uriSpecificExtraHeaders = config.getStringList(HTTP, match, + EXTRA_HEADER); + if (uriSpecificExtraHeaders.length > 0) { + start = findLastEmpty(uriSpecificExtraHeaders) + 1; + if (start > 0) { + uriSpecificExtraHeaders = Arrays.copyOfRange( + uriSpecificExtraHeaders, start, + uriSpecificExtraHeaders.length); + } + extraHeaders = Arrays.asList(uriSpecificExtraHeaders); + } String urlSpecificCookieFile = config.getString(HTTP, match, COOKIE_FILE_KEY); if (urlSpecificCookieFile != null) { @@ -291,12 +366,26 @@ public class HttpConfig { saveCookies = config.getBoolean(HTTP, match, SAVE_COOKIES_KEY, saveCookies); } + // Environment overrides config + agent = SystemReader.getInstance().getenv(ENV_HTTP_USER_AGENT); + if (!StringUtils.isEmptyOrNull(agent)) { + userAgent = UserAgent.clean(agent); + } postBuffer = postBufferSize; sslVerify = sslVerifyFlag; followRedirects = followRedirectsMode; maxRedirects = redirectLimit; } + private int findLastEmpty(String[] values) { + for (int i = values.length - 1; i >= 0; i--) { + if (values[i] == null) { + return i; + } + } + return -1; + } + /** * Determines the best match from a set of subsection names (representing * prefix URLs) for the given {@link URIish}. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java index 0801b8a86a..715cbb48fb 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackParser.java @@ -679,7 +679,8 @@ public abstract class PackParser { verifySafeObject(tempObjectId, type, visit.data); if (isCheckObjectCollisions() && readCurs.has(tempObjectId)) { - checkObjectCollision(tempObjectId, type, visit.data); + checkObjectCollision(tempObjectId, type, visit.data, + visit.delta.sizeBeforeInflating); } PackedObjectInfo oe; @@ -999,6 +1000,7 @@ public abstract class PackParser { UnresolvedDelta n = onEndDelta(); n.position = streamPosition; n.next = baseByPos.put(base, n); + n.sizeBeforeInflating = streamPosition() - streamPosition; deltaCount++; break; } @@ -1020,6 +1022,7 @@ public abstract class PackParser { inflateAndSkip(Source.INPUT, sz); UnresolvedDelta n = onEndDelta(); n.position = streamPosition; + n.sizeBeforeInflating = streamPosition() - streamPosition; r.add(n); deltaCount++; break; @@ -1071,9 +1074,11 @@ public abstract class PackParser { verifySafeObject(tempObjectId, type, data); } + long sizeBeforeInflating = streamPosition() - pos; PackedObjectInfo obj = newInfo(tempObjectId, null, null); obj.setOffset(pos); obj.setType(type); + obj.setSize(sizeBeforeInflating); onEndWholeObject(obj); if (data != null) onInflatedObjectData(obj, type, data); @@ -1148,6 +1153,8 @@ public abstract class PackParser { sz -= n; } } + stats.incrementObjectsDuplicated(); + stats.incrementNumBytesDuplicated(obj.getSize()); } catch (MissingObjectException notLocal) { // This is OK, we don't have a copy of the object locally // but the API throws when we try to read it as usually it's @@ -1155,15 +1162,17 @@ public abstract class PackParser { } } - private void checkObjectCollision(AnyObjectId obj, int type, byte[] data) - throws IOException { + private void checkObjectCollision(AnyObjectId obj, int type, byte[] data, + long sizeBeforeInflating) throws IOException { try { final ObjectLoader ldr = readCurs.open(obj, type); final byte[] existingData = ldr.getCachedBytes(data.length); if (!Arrays.equals(data, existingData)) { - throw new IOException(MessageFormat.format( - JGitText.get().collisionOn, obj.name())); + throw new IOException(MessageFormat + .format(JGitText.get().collisionOn, obj.name())); } + stats.incrementObjectsDuplicated(); + stats.incrementNumBytesDuplicated(sizeBeforeInflating); } catch (MissingObjectException notLocal) { // This is OK, we don't have a copy of the object locally // but the API throws when we try to read it as usually its @@ -1653,6 +1662,8 @@ public abstract class PackParser { UnresolvedDelta next; + long sizeBeforeInflating; + /** @return offset within the input stream. */ public long getOffset() { return position; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackedObjectInfo.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackedObjectInfo.java index fc906de2a8..fe1209b6af 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackedObjectInfo.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PackedObjectInfo.java @@ -29,6 +29,8 @@ public class PackedObjectInfo extends ObjectIdOwnerMap.Entry { private int type = Constants.OBJ_BAD; + private long sizeBeforeInflating; + PackedObjectInfo(final long headerOffset, final int packedCRC, final AnyObjectId id) { super(id); @@ -108,4 +110,12 @@ public class PackedObjectInfo extends ObjectIdOwnerMap.Entry { public void setType(int type) { this.type = type; } + + void setSize(long sizeBeforeInflating) { + this.sizeBeforeInflating = sizeBeforeInflating; + } + + long getSize() { + return sizeBeforeInflating; + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivedPackStatistics.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivedPackStatistics.java index bd8f5585d2..d7bc40006b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivedPackStatistics.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivedPackStatistics.java @@ -19,6 +19,7 @@ import org.eclipse.jgit.lib.Constants; */ public class ReceivedPackStatistics { private long numBytesRead; + private long numBytesDuplicated; private long numWholeCommit; private long numWholeTree; @@ -26,6 +27,7 @@ public class ReceivedPackStatistics { private long numWholeTag; private long numOfsDelta; private long numRefDelta; + private long numObjectsDuplicated; private long numDeltaCommit; private long numDeltaTree; @@ -42,6 +44,17 @@ public class ReceivedPackStatistics { } /** + * Get number of bytes of objects already in the local database + * + * @return number of bytes of objects appeared in both the pack sent by the + * client and the local database + * @since 5.10 + */ + public long getNumBytesDuplicated() { + return numBytesDuplicated; + } + + /** * Get number of whole commit objects in the pack * * @return number of whole commit objects in the pack @@ -96,6 +109,17 @@ public class ReceivedPackStatistics { } /** + * Get number of objects already in the local database + * + * @return number of objects appeared in both the pack sent by the client + * and the local database + * @since 5.10 + */ + public long getNumObjectsDuplicated() { + return numObjectsDuplicated; + } + + /** * Get number of delta commit objects in the pack * * @return number of delta commit objects in the pack @@ -134,6 +158,7 @@ public class ReceivedPackStatistics { /** A builder for {@link ReceivedPackStatistics}. */ public static class Builder { private long numBytesRead; + private long numBytesDuplicated; private long numWholeCommit; private long numWholeTree; @@ -141,6 +166,7 @@ public class ReceivedPackStatistics { private long numWholeTag; private long numOfsDelta; private long numRefDelta; + private long numObjectsDuplicated; private long numDeltaCommit; private long numDeltaTree; @@ -157,6 +183,17 @@ public class ReceivedPackStatistics { } /** + * @param size + * additional bytes already in the local database + * @return this + * @since 5.10 + */ + Builder incrementNumBytesDuplicated(long size) { + numBytesDuplicated += size; + return this; + } + + /** * Increment a whole object count. * * @param type OBJ_COMMIT, OBJ_TREE, OBJ_BLOB, or OBJ_TAG @@ -196,6 +233,17 @@ public class ReceivedPackStatistics { } /** + * Increment the duplicated object count. + * + * @return this + * @since 5.10 + */ + Builder incrementObjectsDuplicated() { + numObjectsDuplicated++; + return this; + } + + /** * Increment a delta object count. * * @param type OBJ_COMMIT, OBJ_TREE, OBJ_BLOB, or OBJ_TAG @@ -226,6 +274,7 @@ public class ReceivedPackStatistics { ReceivedPackStatistics build() { ReceivedPackStatistics s = new ReceivedPackStatistics(); s.numBytesRead = numBytesRead; + s.numBytesDuplicated = numBytesDuplicated; s.numWholeCommit = numWholeCommit; s.numWholeTree = numWholeTree; s.numWholeBlob = numWholeBlob; @@ -236,6 +285,7 @@ public class ReceivedPackStatistics { s.numDeltaTree = numDeltaTree; s.numDeltaBlob = numDeltaBlob; s.numDeltaTag = numDeltaTag; + s.numObjectsDuplicated = numObjectsDuplicated; return s; } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java index b1fac2cffb..fff2938e5d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Thomas Wolf <thomas.wolf@paranor.ch> and others + * Copyright (C) 2018, 2020 Thomas Wolf <thomas.wolf@paranor.ch> and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at @@ -117,6 +117,34 @@ public final class SshConstants { /** Key in an ssh config file. */ public static final String PROXY_COMMAND = "ProxyCommand"; + /** + * Comma-separated list of jump hosts, defining a jump host chain <em>in + * reverse order</em>. Each jump host is a SSH URI or "[user@]host[:port]". + * <p> + * Reverse order means: to connect A->B->target, one can do in + * {@code ~/.ssh/config} either of: + * </p> + * + * <pre> + * Host target + * ProxyJump B,A + * </pre> + * <p> + * <em>or</em> + * </p> + * + * <pre> + * Host target + * ProxyJump B + * + * Host B + * ProxyJump A + * </pre> + * + * @since 5.10 + */ + public static final String PROXY_JUMP = "ProxyJump"; + /** Key in an ssh config file. */ public static final String REMOTE_COMMAND = "RemoteCommand"; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java index 16169f028b..6768387e65 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java @@ -49,6 +49,7 @@ import java.net.SocketException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; @@ -900,7 +901,9 @@ public class TransportHttp extends HttpTransport implements WalkTransport, conn.setRequestProperty(HDR_ACCEPT_ENCODING, ENCODING_GZIP); } conn.setRequestProperty(HDR_PRAGMA, "no-cache"); //$NON-NLS-1$ - if (UserAgent.get() != null) { + if (http.getUserAgent() != null) { + conn.setRequestProperty(HDR_USER_AGENT, http.getUserAgent()); + } else if (UserAgent.get() != null) { conn.setRequestProperty(HDR_USER_AGENT, UserAgent.get()); } int timeOut = getTimeout(); @@ -909,6 +912,7 @@ public class TransportHttp extends HttpTransport implements WalkTransport, conn.setConnectTimeout(effTimeOut); conn.setReadTimeout(effTimeOut); } + addHeaders(conn, http.getExtraHeaders()); // set cookie header if necessary if (!relevantCookies.isEmpty()) { setCookieHeader(conn); @@ -923,6 +927,44 @@ public class TransportHttp extends HttpTransport implements WalkTransport, return conn; } + /** + * Adds a list of header strings to the connection. Headers are expected to + * separate keys from values, i.e. "Key: Value". Headers without colon or + * key are ignored (and logged), as are headers with keys that are not RFC + * 7230 tokens or with non-ASCII values. + * + * @param conn + * The target HttpConnection + * @param headersToAdd + * A list of header strings + */ + static void addHeaders(HttpConnection conn, List<String> headersToAdd) { + for (String header : headersToAdd) { + // Empty values are allowed according to + // https://tools.ietf.org/html/rfc7230 + int colon = header.indexOf(':'); + String key = null; + if (colon > 0) { + key = header.substring(0, colon).trim(); + } + if (key == null || key.isEmpty()) { + LOG.warn(MessageFormat.format( + JGitText.get().invalidHeaderFormat, header)); + } else if (HttpSupport.scanToken(key, 0) != key.length()) { + LOG.warn(MessageFormat.format(JGitText.get().invalidHeaderKey, + header)); + } else { + String value = header.substring(colon + 1).trim(); + if (!StandardCharsets.US_ASCII.newEncoder().canEncode(value)) { + LOG.warn(MessageFormat + .format(JGitText.get().invalidHeaderValue, header)); + } else { + conn.setRequestProperty(key, value); + } + } + } + } + private void setCookieHeader(HttpConnection conn) { StringBuilder cookieHeaderValue = new StringBuilder(); for (HttpCookie cookie : relevantCookies) { 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 9889015261..1242ef1b4a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java @@ -42,6 +42,8 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.UncheckedIOException; import java.text.MessageFormat; +import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -1008,7 +1010,7 @@ public class UploadPack { else advertised = refIdSet(getAdvertisedOrDefaultRefs().values()); - long negotiateStart = System.currentTimeMillis(); + Instant negotiateStart = Instant.now(); accumulator.advertised = advertised.size(); ProtocolV0Parser parser = new ProtocolV0Parser(transferConfig); @@ -1050,8 +1052,8 @@ public class UploadPack { if (!req.getClientShallowCommits().isEmpty()) walk.assumeShallow(req.getClientShallowCommits()); sendPack = negotiate(req, accumulator, pckOut); - accumulator.timeNegotiating += System.currentTimeMillis() - - negotiateStart; + accumulator.timeNegotiating = Duration + .between(negotiateStart, Instant.now()).toMillis(); if (sendPack && !biDirectionalPipe) { // Ensure the request was fully consumed. Any remaining input must @@ -1137,6 +1139,9 @@ public class UploadPack { advertised = refIdSet(getAdvertisedOrDefaultRefs().values()); } + PackStatistics.Accumulator accumulator = new PackStatistics.Accumulator(); + Instant negotiateStart = Instant.now(); + ProtocolV2Parser parser = new ProtocolV2Parser(transferConfig); FetchV2Request req = parser.parseFetchRequest(pckIn); currentRequest = req; @@ -1186,7 +1191,8 @@ public class UploadPack { if (req.wasDoneReceived()) { processHaveLines(req.getPeerHas(), ObjectId.zeroId(), - new PacketLineOut(NullOutputStream.INSTANCE)); + new PacketLineOut(NullOutputStream.INSTANCE), + accumulator); } else { pckOut.writeString("acknowledgments\n"); //$NON-NLS-1$ for (ObjectId id : req.getPeerHas()) { @@ -1195,7 +1201,8 @@ public class UploadPack { } } processHaveLines(req.getPeerHas(), ObjectId.zeroId(), - new PacketLineOut(NullOutputStream.INSTANCE)); + new PacketLineOut(NullOutputStream.INSTANCE), + accumulator); if (okToGiveUp()) { pckOut.writeString("ready\n"); //$NON-NLS-1$ } else if (commonBase.isEmpty()) { @@ -1238,7 +1245,11 @@ public class UploadPack { // But sideband-all is not used, so we have to write it ourselves. pckOut.writeString("packfile\n"); //$NON-NLS-1$ } - sendPack(new PackStatistics.Accumulator(), + + accumulator.timeNegotiating = Duration + .between(negotiateStart, Instant.now()).toMillis(); + + sendPack(accumulator, req, req.getClientCapabilities().contains(OPTION_INCLUDE_TAG) ? db.getRefDatabase().getRefsByPrefix(R_TAGS) @@ -1641,7 +1652,7 @@ public class UploadPack { } if (PacketLineIn.isEnd(line)) { - last = processHaveLines(peerHas, last, pckOut); + last = processHaveLines(peerHas, last, pckOut, accumulator); if (commonBase.isEmpty() || multiAck != MultiAck.OFF) pckOut.writeString("NAK\n"); //$NON-NLS-1$ if (noDone && sentReady) { @@ -1656,7 +1667,7 @@ public class UploadPack { peerHas.add(ObjectId.fromString(line.substring(5))); accumulator.haves++; } else if (line.equals("done")) { //$NON-NLS-1$ - last = processHaveLines(peerHas, last, pckOut); + last = processHaveLines(peerHas, last, pckOut, accumulator); if (commonBase.isEmpty()) pckOut.writeString("NAK\n"); //$NON-NLS-1$ @@ -1672,11 +1683,12 @@ public class UploadPack { } } - private ObjectId processHaveLines(List<ObjectId> peerHas, ObjectId last, PacketLineOut out) + private ObjectId processHaveLines(List<ObjectId> peerHas, ObjectId last, + PacketLineOut out, PackStatistics.Accumulator accumulator) throws IOException { preUploadHook.onBeginNegotiateRound(this, wantIds, peerHas.size()); if (wantAll.isEmpty() && !wantIds.isEmpty()) - parseWants(); + parseWants(accumulator); if (peerHas.isEmpty()) return last; @@ -1773,7 +1785,7 @@ public class UploadPack { return last; } - private void parseWants() throws IOException { + private void parseWants(PackStatistics.Accumulator accumulator) throws IOException { List<ObjectId> notAdvertisedWants = null; for (ObjectId obj : wantIds) { if (!advertised.contains(obj)) { @@ -1782,9 +1794,18 @@ public class UploadPack { notAdvertisedWants.add(obj); } } - if (notAdvertisedWants != null) + if (notAdvertisedWants != null) { + accumulator.notAdvertisedWants = notAdvertisedWants.size(); + + Instant startReachabilityChecking = Instant.now(); + requestValidator.checkWants(this, notAdvertisedWants); + accumulator.reachabilityCheckDuration = Duration + .between(startReachabilityChecking, Instant.now()) + .toMillis(); + } + AsyncRevObjectQueue q = walk.parseAny(wantIds, true); try { RevObject obj; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UserAgent.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UserAgent.java index 5b63635e5a..604eb3a66c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UserAgent.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UserAgent.java @@ -46,7 +46,7 @@ public class UserAgent { return "unknown"; //$NON-NLS-1$ } - private static String clean(String s) { + static String clean(String s) { s = s.trim(); StringBuilder b = new StringBuilder(s.length()); for (int i = 0; i < s.length(); i++) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java index 4c26dd0c40..72278dc9c3 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java @@ -25,6 +25,7 @@ import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.CharsetEncoder; +import java.nio.file.Path; import java.text.MessageFormat; import java.time.Instant; import java.util.Arrays; @@ -48,8 +49,8 @@ import org.eclipse.jgit.errors.NoWorkTreeException; import org.eclipse.jgit.ignore.FastIgnoreRule; import org.eclipse.jgit.ignore.IgnoreNode; import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; -import org.eclipse.jgit.lib.CoreConfig; import org.eclipse.jgit.lib.CoreConfig.CheckStat; import org.eclipse.jgit.lib.CoreConfig.EolStreamType; import org.eclipse.jgit.lib.CoreConfig.SymLinks; @@ -1308,15 +1309,11 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { } FS fs = repository.getFS(); - String path = repository.getConfig().get(CoreConfig.KEY) - .getExcludesFile(); + Path path = repository.getConfig().getPath( + ConfigConstants.CONFIG_CORE_SECTION, null, + ConfigConstants.CONFIG_KEY_EXCLUDESFILE, fs, null, null); if (path != null) { - File excludesfile; - if (path.startsWith("~/")) //$NON-NLS-1$ - excludesfile = fs.resolve(fs.userHome(), path.substring(2)); - else - excludesfile = fs.resolve(null, path); - loadRulesFromFile(r, excludesfile); + loadRulesFromFile(r, path.toFile()); } File exclude = fs.resolve(repository.getDirectory(), diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/IndexDiffFilter.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/IndexDiffFilter.java index 19cda42485..4731f345bc 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/IndexDiffFilter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/IndexDiffFilter.java @@ -217,14 +217,15 @@ public class IndexDiffFilter extends TreeFilter { */ private void copyUntrackedFolders(String currentPath) { String pathToBeSaved = null; - while (!untrackedParentFolders.isEmpty() - && !currentPath.startsWith(untrackedParentFolders.getFirst() - + "/")) //$NON-NLS-1$ + while (!untrackedParentFolders.isEmpty() && !currentPath + .startsWith(untrackedParentFolders.getFirst() + '/')) { pathToBeSaved = untrackedParentFolders.removeFirst(); + } if (pathToBeSaved != null) { - while (!untrackedFolders.isEmpty() - && untrackedFolders.getLast().startsWith(pathToBeSaved)) + while (!untrackedFolders.isEmpty() && untrackedFolders.getLast() + .startsWith(pathToBeSaved + '/')) { untrackedFolders.removeLast(); + } untrackedFolders.addLast(pathToBeSaved); } } 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 bf7b753693..d8cab358e7 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java @@ -1080,8 +1080,9 @@ public abstract class FS { } /** - * Set the last modified time of a file system object. If the OS/JRE support - * symbolic links, the link is modified, not the target, + * Set the last modified time of a file system object. + * <p> + * For symlinks it sets the modified time of the link target. * * @param f * a {@link java.io.File} object. @@ -1097,8 +1098,9 @@ public abstract class FS { } /** - * Set the last modified time of a file system object. If the OS/JRE support - * symbolic links, the link is modified, not the target, + * Set the last modified time of a file system object. + * <p> + * For symlinks it sets the modified time of the link target. * * @param p * a {@link Path} object. 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 c43956e53d..aa39a44642 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FileUtils.java @@ -710,6 +710,8 @@ public class FileUtils { } /** + * Set the last modified time of a file system object. + * * @param file * @param time * @throws IOException @@ -720,6 +722,8 @@ public class FileUtils { } /** + * Set the last modified time of a file system object. + * * @param path * @param time * @throws IOException diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java index 8ff649bccc..04b3eab504 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java @@ -424,6 +424,69 @@ public class HttpSupport { } } + /** + * Scan a RFC 7230 token as it appears in HTTP headers. + * + * @param header + * to scan in + * @param from + * index in {@code header} to start scanning at + * @return the index after the token, that is, on the first non-token + * character or {@code header.length} + * @throws IndexOutOfBoundsException + * if {@code from < 0} or {@code from > header.length()} + * + * @see <a href="https://tools.ietf.org/html/rfc7230#appendix-B">RFC 7230, + * Appendix B: Collected Grammar; "token" production</a> + * @since 5.10 + */ + public static int scanToken(String header, int from) { + int length = header.length(); + int i = from; + if (i < 0 || i > length) { + throw new IndexOutOfBoundsException(); + } + while (i < length) { + char c = header.charAt(i); + switch (c) { + case '!': + case '#': + case '$': + case '%': + case '&': + case '\'': + case '*': + case '+': + case '-': + case '.': + case '^': + case '_': + case '`': + case '|': + case '~': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + i++; + break; + default: + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + i++; + break; + } + return i; + } + } + return i; + } + private HttpSupport() { // Utility class only. } |