aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jgit')
-rw-r--r--org.eclipse.jgit/.settings/.api_filters48
-rw-r--r--org.eclipse.jgit/META-INF/MANIFEST.MF22
-rw-r--r--org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java44
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java45
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java11
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFile.java476
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFileCache.java106
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/AbbreviatedObjectId.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java42
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java9
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java24
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java11
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapCalculator.java45
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmappedReachabilityChecker.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianReachabilityChecker.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java17
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java42
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java21
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java16
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java75
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java117
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java73
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java250
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java11
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java25
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java33
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java15
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java21
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/LRUMap.java83
44 files changed, 1600 insertions, 151 deletions
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index 6719570e05..fc326d4462 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -22,6 +22,14 @@
</message_arguments>
</filter>
</resource>
+ <resource path="src/org/eclipse/jgit/lib/Repository.java" type="org.eclipse.jgit.lib.Repository">
+ <filter id="336695337">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.lib.Repository"/>
+ <message_argument value="getIdentifier()"/>
+ </message_arguments>
+ </filter>
+ </resource>
<resource path="src/org/eclipse/jgit/revwalk/ObjectWalk.java" type="org.eclipse.jgit.revwalk.ObjectWalk">
<filter comment="ignore the risk subclasses could define the same field and cause a name clash" id="336658481">
<message_arguments>
@@ -94,6 +102,26 @@
</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="COOKIE_FILE_CACHE_LIMIT_KEY"/>
+ </message_arguments>
+ </filter>
+ <filter id="336658481">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.transport.HttpConfig"/>
+ <message_argument value="COOKIE_FILE_KEY"/>
+ </message_arguments>
+ </filter>
+ <filter id="336658481">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.transport.HttpConfig"/>
+ <message_argument value="SAVE_COOKIES_KEY"/>
+ </message_arguments>
+ </filter>
+ </resource>
<resource path="src/org/eclipse/jgit/transport/Transport.java" type="org.eclipse.jgit.transport.Transport">
<filter comment="Marked as final since overriding a deprecated stub is likely a mistake" id="421654647">
<message_arguments>
@@ -132,4 +160,24 @@
</message_arguments>
</filter>
</resource>
+ <resource path="src/org/eclipse/jgit/util/HttpSupport.java" type="org.eclipse.jgit.util.HttpSupport">
+ <filter id="336658481">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.util.HttpSupport"/>
+ <message_argument value="HDR_COOKIE"/>
+ </message_arguments>
+ </filter>
+ <filter id="336658481">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.util.HttpSupport"/>
+ <message_argument value="HDR_SET_COOKIE"/>
+ </message_arguments>
+ </filter>
+ <filter id="336658481">
+ <message_arguments>
+ <message_argument value="org.eclipse.jgit.util.HttpSupport"/>
+ <message_argument value="HDR_SET_COOKIE2"/>
+ </message_arguments>
+ </filter>
+ </resource>
</component>
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index 95594f29e5..893f0d4305 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -86,6 +86,7 @@ Export-Package: org.eclipse.jgit.annotations;version="5.4.0",
org.eclipse.jgit.pgm",
org.eclipse.jgit.internal.storage.reftree;version="5.4.0";x-friends:="org.eclipse.jgit.junit,org.eclipse.jgit.test,org.eclipse.jgit.pgm",
org.eclipse.jgit.internal.submodule;version="5.4.0";x-internal:=true,
+ org.eclipse.jgit.internal.transport.http;version="5.4.0";x-friends:="org.eclipse.jgit.test",
org.eclipse.jgit.internal.transport.parser;version="5.4.0";x-friends:="org.eclipse.jgit.http.server,org.eclipse.jgit.test",
org.eclipse.jgit.internal.transport.ssh;version="5.4.0";x-friends:="org.eclipse.jgit.ssh.apache",
org.eclipse.jgit.lib;version="5.4.0";
@@ -160,16 +161,17 @@ Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
com.jcraft.jsch;version="[0.1.37,0.2.0)",
javax.crypto,
javax.net.ssl,
- org.bouncycastle;version="[1.60.0,2.0.0)",
- org.bouncycastle.bcpg;version="[1.60.0,2.0.0)",
- org.bouncycastle.gpg;version="[1.60.0,2.0.0)",
- org.bouncycastle.gpg.keybox;version="[1.60.0,2.0.0)",
- org.bouncycastle.jce.provider;version="[1.60.0,2.0.0)",
- org.bouncycastle.openpgp;version="[1.60.0,2.0.0)",
- org.bouncycastle.openpgp.jcajce;version="[1.60.0,2.0.0)",
- org.bouncycastle.openpgp.operator;version="[1.60.0,2.0.0)",
- org.bouncycastle.openpgp.operator.jcajce;version="[1.60.0,2.0.0)",
- org.bouncycastle.util.encoders;version="[1.60.0,2.0.0)",
+ org.bouncycastle;version="[1.61.0,2.0.0)",
+ org.bouncycastle.bcpg;version="[1.61.0,2.0.0)",
+ org.bouncycastle.gpg;version="[1.61.0,2.0.0)",
+ org.bouncycastle.gpg.keybox;version="[1.61.0,2.0.0)",
+ org.bouncycastle.gpg.keybox.jcajce;version="[1.61.0,2.0.0)",
+ org.bouncycastle.jce.provider;version="[1.61.0,2.0.0)",
+ org.bouncycastle.openpgp;version="[1.61.0,2.0.0)",
+ org.bouncycastle.openpgp.jcajce;version="[1.61.0,2.0.0)",
+ org.bouncycastle.openpgp.operator;version="[1.61.0,2.0.0)",
+ org.bouncycastle.openpgp.operator.jcajce;version="[1.61.0,2.0.0)",
+ org.bouncycastle.util.encoders;version="[1.61.0,2.0.0)",
org.slf4j;version="[1.7.0,2.0.0)",
org.xml.sax,
org.xml.sax.helpers
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 fc2a26f0d7..df42dc757b 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -139,6 +139,7 @@ configSubsectionContainsNewline=config subsection name contains newline
configSubsectionContainsNullByte=config subsection name contains byte 0x00
configValueContainsNullByte=config value contains byte 0x00
configHandleIsStale=config file handle is stale, {0}. retry
+configHandleMayBeLocked=config file handle may be locked by other process, {0}. retry
connectionFailed=connection failed
connectionTimeOut=Connection time out: {0}
contextMustBeNonNegative=context must be >= 0
@@ -208,6 +209,10 @@ couldNotDeleteTemporaryIndexFileShouldNotHappen=Could not delete temporary index
couldNotGetAdvertisedRef=Remote {0} did not advertise Ref for branch {1}. This Ref may not exist in the remote or may be hidden by permission settings.
couldNotGetRepoStatistics=Could not get repository statistics
couldNotLockHEAD=Could not lock HEAD
+couldNotFindTabInLine=Could not find tab in line {0}. Tab is the mandatory separator for the Netscape Cookie File Format.
+couldNotFindSixTabsInLine=Could not find 6 tabs but only {0} in line '{1}'. 7 tab separated columns per line are mandatory for the Netscape Cookie File Format.
+couldNotPersistCookies=Could not persist received cookies in file ''{0}''
+couldNotReadCookieFile=Could not read cookie file ''{0}''
couldNotReadIndexInOneGo=Could not read index in one go, only {0} out of {1} read
couldNotReadObjectWhileParsingCommit=Could not read an object while parsing commit {0}
couldNotRenameDeleteOldIndex=Could not rename delete old index
@@ -455,6 +460,7 @@ mismatchOffset=mismatch offset for object {0}
mismatchCRC=mismatch CRC for object {0}
missingAccesskey=Missing accesskey.
missingConfigurationForKey=No value for key {0} found in configuration
+missingCookieFile=Configured http.cookieFile ''{0}'' is missing
missingCRC=missing CRC for object {0}
missingDeltaBase=delta base
missingForwardImageInGITBinaryPatch=Missing forward-image in GIT binary patch
@@ -625,6 +631,7 @@ rewinding=Rewinding to commit {0}
s3ActionDeletion=Deletion
s3ActionReading=Reading
s3ActionWriting=Writing
+searchForReachableBranches=Finding reachable branches
searchForReuse=Finding sources
searchForSizes=Getting sizes
secondsAgo={0} seconds ago
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 9ebcf9fd0f..9ad77e65fd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
@@ -63,8 +63,7 @@ import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.InvalidPatternException;
import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.ignore.internal.IMatcher;
-import org.eclipse.jgit.ignore.internal.PathMatcher;
+import org.eclipse.jgit.fnmatch.FileNameMatcher;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
@@ -104,12 +103,17 @@ public class DescribeCommand extends GitCommand<String> {
/**
* Pattern matchers to be applied to tags under consideration.
*/
- private List<IMatcher> matchers = new ArrayList<>();
+ private List<FileNameMatcher> matchers = new ArrayList<>();
/**
* Whether to use all tags (incl. lightweight) or not.
*/
- private boolean useTags = false;
+ private boolean useTags;
+
+ /**
+ * Whether to show a uniquely abbreviated commit hash as a fallback or not.
+ */
+ private boolean always;
/**
* Constructor for DescribeCommand.
@@ -197,6 +201,21 @@ public class DescribeCommand extends GitCommand<String> {
return this;
}
+ /**
+ * Always describe the commit by eventually falling back to a uniquely
+ * abbreviated commit hash if no other name matches.
+ *
+ * @param always
+ * <code>true</code> enables falling back to a uniquely
+ * abbreviated commit hash
+ * @return {@code this}
+ * @since 5.4
+ */
+ public DescribeCommand setAlways(boolean always) {
+ this.always = always;
+ return this;
+ }
+
private String longDescription(Ref tag, int depth, ObjectId tip)
throws IOException {
return String.format(
@@ -222,7 +241,7 @@ public class DescribeCommand extends GitCommand<String> {
*/
public DescribeCommand setMatch(String... patterns) throws InvalidPatternException {
for (String p : patterns) {
- matchers.add(PathMatcher.createPathMatcher(p, null, false));
+ matchers.add(new FileNameMatcher(p, null));
}
return this;
}
@@ -255,9 +274,15 @@ public class DescribeCommand extends GitCommand<String> {
// Find the first tag that matches in the stream of all tags
// filtered by matchers ordered by tie break order
Stream<Ref> matchingTags = Stream.empty();
- for (IMatcher matcher : matchers) {
+ for (FileNameMatcher matcher : matchers) {
Stream<Ref> m = tags.stream().filter(
- tag -> matcher.matches(tag.getName(), false, false));
+ tag -> {
+ matcher.append(
+ tag.getName().substring(R_TAGS.length()));
+ boolean result = matcher.isMatch();
+ matcher.reset();
+ return result;
+ });
matchingTags = Stream.of(matchingTags, m).flatMap(i -> i);
}
return matchingTags.sorted(TAG_TIE_BREAKER).findFirst();
@@ -399,8 +424,9 @@ public class DescribeCommand extends GitCommand<String> {
}
// if all the nodes are dominated by all the tags, the walk stops
- if (candidates.isEmpty())
- return null;
+ if (candidates.isEmpty()) {
+ return always ? w.getObjectReader().abbreviate(target).name() : null;
+ }
Candidate best = Collections.min(candidates,
(Candidate o1, Candidate o2) -> o1.depth - o2.depth);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java
index 9b8016ce20..66de8ae131 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java
@@ -133,7 +133,7 @@ public class LogCommand extends GitCommand<Iterable<RevCommit>> {
@Override
public Iterable<RevCommit> call() throws GitAPIException, NoHeadException {
checkCallable();
- if (pathFilters.size() > 0)
+ if (!pathFilters.isEmpty())
walk.setTreeFilter(AndTreeFilter.create(
PathFilterGroup.create(pathFilters), TreeFilter.ANY_DIFF));
if (skip > -1 && maxCount > -1)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
index f0ad29db49..bdb2d1bbc5 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
@@ -60,6 +60,7 @@ import org.eclipse.jgit.api.errors.NoHeadException;
import org.eclipse.jgit.api.errors.RefNotAdvertisedException;
import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
+import org.eclipse.jgit.dircache.DirCacheCheckout;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.BranchConfig.BranchRebaseMode;
@@ -67,12 +68,17 @@ import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryState;
import org.eclipse.jgit.lib.SubmoduleConfig.FetchRecurseSubmodulesMode;
import org.eclipse.jgit.merge.MergeStrategy;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.FetchResult;
import org.eclipse.jgit.transport.TagOpt;
@@ -339,6 +345,45 @@ public class PullCommand extends TransportCommand<PullCommand, PullResult> {
PullResult result;
if (pullRebaseMode != BranchRebaseMode.NONE) {
+ try {
+ Ref head = repo.exactRef(Constants.HEAD);
+ if (head == null) {
+ throw new NoHeadException(JGitText
+ .get().commitOnRepoWithoutHEADCurrentlyNotSupported);
+ }
+ ObjectId headId = head.getObjectId();
+ if (headId == null) {
+ // Pull on an unborn branch: checkout
+ try (RevWalk revWalk = new RevWalk(repo)) {
+ RevCommit srcCommit = revWalk
+ .parseCommit(commitToMerge);
+ DirCacheCheckout dco = new DirCacheCheckout(repo,
+ repo.lockDirCache(), srcCommit.getTree());
+ dco.setFailOnConflict(true);
+ dco.setProgressMonitor(monitor);
+ dco.checkout();
+ RefUpdate refUpdate = repo
+ .updateRef(head.getTarget().getName());
+ refUpdate.setNewObjectId(commitToMerge);
+ refUpdate.setExpectedOldObjectId(null);
+ refUpdate.setRefLogMessage("initial pull", false); //$NON-NLS-1$
+ if (refUpdate.update() != Result.NEW) {
+ throw new NoHeadException(JGitText
+ .get().commitOnRepoWithoutHEADCurrentlyNotSupported);
+ }
+ monitor.endTask();
+ return new PullResult(fetchRes, remote,
+ RebaseResult.result(
+ RebaseResult.Status.FAST_FORWARD,
+ srcCommit));
+ }
+ }
+ } catch (NoHeadException e) {
+ throw e;
+ } catch (IOException e) {
+ throw new JGitInternalException(JGitText
+ .get().exceptionCaughtDuringExecutionOfPullCommand, e);
+ }
RebaseCommand rebase = new RebaseCommand(repo);
RebaseResult rebaseRes = rebase.setUpstream(commitToMerge)
.setUpstreamName(upstreamName).setProgressMonitor(monitor)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
index cdd1b80bb4..593874c121 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
@@ -490,7 +490,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
resetSoftToParent();
List<RebaseTodoLine> steps = repo.readRebaseTodo(
rebaseState.getPath(GIT_REBASE_TODO), false);
- RebaseTodoLine nextStep = steps.size() > 0 ? steps.get(0) : null;
+ RebaseTodoLine nextStep = steps.isEmpty() ? null : steps.get(0);
File messageFixupFile = rebaseState.getFile(MESSAGE_FIXUP);
File messageSquashFile = rebaseState.getFile(MESSAGE_SQUASH);
if (isSquash && messageFixupFile.exists())
@@ -1083,7 +1083,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
repo.writeRebaseTodoFile(rebaseState.getPath(GIT_REBASE_TODO),
todoLines, false);
- if (poppedLines.size() > 0) {
+ if (!poppedLines.isEmpty()) {
repo.writeRebaseTodoFile(rebaseState.getPath(DONE), poppedLines,
true);
}
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 ca0024d1c9..bdaef5a366 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -200,6 +200,7 @@ public class JGitText extends TranslationBundle {
/***/ public String configSubsectionContainsNullByte;
/***/ public String configValueContainsNullByte;
/***/ public String configHandleIsStale;
+ /***/ public String configHandleMayBeLocked;
/***/ public String connectionFailed;
/***/ public String connectionTimeOut;
/***/ public String contextMustBeNonNegative;
@@ -267,9 +268,13 @@ public class JGitText extends TranslationBundle {
/***/ public String couldNotCheckOutBecauseOfConflicts;
/***/ public String couldNotDeleteLockFileShouldNotHappen;
/***/ public String couldNotDeleteTemporaryIndexFileShouldNotHappen;
+ /***/ public String couldNotFindTabInLine;
+ /***/ public String couldNotFindSixTabsInLine;
/***/ public String couldNotGetAdvertisedRef;
/***/ public String couldNotGetRepoStatistics;
/***/ public String couldNotLockHEAD;
+ /***/ public String couldNotPersistCookies;
+ /***/ public String couldNotReadCookieFile;
/***/ public String couldNotReadIndexInOneGo;
/***/ public String couldNotReadObjectWhileParsingCommit;
/***/ public String couldNotRenameDeleteOldIndex;
@@ -516,6 +521,7 @@ public class JGitText extends TranslationBundle {
/***/ public String mismatchCRC;
/***/ public String missingAccesskey;
/***/ public String missingConfigurationForKey;
+ /***/ public String missingCookieFile;
/***/ public String missingCRC;
/***/ public String missingDeltaBase;
/***/ public String missingForwardImageInGITBinaryPatch;
@@ -686,6 +692,7 @@ public class JGitText extends TranslationBundle {
/***/ public String s3ActionDeletion;
/***/ public String s3ActionReading;
/***/ public String s3ActionWriting;
+ /***/ public String searchForReachableBranches;
/***/ public String searchForReuse;
/***/ public String searchForSizes;
/***/ public String secondsAgo;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java
index 5169e929e4..8e5c5a7f75 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsRepository.java
@@ -126,6 +126,12 @@ public abstract class DfsRepository extends Repository {
/** {@inheritDoc} */
@Override
+ public String getIdentifier() {
+ return getDescription().getRepositoryName();
+ }
+
+ /** {@inheritDoc} */
+ @Override
public void scanForRepoChanges() throws IOException {
getRefDatabase().refresh();
getObjectDatabase().clearCache();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
index d82d29e4cf..90772970ae 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
@@ -390,6 +390,17 @@ public class FileRepository extends Repository {
/** {@inheritDoc} */
@Override
+ public String getIdentifier() {
+ File directory = getDirectory();
+ if (directory != null) {
+ return directory.getPath();
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
public FileBasedConfig getConfig() {
if (systemConfig.isOutdated()) {
try {
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 00124bcf27..4540860a0d 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
@@ -244,7 +244,7 @@ public class GC {
* If the configuration parameter "gc.pruneexpire" couldn't be
* parsed
*/
- // TODO(ms): in 5.0 change signature and return Future<Collection<PackFile>>
+ // TODO(ms): change signature and return Future<Collection<PackFile>>
@SuppressWarnings("FutureReturnValueIgnored")
public Collection<PackFile> gc() throws IOException, ParseException {
if (!background) {
@@ -281,7 +281,7 @@ public class GC {
}
return Collections.emptyList();
};
- // TODO(ms): in 5.0 change signature and return the Future
+ // TODO(ms): change signature and return the Future
executor().submit(gcTask);
return Collections.emptyList();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFile.java
new file mode 100644
index 0000000000..075f55c733
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFile.java
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 2018, Konrad Windszus <konrad_w@gmx.de>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.http;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.StringReader;
+import java.io.Writer;
+import java.net.HttpCookie;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.file.FileSnapshot;
+import org.eclipse.jgit.internal.storage.file.LockFile;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FileUtils;
+import org.eclipse.jgit.util.IO;
+import org.eclipse.jgit.util.RawParseUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Wraps all cookies persisted in a <strong>Netscape Cookie File Format</strong>
+ * being referenced via the git config <a href=
+ * "https://git-scm.com/docs/git-config#git-config-httpcookieFile">http.cookieFile</a>.
+ *
+ * It will only load the cookies lazily, i.e. before calling
+ * {@link #getCookies(boolean)} the file is not evaluated. This class also
+ * allows persisting cookies in that file format.
+ * <p>
+ * In general this class is not thread-safe. So any consumer needs to take care
+ * of synchronization!
+ *
+ * @see <a href="http://www.cookiecentral.com/faq/#3.5">Netscape Cookie File
+ * Format</a>
+ * @see <a href=
+ * "https://unix.stackexchange.com/questions/36531/format-of-cookies-when-using-wget">Cookie
+ * format for wget</a>
+ * @see <a href=
+ * "https://github.com/curl/curl/blob/07ebaf837843124ee670e5b8c218b80b92e06e47/lib/cookie.c#L745">libcurl
+ * Cookie file parsing</a>
+ * @see <a href=
+ * "https://github.com/curl/curl/blob/07ebaf837843124ee670e5b8c218b80b92e06e47/lib/cookie.c#L1417">libcurl
+ * Cookie file writing</a>
+ * @see NetscapeCookieFileCache
+ */
+public final class NetscapeCookieFile {
+
+ private static final String HTTP_ONLY_PREAMBLE = "#HttpOnly_"; //$NON-NLS-1$
+
+ private static final String COLUMN_SEPARATOR = "\t"; //$NON-NLS-1$
+
+ private static final String LINE_SEPARATOR = "\n"; //$NON-NLS-1$
+
+ /**
+ * Maximum number of retries to acquire the lock for writing to the
+ * underlying file.
+ */
+ private static final int LOCK_ACQUIRE_MAX_RETRY_COUNT = 4;
+
+ /**
+ * Sleep time in milliseconds between retries to acquire the lock for
+ * writing to the underlying file.
+ */
+ private static final int LOCK_ACQUIRE_RETRY_SLEEP = 500;
+
+ private final Path path;
+
+ private FileSnapshot snapshot;
+
+ private byte[] hash;
+
+ final Date creationDate;
+
+ private Set<HttpCookie> cookies = null;
+
+ private static final Logger LOG = LoggerFactory
+ .getLogger(NetscapeCookieFile.class);
+
+ /**
+ * @param path
+ */
+ public NetscapeCookieFile(Path path) {
+ this(path, new Date());
+ }
+
+ NetscapeCookieFile(Path path, Date creationDate) {
+ this.path = path;
+ this.snapshot = FileSnapshot.DIRTY;
+ this.creationDate = creationDate;
+ }
+
+ /**
+ * @return the path to the underlying cookie file
+ */
+ public Path getPath() {
+ return path;
+ }
+
+ /**
+ * @param refresh
+ * if {@code true} updates the list from the underlying cookie
+ * file if it has been modified since the last read otherwise
+ * returns the current transient state. In case the cookie file
+ * has never been read before will always read from the
+ * underlying file disregarding the value of this parameter.
+ * @return all cookies (may contain session cookies as well). This does not
+ * return a copy of the list but rather the original one. Every
+ * addition to the returned list can afterwards be persisted via
+ * {@link #write(URL)}. Errors in the underlying file will not lead
+ * to exceptions but rather to an empty set being returned and the
+ * underlying error being logged.
+ */
+ public Set<HttpCookie> getCookies(boolean refresh) {
+ if (cookies == null || refresh) {
+ try {
+ byte[] in = getFileContentIfModified();
+ Set<HttpCookie> newCookies = parseCookieFile(in, creationDate);
+ if (cookies != null) {
+ cookies = mergeCookies(newCookies, cookies);
+ } else {
+ cookies = newCookies;
+ }
+ return cookies;
+ } catch (IOException | IllegalArgumentException e) {
+ LOG.warn(
+ MessageFormat.format(
+ JGitText.get().couldNotReadCookieFile, path),
+ e);
+ if (cookies == null) {
+ cookies = new LinkedHashSet<>();
+ }
+ }
+ }
+ return cookies;
+
+ }
+
+ /**
+ * Parses the given file and extracts all cookie information from it.
+ *
+ * @param input
+ * the file content to parse
+ * @param creationDate
+ * the date for the creation of the cookies (used to calculate
+ * the maxAge based on the expiration date given within the file)
+ * @return the set of parsed cookies from the given file (even expired
+ * ones). If there is more than one cookie with the same name in
+ * this file the last one overwrites the first one!
+ * @throws IOException
+ * if the given file could not be read for some reason
+ * @throws IllegalArgumentException
+ * if the given file does not have a proper format.
+ */
+ private static Set<HttpCookie> parseCookieFile(@NonNull byte[] input,
+ @NonNull Date creationDate)
+ throws IOException, IllegalArgumentException {
+
+ String decoded = RawParseUtils.decode(StandardCharsets.US_ASCII, input);
+
+ Set<HttpCookie> cookies = new LinkedHashSet<>();
+ try (BufferedReader reader = new BufferedReader(
+ new StringReader(decoded))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ HttpCookie cookie = parseLine(line, creationDate);
+ if (cookie != null) {
+ cookies.add(cookie);
+ }
+ }
+ }
+ return cookies;
+ }
+
+ private static HttpCookie parseLine(@NonNull String line,
+ @NonNull Date creationDate) {
+ if (line.isEmpty() || (line.startsWith("#") //$NON-NLS-1$
+ && !line.startsWith(HTTP_ONLY_PREAMBLE))) {
+ return null;
+ }
+ String[] cookieLineParts = line.split(COLUMN_SEPARATOR, 7);
+ if (cookieLineParts == null) {
+ throw new IllegalArgumentException(MessageFormat
+ .format(JGitText.get().couldNotFindTabInLine, line));
+ }
+ if (cookieLineParts.length < 7) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().couldNotFindSixTabsInLine,
+ Integer.valueOf(cookieLineParts.length), line));
+ }
+ String name = cookieLineParts[5];
+ String value = cookieLineParts[6];
+ HttpCookie cookie = new HttpCookie(name, value);
+
+ String domain = cookieLineParts[0];
+ if (domain.startsWith(HTTP_ONLY_PREAMBLE)) {
+ cookie.setHttpOnly(true);
+ domain = domain.substring(HTTP_ONLY_PREAMBLE.length());
+ }
+ // strip off leading "."
+ // (https://tools.ietf.org/html/rfc6265#section-5.2.3)
+ if (domain.startsWith(".")) { //$NON-NLS-1$
+ domain = domain.substring(1);
+ }
+ cookie.setDomain(domain);
+ // domain evaluation as boolean flag not considered (i.e. always assumed
+ // to be true)
+ cookie.setPath(cookieLineParts[2]);
+ cookie.setSecure(Boolean.parseBoolean(cookieLineParts[3]));
+
+ long expires = Long.parseLong(cookieLineParts[4]);
+ long maxAge = (expires - creationDate.getTime()) / 1000;
+ if (maxAge <= 0) {
+ return null; // skip expired cookies
+ }
+ cookie.setMaxAge(maxAge);
+ return cookie;
+ }
+
+ /**
+ * Writes all the cookies being maintained in the set being returned by
+ * {@link #getCookies(boolean)} to the underlying file.
+ *
+ * Session-cookies will not be persisted.
+ *
+ * @param url
+ * url for which to write the cookies (important to derive
+ * default values for non-explicitly set attributes)
+ * @throws IOException
+ * @throws IllegalArgumentException
+ * @throws InterruptedException
+ */
+ public void write(URL url)
+ throws IllegalArgumentException, IOException, InterruptedException {
+ try {
+ byte[] cookieFileContent = getFileContentIfModified();
+ if (cookieFileContent != null) {
+ LOG.debug(
+ "Reading the underlying cookie file '{}' as it has been modified since the last access", //$NON-NLS-1$
+ path);
+ // reread new changes if necessary
+ Set<HttpCookie> cookiesFromFile = NetscapeCookieFile
+ .parseCookieFile(cookieFileContent, creationDate);
+ this.cookies = mergeCookies(cookiesFromFile, cookies);
+ }
+ } catch (FileNotFoundException e) {
+ // ignore if file previously did not exist yet!
+ }
+
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ try (Writer writer = new OutputStreamWriter(output,
+ StandardCharsets.US_ASCII)) {
+ write(writer, cookies, url, creationDate);
+ }
+ LockFile lockFile = new LockFile(path.toFile());
+ for (int retryCount = 0; retryCount < LOCK_ACQUIRE_MAX_RETRY_COUNT; retryCount++) {
+ if (lockFile.lock()) {
+ try {
+ lockFile.setNeedSnapshot(true);
+ lockFile.write(output.toByteArray());
+ if (!lockFile.commit()) {
+ throw new IOException(MessageFormat.format(
+ JGitText.get().cannotCommitWriteTo, path));
+ }
+ } finally {
+ lockFile.unlock();
+ }
+ return;
+ }
+ Thread.sleep(LOCK_ACQUIRE_RETRY_SLEEP);
+ }
+ throw new IOException(
+ MessageFormat.format(JGitText.get().cannotLock, lockFile));
+
+ }
+
+ /**
+ * Read the underying file and return its content but only in case it has
+ * been modified since the last access. Internally calculates the hash and
+ * maintains {@link FileSnapshot}s to prevent issues described as <a href=
+ * "https://github.com/git/git/blob/master/Documentation/technical/racy-git.txt">"Racy
+ * Git problem"</a>. Inspired by {@link FileBasedConfig#load()}.
+ *
+ * @return the file contents in case the file has been modified since the
+ * last access, otherwise {@code null}
+ * @throws IOException
+ */
+ private byte[] getFileContentIfModified() throws IOException {
+ final int maxStaleRetries = 5;
+ int retries = 0;
+ File file = getPath().toFile();
+ if (!file.exists()) {
+ LOG.warn(MessageFormat.format(JGitText.get().missingCookieFile,
+ file.getAbsolutePath()));
+ return new byte[0];
+ }
+ while (true) {
+ final FileSnapshot oldSnapshot = snapshot;
+ final FileSnapshot newSnapshot = FileSnapshot.save(file);
+ try {
+ final byte[] in = IO.readFully(file);
+ byte[] newHash = hash(in);
+ if (Arrays.equals(hash, newHash)) {
+ if (oldSnapshot.equals(newSnapshot)) {
+ oldSnapshot.setClean(newSnapshot);
+ } else {
+ snapshot = newSnapshot;
+ }
+ } else {
+ snapshot = newSnapshot;
+ hash = newHash;
+ }
+ return in;
+ } catch (FileNotFoundException e) {
+ throw e;
+ } catch (IOException e) {
+ if (FileUtils.isStaleFileHandle(e)
+ && retries < maxStaleRetries) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(MessageFormat.format(
+ JGitText.get().configHandleIsStale,
+ Integer.valueOf(retries)), e);
+ }
+ retries++;
+ continue;
+ }
+ throw new IOException(MessageFormat
+ .format(JGitText.get().cannotReadFile, getPath()), e);
+ }
+ }
+
+ }
+
+ private byte[] hash(final byte[] in) {
+ return Constants.newMessageDigest().digest(in);
+ }
+
+ /**
+ * Writes the given cookies to the file in the Netscape Cookie File Format
+ * (also used by curl)
+ *
+ * @param writer
+ * the writer to use to persist the cookies.
+ * @param cookies
+ * the cookies to write into the file
+ * @param url
+ * the url for which to write the cookie (to derive the default
+ * values for certain cookie attributes)
+ * @param creationDate
+ * the date when the cookie has been created. Important for
+ * calculation the cookie expiration time (calculated from
+ * cookie's maxAge and this creation time).
+ * @throws IOException
+ */
+ static void write(@NonNull Writer writer,
+ @NonNull Collection<HttpCookie> cookies, @NonNull URL url,
+ @NonNull Date creationDate) throws IOException {
+ for (HttpCookie cookie : cookies) {
+ writeCookie(writer, cookie, url, creationDate);
+ }
+ }
+
+ private static void writeCookie(@NonNull Writer writer,
+ @NonNull HttpCookie cookie, @NonNull URL url,
+ @NonNull Date creationDate) throws IOException {
+ if (cookie.getMaxAge() <= 0) {
+ return; // skip expired cookies
+ }
+ String domain = ""; //$NON-NLS-1$
+ if (cookie.isHttpOnly()) {
+ domain = HTTP_ONLY_PREAMBLE;
+ }
+ if (cookie.getDomain() != null) {
+ domain += cookie.getDomain();
+ } else {
+ domain += url.getHost();
+ }
+ writer.write(domain);
+ writer.write(COLUMN_SEPARATOR);
+ writer.write("TRUE"); //$NON-NLS-1$
+ writer.write(COLUMN_SEPARATOR);
+ String path = cookie.getPath();
+ if (path == null) {
+ path = url.getPath();
+ }
+ writer.write(path);
+ writer.write(COLUMN_SEPARATOR);
+ writer.write(Boolean.toString(cookie.getSecure()).toUpperCase());
+ writer.write(COLUMN_SEPARATOR);
+ final String expirationDate;
+ // whenCreated field is not accessible in HttpCookie
+ expirationDate = String
+ .valueOf(creationDate.getTime() + (cookie.getMaxAge() * 1000));
+ writer.write(expirationDate);
+ writer.write(COLUMN_SEPARATOR);
+ writer.write(cookie.getName());
+ writer.write(COLUMN_SEPARATOR);
+ writer.write(cookie.getValue());
+ writer.write(LINE_SEPARATOR);
+ }
+
+ /**
+ * Merge the given sets in the following way. All cookies from
+ * {@code cookies1} and {@code cookies2} are contained in the resulting set
+ * which have unique names. If there is a duplicate entry for one name only
+ * the entry from set {@code cookies1} ends up in the resulting set.
+ *
+ * @param cookies1
+ * @param cookies2
+ *
+ * @return the merged cookies
+ */
+ static Set<HttpCookie> mergeCookies(Set<HttpCookie> cookies1,
+ @Nullable Set<HttpCookie> cookies2) {
+ Set<HttpCookie> mergedCookies = new LinkedHashSet<>(cookies1);
+ if (cookies2 != null) {
+ mergedCookies.addAll(cookies2);
+ }
+ return mergedCookies;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFileCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFileCache.java
new file mode 100644
index 0000000000..882b2d055b
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/transport/http/NetscapeCookieFileCache.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018, Konrad Windszus <konrad_w@gmx.de>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.internal.transport.http;
+
+import java.nio.file.Path;
+
+import org.eclipse.jgit.transport.HttpConfig;
+import org.eclipse.jgit.util.LRUMap;
+
+/**
+ * A cache of all known cookie files ({@link NetscapeCookieFile}). May contain
+ * at most {@code n} entries, where the least-recently used one is evicted as
+ * soon as more entries are added. The maximum number of entries (={@code n})
+ * can be set via the git config key {@code http.cookieFileCacheLimit}. By
+ * default it is set to 10.
+ * <p>
+ * The cache is global, i.e. it is shared among all consumers within the same
+ * Java process.
+ *
+ * @see NetscapeCookieFile
+ *
+ */
+public class NetscapeCookieFileCache {
+
+ private final LRUMap<Path, NetscapeCookieFile> cookieFileMap;
+
+ private static NetscapeCookieFileCache instance;
+
+ private NetscapeCookieFileCache(HttpConfig config) {
+ cookieFileMap = new LRUMap<>(config.getCookieFileCacheLimit(),
+ config.getCookieFileCacheLimit());
+ }
+
+ /**
+ * @param config
+ * the config which defines the limit for this cache
+ * @return the singleton instance of the cookie file cache. If the cache has
+ * already been created the given config is ignored (even if it
+ * differs from the config, with which the cache has originally been
+ * created)
+ */
+ public static NetscapeCookieFileCache getInstance(HttpConfig config) {
+ if (instance == null) {
+ return new NetscapeCookieFileCache(config);
+ } else {
+ return instance;
+ }
+ }
+
+ /**
+ * @param path
+ * the path of the cookie file to retrieve
+ * @return the cache entry belonging to the requested file
+ */
+ public NetscapeCookieFile getEntry(Path path) {
+ if (!cookieFileMap.containsKey(path)) {
+ synchronized (NetscapeCookieFileCache.class) {
+ if (!cookieFileMap.containsKey(path)) {
+ cookieFileMap.put(path, new NetscapeCookieFile(path));
+ }
+ }
+ }
+ return cookieFileMap.get(path);
+ }
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbbreviatedObjectId.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbbreviatedObjectId.java
index d105d0d200..b0339c677f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbbreviatedObjectId.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbbreviatedObjectId.java
@@ -162,7 +162,7 @@ public final class AbbreviatedObjectId implements Serializable {
r |= RawParseUtils.parseHexInt4(bs[p++]);
n++;
}
- return r << (8 - n) * 4;
+ return r << ((8 - n) * 4);
}
static int mask(int nibbles, int word, int v) {
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 4726975d07..1032fd0df1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
@@ -107,7 +107,7 @@ public class Config {
* must ensure it is a special copy of the empty string. It also must
* be treated like the empty string.
*/
- static final String MAGIC_EMPTY_VALUE = new String();
+ private static final String MISSING_ENTRY = new String();
/**
* Create a configuration with no default fallback.
@@ -129,6 +129,17 @@ public class Config {
}
/**
+ * Check if a given string is the "missing" value.
+ *
+ * @param value
+ * @return true if the given string is the "missing" value.
+ * @since 5.4
+ */
+ public static boolean isMissing(String value) {
+ return value == MISSING_ENTRY;
+ }
+
+ /**
* Globally sets a {@link org.eclipse.jgit.lib.TypedConfigGetter} that is
* subsequently used to read typed values from all git configs.
*
@@ -1041,7 +1052,7 @@ public class Config {
if (e.prefix == null || "".equals(e.prefix)) //$NON-NLS-1$
out.append('\t');
out.append(e.name);
- if (MAGIC_EMPTY_VALUE != e.value) {
+ if (MISSING_ENTRY != e.value) {
out.append(" ="); //$NON-NLS-1$
if (e.value != null) {
out.append(' ');
@@ -1132,7 +1143,7 @@ public class Config {
e.name = readKeyName(in);
if (e.name.endsWith("\n")) { //$NON-NLS-1$
e.name = e.name.substring(0, e.name.length() - 1);
- e.value = MAGIC_EMPTY_VALUE;
+ e.value = MISSING_ENTRY;
} else
e.value = readValue(in);
@@ -1165,7 +1176,7 @@ public class Config {
private void addIncludedConfig(final List<ConfigLine> newEntries,
ConfigLine line, int depth) throws ConfigInvalidException {
if (!line.name.equalsIgnoreCase("path") || //$NON-NLS-1$
- line.value == null || line.value.equals(MAGIC_EMPTY_VALUE)) {
+ line.value == null || line.value.equals(MISSING_ENTRY)) {
throw new ConfigInvalidException(MessageFormat.format(
JGitText.get().invalidLineInConfigFileWithParam, line));
}
@@ -1413,11 +1424,23 @@ public class Config {
case '"':
value.append('"');
continue;
+ case '\r': {
+ int next = in.read();
+ if (next == '\n') {
+ continue; // CR-LF
+ } else if (next >= 0) {
+ in.reset();
+ }
+ break;
+ }
default:
- throw new ConfigInvalidException(MessageFormat.format(
- JGitText.get().badEscape,
- Character.valueOf(((char) c))));
+ break;
}
+ throw new ConfigInvalidException(
+ MessageFormat.format(JGitText.get().badEscape,
+ Character.isAlphabetic(c)
+ ? Character.valueOf(((char) c))
+ : toUnicodeLiteral(c)));
}
if ('"' == c) {
@@ -1430,6 +1453,11 @@ public class Config {
return value.length() > 0 ? value.toString() : null;
}
+ private static String toUnicodeLiteral(int c) {
+ return String.format("\\u%04x", //$NON-NLS-1$
+ Integer.valueOf(c));
+ }
+
/**
* Parses a section of the configuration into an application model object.
* <p>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
index 6a66cf682f..4c70d20d6c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
@@ -72,7 +72,7 @@ public class DefaultTypedConfigGetter implements TypedConfigGetter {
if (n == null) {
return defaultValue;
}
- if (Config.MAGIC_EMPTY_VALUE == n) {
+ if (Config.isMissing(n)) {
return true;
}
try {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java
index 06b4b227c8..c0fcd4161f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RebaseTodoFile.java
@@ -179,7 +179,7 @@ public class RebaseTodoFile {
int nextSpace = RawParseUtils.next(buf, tokenBegin, ' ');
int tokenCount = 0;
- while (tokenCount < 3 && nextSpace < lineEnd) {
+ while (tokenCount < 3 && nextSpace <= lineEnd) {
switch (tokenCount) {
case 0:
String actionToken = new String(buf, tokenBegin,
@@ -191,8 +191,14 @@ public class RebaseTodoFile {
break;
case 1:
nextSpace = RawParseUtils.next(buf, tokenBegin, ' ');
- String commitToken = new String(buf, tokenBegin,
- nextSpace - tokenBegin - 1, UTF_8);
+ String commitToken;
+ if (nextSpace > lineEnd + 1) {
+ commitToken = new String(buf, tokenBegin,
+ lineEnd - tokenBegin + 1, UTF_8);
+ } else {
+ commitToken = new String(buf, tokenBegin,
+ nextSpace - tokenBegin - 1, UTF_8);
+ }
tokenBegin = nextSpace;
commit = AbbreviatedObjectId.fromString(commitToken);
break;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
index aac63e9d24..d53b0c926a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -240,6 +240,15 @@ public abstract class Repository implements AutoCloseable {
}
/**
+ * Get repository identifier.
+ *
+ * @return repository identifier. The returned identifier has to be unique
+ * within a given Git server.
+ * @since 5.4
+ */
+ public abstract String getIdentifier();
+
+ /**
* Get the object database which stores this repository's data.
*
* @return the object database which stores this repository's data.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java
index df9615fc9d..0d44317658 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgKeyLocator.java
@@ -54,6 +54,8 @@ import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
import java.text.MessageFormat;
import java.util.Iterator;
import java.util.Locale;
@@ -67,6 +69,7 @@ import org.bouncycastle.gpg.keybox.KeyBox;
import org.bouncycastle.gpg.keybox.KeyInformation;
import org.bouncycastle.gpg.keybox.PublicKeyRingBlob;
import org.bouncycastle.gpg.keybox.UserID;
+import org.bouncycastle.gpg.keybox.jcajce.JcaKeyBoxBuilder;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSecretKey;
@@ -210,9 +213,12 @@ class BouncyCastleGpgKeyLocator {
* @return publicKey the public key (maybe <code>null</code>)
* @throws IOException
* in case of problems reading the file
+ * @throws NoSuchAlgorithmException
+ * @throws NoSuchProviderException
*/
private PGPPublicKey findPublicKeyInKeyBox(Path keyboxFile)
- throws IOException {
+ throws IOException, NoSuchAlgorithmException,
+ NoSuchProviderException {
KeyBox keyBox = readKeyBoxFile(keyboxFile);
for (KeyBlob keyBlob : keyBox.getKeyBlobs()) {
if (keyBlob.getType() == BlobType.OPEN_PGP_BLOB) {
@@ -236,15 +242,17 @@ class BouncyCastleGpgKeyLocator {
* @return the secret key
* @throws IOException
* in case of issues reading key files
+ * @throws NoSuchAlgorithmException
+ * @throws NoSuchProviderException
* @throws PGPException
* in case of issues finding a key
* @throws CanceledException
* @throws URISyntaxException
* @throws UnsupportedCredentialItem
*/
- public BouncyCastleGpgKey findSecretKey()
- throws IOException, PGPException, CanceledException,
- UnsupportedCredentialItem, URISyntaxException {
+ public BouncyCastleGpgKey findSecretKey() throws IOException,
+ NoSuchAlgorithmException, NoSuchProviderException, PGPException,
+ CanceledException, UnsupportedCredentialItem, URISyntaxException {
if (exists(USER_KEYBOX_PATH)) {
PGPPublicKey publicKey = //
findPublicKeyInKeyBox(USER_KEYBOX_PATH);
@@ -376,14 +384,12 @@ class BouncyCastleGpgKeyLocator {
.getPublicKey();
}
- private KeyBox readKeyBoxFile(Path keyboxFile) throws IOException {
+ private KeyBox readKeyBoxFile(Path keyboxFile) throws IOException,
+ NoSuchAlgorithmException, NoSuchProviderException {
KeyBox keyBox;
try (InputStream in = new BufferedInputStream(
newInputStream(keyboxFile))) {
- // note: KeyBox constructor reads in the whole InputStream at once
- // this code will change in 1.61 to
- // either 'new BcKeyBox(in)' or 'new JcaKeyBoxBuilder().build(in)'
- keyBox = new KeyBox(in, new JcaKeyFingerprintCalculator());
+ keyBox = new JcaKeyBoxBuilder().build(in);
}
return keyBox;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java
index 4d696dd9e7..cfe0931b47 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java
@@ -45,6 +45,8 @@ package org.eclipse.jgit.lib.internal;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URISyntaxException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
import java.security.Security;
import org.bouncycastle.bcpg.ArmoredOutputStream;
@@ -100,7 +102,8 @@ public class BouncyCastleGpgSigner extends GpgSigner {
BouncyCastleGpgKey gpgKey = locateSigningKey(gpgSigningKey,
committer, passphrasePrompt);
return gpgKey != null;
- } catch (PGPException | IOException | URISyntaxException e) {
+ } catch (PGPException | IOException | NoSuchAlgorithmException
+ | NoSuchProviderException | URISyntaxException e) {
return false;
}
}
@@ -109,7 +112,8 @@ public class BouncyCastleGpgSigner extends GpgSigner {
PersonIdent committer,
BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt)
throws CanceledException, UnsupportedCredentialItem, IOException,
- PGPException, URISyntaxException {
+ NoSuchAlgorithmException, NoSuchProviderException, PGPException,
+ URISyntaxException {
if (gpgSigningKey == null || gpgSigningKey.isEmpty()) {
gpgSigningKey = committer.getEmailAddress();
}
@@ -153,7 +157,8 @@ public class BouncyCastleGpgSigner extends GpgSigner {
signatureGenerator.generate().encode(out);
}
commit.setGpgSignature(new GpgSignature(buffer.toByteArray()));
- } catch (PGPException | IOException | URISyntaxException e) {
+ } catch (PGPException | IOException | NoSuchAlgorithmException
+ | NoSuchProviderException | URISyntaxException e) {
throw new JGitInternalException(e.getMessage(), e);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
index b69327a9b3..0c3d3fec26 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -1190,7 +1190,7 @@ public class ResolveMerger extends ThreeWayMerger {
* otherwise
*/
public boolean failed() {
- return failingPaths.size() > 0;
+ return !failingPaths.isEmpty();
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapCalculator.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapCalculator.java
index e1d5d4adad..14e95670aa 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapCalculator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapCalculator.java
@@ -1,3 +1,45 @@
+/*
+ * Copyright (C) 2019, Google LLC.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
package org.eclipse.jgit.revwalk;
import static java.util.Objects.requireNonNull;
@@ -52,8 +94,9 @@ class BitmapCalculator {
* @throws MissingObjectException
* the supplied id doesn't exist
* @throws IncorrectObjectTypeException
- * the supplied id doens't refer to a commit or a tag
+ * the supplied id doesn't refer to a commit or a tag
* @throws IOException
+ * if the walk cannot open a packfile or loose object
*/
BitmapBuilder getBitmap(RevCommit start, ProgressMonitor pm)
throws MissingObjectException,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmappedReachabilityChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmappedReachabilityChecker.java
index ab453433d8..6e510f677b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmappedReachabilityChecker.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmappedReachabilityChecker.java
@@ -55,10 +55,8 @@ import org.eclipse.jgit.lib.NullProgressMonitor;
/**
* Checks the reachability using bitmaps.
- *
- * @since 5.4
*/
-public class BitmappedReachabilityChecker implements ReachabilityChecker {
+class BitmappedReachabilityChecker implements ReachabilityChecker {
private final RevWalk walk;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianReachabilityChecker.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianReachabilityChecker.java
index 4012a45d3c..bba3c5cff3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianReachabilityChecker.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/PedestrianReachabilityChecker.java
@@ -52,10 +52,8 @@ import org.eclipse.jgit.errors.MissingObjectException;
/**
* Checks the reachability walking the graph from the starters towards the
* target.
- *
- * @since 5.4
*/
-public class PedestrianReachabilityChecker implements ReachabilityChecker {
+class PedestrianReachabilityChecker implements ReachabilityChecker {
private final boolean topoSort;
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 f12eb2ff86..80fc81073c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
@@ -250,6 +250,23 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable {
}
/**
+ * Get a reachability checker for commits over this revwalk.
+ *
+ * @return the most efficient reachability checker for this repository.
+ * @throws IOException
+ * if it cannot open any of the underlying indices.
+ *
+ * @since 5.4
+ */
+ public ReachabilityChecker createReachabilityChecker() throws IOException {
+ if (reader.getBitmapIndex() != null) {
+ return new BitmappedReachabilityChecker(this);
+ }
+
+ return new PedestrianReachabilityChecker(true, this);
+ }
+
+ /**
* {@inheritDoc}
* <p>
* Release any resources used by this walker's reader.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java
index fabf7075d5..2b721b8877 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalkUtils.java
@@ -50,6 +50,9 @@ import java.util.List;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
/**
@@ -153,15 +156,51 @@ public final class RevWalkUtils {
RevWalk revWalk, Collection<Ref> refs)
throws MissingObjectException, IncorrectObjectTypeException,
IOException {
+ return findBranchesReachableFrom(commit, revWalk, refs,
+ NullProgressMonitor.INSTANCE);
+ }
+
+ /**
+ * Find the list of branches a given commit is reachable from when following
+ * parents.
+ * <p>
+ * Note that this method calls
+ * {@link org.eclipse.jgit.revwalk.RevWalk#reset()} at the beginning.
+ * <p>
+ * In order to improve performance this method assumes clock skew among
+ * committers is never larger than 24 hours.
+ *
+ * @param commit
+ * the commit we are looking at
+ * @param revWalk
+ * The RevWalk to be used.
+ * @param refs
+ * the set of branches we want to see reachability from
+ * @param monitor
+ * the callback for progress and cancellation
+ * @return the list of branches a given commit is reachable from
+ * @throws org.eclipse.jgit.errors.MissingObjectException
+ * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
+ * @throws java.io.IOException
+ * @since 5.4
+ */
+ public static List<Ref> findBranchesReachableFrom(RevCommit commit,
+ RevWalk revWalk, Collection<Ref> refs, ProgressMonitor monitor)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ IOException {
// Make sure commit is from the same RevWalk
commit = revWalk.parseCommit(commit.getId());
revWalk.reset();
List<Ref> result = new ArrayList<>();
-
+ monitor.beginTask(JGitText.get().searchForReachableBranches,
+ refs.size());
final int SKEW = 24*3600; // one day clock skew
for (Ref ref : refs) {
+ if (monitor.isCancelled())
+ return result;
+ monitor.update(1);
RevObject maybehead = revWalk.parseAny(ref.getObjectId());
if (!(maybehead instanceof RevCommit))
continue;
@@ -176,6 +215,7 @@ public final class RevWalkUtils {
if (revWalk.isMergedInto(commit, headCommit))
result.add(ref);
}
+ monitor.endTask();
return result;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
index 2b31ebd8e3..fc6f4a39cd 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/FileBasedConfig.java
@@ -148,7 +148,8 @@ public class FileBasedConfig extends StoredConfig {
*/
@Override
public void load() throws IOException, ConfigInvalidException {
- final int maxStaleRetries = 5;
+ final int maxRetries = 5;
+ int retryDelayMillis = 20;
int retries = 0;
while (true) {
final FileSnapshot oldSnapshot = snapshot;
@@ -177,6 +178,22 @@ public class FileBasedConfig extends StoredConfig {
}
return;
} catch (FileNotFoundException noFile) {
+ // might be locked by another process (see exception Javadoc)
+ if (retries < maxRetries && configFile.exists()) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(MessageFormat.format(
+ JGitText.get().configHandleMayBeLocked,
+ Integer.valueOf(retries)), noFile);
+ }
+ try {
+ Thread.sleep(retryDelayMillis);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ retries++;
+ retryDelayMillis *= 2; // max wait 1260 ms
+ continue;
+ }
if (configFile.exists()) {
throw noFile;
}
@@ -185,7 +202,7 @@ public class FileBasedConfig extends StoredConfig {
return;
} catch (IOException e) {
if (FileUtils.isStaleFileHandle(e)
- && retries < maxStaleRetries) {
+ && retries < maxRetries) {
if (LOG.isDebugEnabled()) {
LOG.debug(MessageFormat.format(
JGitText.get().configHandleIsStale,
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 20a5d9da72..e8724b72db 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackConnection.java
@@ -201,7 +201,7 @@ abstract class BasePackConnection extends BaseConnection {
throw noRepository();
throw eof;
}
- if (line == PacketLineIn.END)
+ if (PacketLineIn.isEnd(line))
break;
if (line.startsWith("ERR ")) { //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
index 847e901980..35ea35ecb8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java
@@ -383,8 +383,7 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
JGitText.get().errorOccurredDuringUnpackingOnTheRemoteEnd, unpackStatus));
}
- String refLine;
- while ((refLine = pckIn.readString()) != PacketLineIn.END) {
+ for (String refLine : pckIn.readStrings()) {
boolean ok = false;
int refNameEnd = -1;
if (refLine.startsWith("ok ")) { //$NON-NLS-1$
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
index 1741db97fe..e402de0158 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
@@ -1282,7 +1282,7 @@ public abstract class BaseReceivePack {
return;
throw eof;
}
- if (line == PacketLineIn.END) {
+ if (PacketLineIn.isEnd(line)) {
break;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
index a09b1ff1d2..d1db51eca6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java
@@ -227,7 +227,7 @@ public class BundleWriter {
exc.add(r.getId());
packWriter.setIndexDisabled(true);
packWriter.setDeltaBaseAsOffset(true);
- packWriter.setThin(exc.size() > 0);
+ packWriter.setThin(!exc.isEmpty());
packWriter.setReuseValidatingObjects(false);
if (exc.isEmpty()) {
packWriter.setTagTargets(tagTargets);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java
index 53eaa6a7f9..01f6fec7e4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HMACSHA1NonceGenerator.java
@@ -45,14 +45,12 @@ package org.eclipse.jgit.transport;
import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static java.nio.charset.StandardCharsets.UTF_8;
-import java.io.File;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
-import org.eclipse.jgit.internal.storage.dfs.DfsRepository;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.PushCertificate.NonceStatus;
@@ -87,19 +85,7 @@ public class HMACSHA1NonceGenerator implements NonceGenerator {
@Override
public synchronized String createNonce(Repository repo, long timestamp)
throws IllegalStateException {
- String path;
- if (repo instanceof DfsRepository) {
- path = ((DfsRepository) repo).getDescription().getRepositoryName();
- } else {
- File directory = repo.getDirectory();
- if (directory != null) {
- path = directory.getPath();
- } else {
- throw new IllegalStateException();
- }
- }
-
- String input = path + ":" + String.valueOf(timestamp); //$NON-NLS-1$
+ String input = repo.getIdentifier() + ":" + String.valueOf(timestamp); //$NON-NLS-1$
byte[] rawHmac = mac.doFinal(input.getBytes(UTF_8));
return Long.toString(timestamp) + "-" + toHex(rawHmac); //$NON-NLS-1$
}
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 101ce35685..54c21cbc8c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpConfig.java
@@ -89,6 +89,30 @@ public class HttpConfig {
/** git config key for the "sslVerify" setting. */
public static final String SSL_VERIFY_KEY = "sslVerify"; //$NON-NLS-1$
+ /**
+ * git config key for the "cookieFile" setting.
+ *
+ * @since 5.4
+ */
+ public static final String COOKIE_FILE_KEY = "cookieFile"; //$NON-NLS-1$
+
+ /**
+ * git config key for the "saveCookies" setting.
+ *
+ * @since 5.4
+ */
+ public static final String SAVE_COOKIES_KEY = "saveCookies"; //$NON-NLS-1$
+
+ /**
+ * Custom JGit config key which holds the maximum number of cookie files to
+ * keep in the cache.
+ *
+ * @since 5.4
+ */
+ public static final String COOKIE_FILE_CACHE_LIMIT_KEY = "cookieFileCacheLimit"; //$NON-NLS-1$
+
+ private static final int DEFAULT_COOKIE_FILE_CACHE_LIMIT = 10;
+
private static final String MAX_REDIRECT_SYSTEM_PROPERTY = "http.maxRedirects"; //$NON-NLS-1$
private static final int DEFAULT_MAX_REDIRECTS = 5;
@@ -153,6 +177,12 @@ public class HttpConfig {
private int maxRedirects;
+ private String cookieFile;
+
+ private boolean saveCookies;
+
+ private int cookieFileCacheLimit;
+
/**
* Get the "http.postBuffer" setting
*
@@ -190,6 +220,40 @@ public class HttpConfig {
}
/**
+ * Get the "http.cookieFile" setting
+ *
+ * @return the value of the "http.cookieFile" setting
+ *
+ * @since 5.4
+ */
+ public String getCookieFile() {
+ return cookieFile;
+ }
+
+ /**
+ * Get the "http.saveCookies" setting
+ *
+ * @return the value of the "http.saveCookies" setting
+ *
+ * @since 5.4
+ */
+ public boolean getSaveCookies() {
+ return saveCookies;
+ }
+
+ /**
+ * Get the "http.cookieFileCacheLimit" setting (gives the maximum number of
+ * cookie files to keep in the LRU cache)
+ *
+ * @return the value of the "http.cookieFileCacheLimit" setting
+ *
+ * @since 5.4
+ */
+ public int getCookieFileCacheLimit() {
+ return cookieFileCacheLimit;
+ }
+
+ /**
* Creates a new {@link org.eclipse.jgit.transport.HttpConfig} tailored to
* the given {@link org.eclipse.jgit.transport.URIish}.
*
@@ -237,6 +301,10 @@ public class HttpConfig {
if (redirectLimit < 0) {
redirectLimit = MAX_REDIRECTS;
}
+ 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
@@ -251,6 +319,13 @@ public class HttpConfig {
if (newMaxRedirects >= 0) {
redirectLimit = newMaxRedirects;
}
+ String urlSpecificCookieFile = config.getString(HTTP, match,
+ COOKIE_FILE_KEY);
+ if (urlSpecificCookieFile != null) {
+ cookieFile = urlSpecificCookieFile;
+ }
+ saveCookies = config.getBoolean(HTTP, match, SAVE_COOKIES_KEY,
+ saveCookies);
}
postBuffer = postBufferSize;
sslVerify = sslVerifyFlag;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java
index c6e19d5762..90f1b373ba 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineIn.java
@@ -49,7 +49,9 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.IOException;
import java.io.InputStream;
+import java.io.UncheckedIOException;
import java.text.MessageFormat;
+import java.util.Iterator;
import org.eclipse.jgit.errors.PackProtocolException;
import org.eclipse.jgit.internal.JGitText;
@@ -72,14 +74,25 @@ import org.slf4j.LoggerFactory;
public class PacketLineIn {
private static final Logger log = LoggerFactory.getLogger(PacketLineIn.class);
- /** Magic return from {@link #readString()} when a flush packet is found. */
+ /**
+ * Magic return from {@link #readString()} when a flush packet is found.
+ *
+ * @deprecated Callers should use {@link #isEnd(String)} to check if a
+ * string is the end marker, or
+ * {@link PacketLineIn#readStrings()} to iterate over all
+ * strings in the input stream until the marker is reached.
+ */
+ @Deprecated
public static final String END = new StringBuilder(0).toString(); /* must not string pool */
/**
* Magic return from {@link #readString()} when a delim packet is found.
*
* @since 5.0
+ * @deprecated Callers should use {@link #isDelimiter(String)} to check if a
+ * string is the delimiter.
*/
+ @Deprecated
public static final String DELIM = new StringBuilder(0).toString(); /* must not string pool */
static enum AckNackResult {
@@ -193,6 +206,20 @@ public class PacketLineIn {
}
/**
+ * Get an iterator to read strings from the input stream.
+ *
+ * @return an iterator that calls {@link #readString()} until {@link #END}
+ * is encountered.
+ *
+ * @throws IOException
+ * on failure to read the initial packet line.
+ * @since 5.4
+ */
+ public PacketLineInIterator readStrings() throws IOException {
+ return new PacketLineInIterator(this);
+ }
+
+ /**
* Read a single UTF-8 encoded string packet from the input stream.
* <p>
* Unlike {@link #readString()} a trailing LF will be retained.
@@ -224,6 +251,52 @@ public class PacketLineIn {
return s;
}
+ /**
+ * Check if a string is the delimiter marker.
+ *
+ * @param s
+ * the string to check
+ * @return true if the given string is {@link #DELIM}, otherwise false.
+ * @since 5.4
+ */
+ public static boolean isDelimiter(String s) {
+ return s == DELIM;
+ }
+
+ /**
+ * Get the delimiter marker.
+ * <p>
+ * Intended for use only in tests.
+ *
+ * @return The delimiter marker.
+ */
+ static String delimiter() {
+ return DELIM;
+ }
+
+ /**
+ * Get the end marker.
+ * <p>
+ * Intended for use only in tests.
+ *
+ * @return The end marker.
+ */
+ static String end() {
+ return END;
+ }
+
+ /**
+ * Check if a string is the packet end marker.
+ *
+ * @param s
+ * the string to check
+ * @return true if the given string is {@link #END}, otherwise false.
+ * @since 5.4
+ */
+ public static boolean isEnd(String s) {
+ return s == END;
+ }
+
void discardUntilEnd() throws IOException {
for (;;) {
int n = readLength();
@@ -282,4 +355,46 @@ public class PacketLineIn {
public static class InputOverLimitIOException extends IOException {
private static final long serialVersionUID = 1L;
}
+
+ /**
+ * Iterator over packet lines.
+ * <p>
+ * Calls {@link #readString()} on the {@link PacketLineIn} until
+ * {@link #END} is encountered.
+ *
+ * @since 5.4
+ *
+ */
+ public static class PacketLineInIterator implements Iterable<String> {
+ private PacketLineIn in;
+
+ private String current;
+
+ PacketLineInIterator(PacketLineIn in) throws IOException {
+ this.in = in;
+ current = in.readString();
+ }
+
+ @Override
+ public Iterator<String> iterator() {
+ return new Iterator<String>() {
+ @Override
+ public boolean hasNext() {
+ return !PacketLineIn.isEnd(current);
+ }
+
+ @Override
+ public String next() {
+ String next = current;
+ try {
+ current = in.readString();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ return next;
+ }
+ };
+ }
+
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java
index 396327aab0..428a45c09d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java
@@ -99,7 +99,7 @@ final class ProtocolV0Parser {
throw eof;
}
- if (line == PacketLineIn.END) {
+ if (PacketLineIn.isEnd(line)) {
break;
}
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 cb04ff69a9..caba15fc54 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java
@@ -92,7 +92,7 @@ final class ProtocolV2Parser {
String agentPrefix = OPTION_AGENT + '=';
String line = pckIn.readString();
- while (line != PacketLineIn.DELIM && line != PacketLineIn.END) {
+ while (!PacketLineIn.isDelimiter(line) && !PacketLineIn.isEnd(line)) {
if (line.startsWith(serverOptionPrefix)) {
serverOptionConsumer
.accept(line.substring(serverOptionPrefix.length()));
@@ -133,40 +133,41 @@ final class ProtocolV2Parser {
serverOption -> reqBuilder.addServerOption(serverOption),
agent -> reqBuilder.setAgent(agent));
- if (line == PacketLineIn.END) {
+ if (PacketLineIn.isEnd(line)) {
return reqBuilder.build();
}
- if (line != PacketLineIn.DELIM) {
+ if (!PacketLineIn.isDelimiter(line)) {
throw new PackProtocolException(
MessageFormat.format(JGitText.get().unexpectedPacketLine,
line));
}
boolean filterReceived = false;
- while ((line = pckIn.readString()) != PacketLineIn.END) {
- if (line.startsWith("want ")) { //$NON-NLS-1$
- reqBuilder.addWantId(ObjectId.fromString(line.substring(5)));
+ for (String line2 : pckIn.readStrings()) {
+ if (line2.startsWith("want ")) { //$NON-NLS-1$
+ reqBuilder.addWantId(ObjectId.fromString(line2.substring(5)));
} else if (transferConfig.isAllowRefInWant()
- && line.startsWith(OPTION_WANT_REF + " ")) { //$NON-NLS-1$
- reqBuilder.addWantedRef(line.substring(OPTION_WANT_REF.length() + 1));
- } else if (line.startsWith("have ")) { //$NON-NLS-1$
- reqBuilder.addPeerHas(ObjectId.fromString(line.substring(5)));
- } else if (line.equals("done")) { //$NON-NLS-1$
+ && line2.startsWith(OPTION_WANT_REF + " ")) { //$NON-NLS-1$
+ reqBuilder.addWantedRef(
+ line2.substring(OPTION_WANT_REF.length() + 1));
+ } else if (line2.startsWith("have ")) { //$NON-NLS-1$
+ reqBuilder.addPeerHas(ObjectId.fromString(line2.substring(5)));
+ } else if (line2.equals("done")) { //$NON-NLS-1$
reqBuilder.setDoneReceived();
- } else if (line.equals(OPTION_THIN_PACK)) {
+ } else if (line2.equals(OPTION_THIN_PACK)) {
reqBuilder.addClientCapability(OPTION_THIN_PACK);
- } else if (line.equals(OPTION_NO_PROGRESS)) {
+ } else if (line2.equals(OPTION_NO_PROGRESS)) {
reqBuilder.addClientCapability(OPTION_NO_PROGRESS);
- } else if (line.equals(OPTION_INCLUDE_TAG)) {
+ } else if (line2.equals(OPTION_INCLUDE_TAG)) {
reqBuilder.addClientCapability(OPTION_INCLUDE_TAG);
- } else if (line.equals(OPTION_OFS_DELTA)) {
+ } else if (line2.equals(OPTION_OFS_DELTA)) {
reqBuilder.addClientCapability(OPTION_OFS_DELTA);
- } else if (line.startsWith("shallow ")) { //$NON-NLS-1$
+ } else if (line2.startsWith("shallow ")) { //$NON-NLS-1$
reqBuilder.addClientShallowCommit(
- ObjectId.fromString(line.substring(8)));
- } else if (line.startsWith("deepen ")) { //$NON-NLS-1$
- int parsedDepth = Integer.parseInt(line.substring(7));
+ ObjectId.fromString(line2.substring(8)));
+ } else if (line2.startsWith("deepen ")) { //$NON-NLS-1$
+ int parsedDepth = Integer.parseInt(line2.substring(7));
if (parsedDepth <= 0) {
throw new PackProtocolException(
MessageFormat.format(JGitText.get().invalidDepth,
@@ -181,19 +182,19 @@ final class ProtocolV2Parser {
JGitText.get().deepenNotWithDeepen);
}
reqBuilder.setDepth(parsedDepth);
- } else if (line.startsWith("deepen-not ")) { //$NON-NLS-1$
- reqBuilder.addDeepenNotRef(line.substring(11));
+ } else if (line2.startsWith("deepen-not ")) { //$NON-NLS-1$
+ reqBuilder.addDeepenNotRef(line2.substring(11));
if (reqBuilder.getDepth() != 0) {
throw new PackProtocolException(
JGitText.get().deepenNotWithDeepen);
}
- } else if (line.equals(OPTION_DEEPEN_RELATIVE)) {
+ } else if (line2.equals(OPTION_DEEPEN_RELATIVE)) {
reqBuilder.addClientCapability(OPTION_DEEPEN_RELATIVE);
- } else if (line.startsWith("deepen-since ")) { //$NON-NLS-1$
- int ts = Integer.parseInt(line.substring(13));
+ } else if (line2.startsWith("deepen-since ")) { //$NON-NLS-1$
+ int ts = Integer.parseInt(line2.substring(13));
if (ts <= 0) {
throw new PackProtocolException(MessageFormat
- .format(JGitText.get().invalidTimestamp, line));
+ .format(JGitText.get().invalidTimestamp, line2));
}
if (reqBuilder.getDepth() != 0) {
throw new PackProtocolException(
@@ -201,17 +202,17 @@ final class ProtocolV2Parser {
}
reqBuilder.setDeepenSince(ts);
} else if (transferConfig.isAllowFilter()
- && line.startsWith(OPTION_FILTER + ' ')) {
+ && line2.startsWith(OPTION_FILTER + ' ')) {
if (filterReceived) {
throw new PackProtocolException(
JGitText.get().tooManyFilters);
}
filterReceived = true;
reqBuilder.setFilterSpec(FilterSpec.fromFilterLine(
- line.substring(OPTION_FILTER.length() + 1)));
+ line2.substring(OPTION_FILTER.length() + 1)));
} else {
throw new PackProtocolException(MessageFormat
- .format(JGitText.get().unexpectedPacketLine, line));
+ .format(JGitText.get().unexpectedPacketLine, line2));
}
}
@@ -244,25 +245,25 @@ final class ProtocolV2Parser {
serverOption -> builder.addServerOption(serverOption),
agent -> builder.setAgent(agent));
- if (line == PacketLineIn.END) {
+ if (PacketLineIn.isEnd(line)) {
return builder.build();
}
- if (line != PacketLineIn.DELIM) {
+ if (!PacketLineIn.isDelimiter(line)) {
throw new PackProtocolException(MessageFormat
.format(JGitText.get().unexpectedPacketLine, line));
}
- while ((line = pckIn.readString()) != PacketLineIn.END) {
- if (line.equals("peel")) { //$NON-NLS-1$
+ for (String line2 : pckIn.readStrings()) {
+ if (line2.equals("peel")) { //$NON-NLS-1$
builder.setPeel(true);
- } else if (line.equals("symrefs")) { //$NON-NLS-1$
+ } else if (line2.equals("symrefs")) { //$NON-NLS-1$
builder.setSymrefs(true);
- } else if (line.startsWith("ref-prefix ")) { //$NON-NLS-1$
- prefixes.add(line.substring("ref-prefix ".length())); //$NON-NLS-1$
+ } else if (line2.startsWith("ref-prefix ")) { //$NON-NLS-1$
+ prefixes.add(line2.substring("ref-prefix ".length())); //$NON-NLS-1$
} else {
throw new PackProtocolException(MessageFormat
- .format(JGitText.get().unexpectedPacketLine, line));
+ .format(JGitText.get().unexpectedPacketLine, line2));
}
}
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 4652c3fda8..d6adf1e0d8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceivePack.java
@@ -348,7 +348,7 @@ public class ReceivePack extends BaseReceivePack {
pushOptions = new ArrayList<>(4);
for (;;) {
String option = in.readString();
- if (option == PacketLineIn.END) {
+ if (PacketLineIn.isEnd(option)) {
break;
}
pushOptions.add(option);
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 b752a65275..27ab879515 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
@@ -54,8 +54,11 @@ import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT;
import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT_ENCODING;
import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_ENCODING;
import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_TYPE;
+import static org.eclipse.jgit.util.HttpSupport.HDR_COOKIE;
import static org.eclipse.jgit.util.HttpSupport.HDR_LOCATION;
import static org.eclipse.jgit.util.HttpSupport.HDR_PRAGMA;
+import static org.eclipse.jgit.util.HttpSupport.HDR_SET_COOKIE;
+import static org.eclipse.jgit.util.HttpSupport.HDR_SET_COOKIE2;
import static org.eclipse.jgit.util.HttpSupport.HDR_USER_AGENT;
import static org.eclipse.jgit.util.HttpSupport.HDR_WWW_AUTHENTICATE;
import static org.eclipse.jgit.util.HttpSupport.METHOD_GET;
@@ -68,11 +71,15 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
+import java.net.HttpCookie;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.URISyntaxException;
import java.net.URL;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.security.cert.CertPathBuilderException;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertificateException;
@@ -84,6 +91,8 @@ import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -100,6 +109,8 @@ import org.eclipse.jgit.errors.PackProtocolException;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.file.RefDirectory;
+import org.eclipse.jgit.internal.transport.http.NetscapeCookieFile;
+import org.eclipse.jgit.internal.transport.http.NetscapeCookieFileCache;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdRef;
@@ -116,6 +127,7 @@ import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.HttpSupport;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
+import org.eclipse.jgit.util.StringUtils;
import org.eclipse.jgit.util.SystemReader;
import org.eclipse.jgit.util.TemporaryBuffer;
import org.eclipse.jgit.util.io.DisabledOutputStream;
@@ -274,6 +286,19 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
private boolean sslFailure = false;
+ /**
+ * All stored cookies bound to this repo (independent of the baseUrl)
+ */
+ private final NetscapeCookieFile cookieFile;
+
+ /**
+ * The cookies to be sent with each request to the given {@link #baseUrl}.
+ * Filtered view on top of {@link #cookieFile} where only cookies which
+ * apply to the current url are left. This set needs to be filtered for
+ * expired entries each time prior to sending them.
+ */
+ private final Set<HttpCookie> relevantCookies;
+
TransportHttp(Repository local, URIish uri)
throws NotSupportedException {
super(local, uri);
@@ -281,6 +306,8 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
http = new HttpConfig(local.getConfig(), uri);
proxySelector = ProxySelector.getDefault();
sslVerify = http.isSslVerify();
+ cookieFile = getCookieFileFromConfig(http);
+ relevantCookies = filterCookies(cookieFile, baseUrl);
}
private URL toURL(URIish urish) throws MalformedURLException {
@@ -321,6 +348,8 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
http = new HttpConfig(uri);
proxySelector = ProxySelector.getDefault();
sslVerify = http.isSslVerify();
+ cookieFile = getCookieFileFromConfig(http);
+ relevantCookies = filterCookies(cookieFile, baseUrl);
}
/**
@@ -508,6 +537,7 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
conn.setRequestProperty(HDR_ACCEPT, "*/*"); //$NON-NLS-1$
}
final int status = HttpSupport.response(conn);
+ processResponseCookies(conn);
switch (status) {
case HttpConnection.HTTP_OK:
// Check if HttpConnection did some authentication in the
@@ -596,6 +626,57 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
}
}
+ void processResponseCookies(HttpConnection conn) {
+ if (cookieFile != null && http.getSaveCookies()) {
+ List<HttpCookie> foundCookies = new LinkedList<>();
+
+ List<String> cookieHeaderValues = conn
+ .getHeaderFields(HDR_SET_COOKIE);
+ if (!cookieHeaderValues.isEmpty()) {
+ foundCookies.addAll(
+ extractCookies(HDR_SET_COOKIE, cookieHeaderValues));
+ }
+ cookieHeaderValues = conn.getHeaderFields(HDR_SET_COOKIE2);
+ if (!cookieHeaderValues.isEmpty()) {
+ foundCookies.addAll(
+ extractCookies(HDR_SET_COOKIE2, cookieHeaderValues));
+ }
+ if (!foundCookies.isEmpty()) {
+ try {
+ // update cookie lists with the newly received cookies!
+ Set<HttpCookie> cookies = cookieFile.getCookies(false);
+ cookies.addAll(foundCookies);
+ cookieFile.write(baseUrl);
+ relevantCookies.addAll(foundCookies);
+ } catch (IOException | IllegalArgumentException
+ | InterruptedException e) {
+ LOG.warn(MessageFormat.format(
+ JGitText.get().couldNotPersistCookies,
+ cookieFile.getPath()), e);
+ }
+ }
+ }
+ }
+
+ private List<HttpCookie> extractCookies(String headerKey,
+ List<String> headerValues) {
+ List<HttpCookie> foundCookies = new LinkedList<>();
+ for (String headerValue : headerValues) {
+ foundCookies
+ .addAll(HttpCookie.parse(headerKey + ':' + headerValue));
+ }
+ // HttpCookies.parse(...) is only compliant with RFC 2965. Make it RFC
+ // 6265 compliant by applying the logic from
+ // https://tools.ietf.org/html/rfc6265#section-5.2.3
+ for (HttpCookie foundCookie : foundCookies) {
+ String domain = foundCookie.getDomain();
+ if (domain != null && domain.startsWith(".")) { //$NON-NLS-1$
+ foundCookie.setDomain(domain.substring(1));
+ }
+ }
+ return foundCookies;
+ }
+
private static class CredentialItems {
CredentialItem.InformationalMessage message;
@@ -847,14 +928,35 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
conn.setConnectTimeout(effTimeOut);
conn.setReadTimeout(effTimeOut);
}
+ // set cookie header if necessary
+ if (!relevantCookies.isEmpty()) {
+ setCookieHeader(conn);
+ }
+
if (this.headers != null && !this.headers.isEmpty()) {
- for (Map.Entry<String, String> entry : this.headers.entrySet())
+ for (Map.Entry<String, String> entry : this.headers.entrySet()) {
conn.setRequestProperty(entry.getKey(), entry.getValue());
+ }
}
authMethod.configureRequest(conn);
return conn;
}
+ private void setCookieHeader(HttpConnection conn) {
+ StringBuilder cookieHeaderValue = new StringBuilder();
+ for (HttpCookie cookie : relevantCookies) {
+ if (!cookie.hasExpired()) {
+ if (cookieHeaderValue.length() > 0) {
+ cookieHeaderValue.append(';');
+ }
+ cookieHeaderValue.append(cookie.toString());
+ }
+ }
+ if (cookieHeaderValue.length() > 0) {
+ conn.setRequestProperty(HDR_COOKIE, cookieHeaderValue.toString());
+ }
+ }
+
final InputStream openInputStream(HttpConnection conn)
throws IOException {
InputStream input = conn.getInputStream();
@@ -868,6 +970,150 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
return new TransportException(uri, why);
}
+ private static NetscapeCookieFile getCookieFileFromConfig(
+ HttpConfig config) {
+ if (!StringUtils.isEmptyOrNull(config.getCookieFile())) {
+ try {
+ Path cookieFilePath = Paths.get(config.getCookieFile());
+ return NetscapeCookieFileCache.getInstance(config)
+ .getEntry(cookieFilePath);
+ } catch (InvalidPathException e) {
+ LOG.warn(MessageFormat.format(
+ JGitText.get().couldNotReadCookieFile,
+ config.getCookieFile()), e);
+ }
+ }
+ return null;
+ }
+
+ private static Set<HttpCookie> filterCookies(NetscapeCookieFile cookieFile,
+ URL url) {
+ if (cookieFile != null) {
+ return filterCookies(cookieFile.getCookies(true), url);
+ }
+ return Collections.emptySet();
+ }
+
+ /**
+ *
+ * @param allCookies
+ * a list of cookies.
+ * @param url
+ * the url for which to filter the list of cookies.
+ * @return only the cookies from {@code allCookies} which are relevant (i.e.
+ * are not expired, have a matching domain, have a matching path and
+ * have a matching secure attribute)
+ */
+ private static Set<HttpCookie> filterCookies(Set<HttpCookie> allCookies,
+ URL url) {
+ Set<HttpCookie> filteredCookies = new HashSet<>();
+ for (HttpCookie cookie : allCookies) {
+ if (cookie.hasExpired()) {
+ continue;
+ }
+ if (!matchesCookieDomain(url.getHost(), cookie.getDomain())) {
+ continue;
+ }
+ if (!matchesCookiePath(url.getPath(), cookie.getPath())) {
+ continue;
+ }
+ if (cookie.getSecure() && !"https".equals(url.getProtocol())) { //$NON-NLS-1$
+ continue;
+ }
+ filteredCookies.add(cookie);
+ }
+ return filteredCookies;
+ }
+
+ /**
+ *
+ * The utility method to check whether a host name is in a cookie's domain
+ * or not. Similar to {@link HttpCookie#domainMatches(String, String)} but
+ * implements domain matching rules according to
+ * <a href="https://tools.ietf.org/html/rfc6265#section-5.1.3">RFC 6265,
+ * section 5.1.3</a> instead of the rules from
+ * <a href="https://tools.ietf.org/html/rfc2965#section-3.3">RFC 2965,
+ * section 3.3.1</a>.
+ * <p>
+ * The former rules are also used by libcurl internally.
+ * <p>
+ * The rules are as follows
+ *
+ * A string matches another domain string if at least one of the following
+ * conditions holds:
+ * <ul>
+ * <li>The domain string and the string are identical. (Note that both the
+ * domain string and the string will have been canonicalized to lower case
+ * at this point.)</li>
+ * <li>All of the following conditions hold
+ * <ul>
+ * <li>The domain string is a suffix of the string.</li>
+ * <li>The last character of the string that is not included in the domain
+ * string is a %x2E (".") character.</li>
+ * <li>The string is a host name (i.e., not an IP address).</li>
+ * </ul>
+ * </li>
+ * </ul>
+ *
+ * @param host
+ * the host to compare against the cookieDomain
+ * @param cookieDomain
+ * the domain to compare against
+ * @return {@code true} if they domain-match; {@code false} if not
+ *
+ * @see <a href= "https://tools.ietf.org/html/rfc6265#section-5.1.3">RFC
+ * 6265, section 5.1.3 (Domain Matching)</a>
+ * @see <a href=
+ * "https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8206092">JDK-8206092
+ * : HttpCookie.domainMatches() does not match to sub-sub-domain</a>
+ */
+ static boolean matchesCookieDomain(String host, String cookieDomain) {
+ cookieDomain = cookieDomain.toLowerCase(Locale.ROOT);
+ host = host.toLowerCase(Locale.ROOT);
+ if (host.equals(cookieDomain)) {
+ return true;
+ } else {
+ if (!host.endsWith(cookieDomain)) {
+ return false;
+ }
+ return host
+ .charAt(host.length() - cookieDomain.length() - 1) == '.';
+ }
+ }
+
+ /**
+ * The utility method to check whether a path is matching a cookie path
+ * domain or not. The rules are defined by
+ * <a href="https://tools.ietf.org/html/rfc6265#section-5.1.4">RFC 6265,
+ * section 5.1.4</a>:
+ *
+ * A request-path path-matches a given cookie-path if at least one of the
+ * following conditions holds:
+ * <ul>
+ * <li>The cookie-path and the request-path are identical.</li>
+ * <li>The cookie-path is a prefix of the request-path, and the last
+ * character of the cookie-path is %x2F ("/").</li>
+ * <li>The cookie-path is a prefix of the request-path, and the first
+ * character of the request-path that is not included in the cookie- path is
+ * a %x2F ("/") character.</li>
+ * </ul>
+ * @param path
+ * the path to check
+ * @param cookiePath
+ * the cookie's path
+ *
+ * @return {@code true} if they path-match; {@code false} if not
+ */
+ static boolean matchesCookiePath(String path, String cookiePath) {
+ if (cookiePath.equals(path)) {
+ return true;
+ }
+ if (!cookiePath.endsWith("/")) { //$NON-NLS-1$
+ cookiePath += "/"; //$NON-NLS-1$
+ }
+ return path.startsWith(cookiePath);
+ }
+
private boolean isSmartHttp(HttpConnection c, String service) {
final String expType = "application/x-" + service + "-advertisement"; //$NON-NLS-1$ //$NON-NLS-2$
final String actType = c.getContentType();
@@ -902,7 +1148,7 @@ public class TransportHttp extends HttpTransport implements WalkTransport,
JGitText.get().expectedGot, exp, act));
}
- while (pckIn.readString() != PacketLineIn.END) {
+ while (!PacketLineIn.isEnd(pckIn.readString())) {
// for now, ignore the remaining header lines
}
}
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 3ed9886c45..9278f42adf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -106,10 +106,8 @@ import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.AsyncRevObjectQueue;
import org.eclipse.jgit.revwalk.BitmapWalker;
-import org.eclipse.jgit.revwalk.BitmappedReachabilityChecker;
import org.eclipse.jgit.revwalk.DepthWalk;
import org.eclipse.jgit.revwalk.ObjectWalk;
-import org.eclipse.jgit.revwalk.PedestrianReachabilityChecker;
import org.eclipse.jgit.revwalk.ReachabilityChecker;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevFlag;
@@ -1231,7 +1229,7 @@ public class UploadPack {
/* EOF when awaiting command is fine */
return true;
}
- if (command == PacketLineIn.END) {
+ if (PacketLineIn.isEnd(command)) {
// A blank request is valid according
// to the protocol; do nothing in this
// case.
@@ -1602,7 +1600,7 @@ public class UploadPack {
throw eof;
}
- if (line == PacketLineIn.END) {
+ if (PacketLineIn.isEnd(line)) {
last = processHaveLines(peerHas, last, pckOut);
if (commonBase.isEmpty() || multiAck != MultiAck.OFF)
pckOut.writeString("NAK\n"); //$NON-NLS-1$
@@ -1909,9 +1907,8 @@ public class UploadPack {
}
// All wants are commits, we can use ReachabilityChecker
- ReachabilityChecker reachabilityChecker = repoHasBitmaps
- ? new BitmappedReachabilityChecker(walk)
- : new PedestrianReachabilityChecker(true, walk);
+ ReachabilityChecker reachabilityChecker = walk
+ .createReachabilityChecker();
List<RevCommit> starters = objectIdsToRevCommits(walk,
reachableFrom);
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 c0c24872b2..3efa66459c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -1516,6 +1516,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
ObjectId blobId = entry.getObjectId();
if (entry.getStage() > 0
&& entry.getStage() != DirCacheEntry.STAGE_2) {
+ blobId = null;
// Merge conflict: check ours (stage 2)
byte[] name = entry.getRawPath();
int i = 0;
@@ -1523,7 +1524,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
dirCache.next(1);
i++;
entry = dirCache.getDirCacheEntry();
- if (!Arrays.equals(name, entry.getRawPath())) {
+ if (entry == null
+ || !Arrays.equals(name, entry.getRawPath())) {
break;
}
if (entry.getStage() == DirCacheEntry.STAGE_2) {
@@ -1533,17 +1535,20 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
}
dirCache.back(i);
}
- try (ObjectReader reader = repository.newObjectReader()) {
- ObjectLoader loader = reader.open(blobId, Constants.OBJ_BLOB);
- try {
- return RawText.isCrLfText(loader.getCachedBytes());
- } catch (LargeObjectException e) {
- try (InputStream in = loader.openStream()) {
- return RawText.isCrLfText(in);
+ if (blobId != null) {
+ try (ObjectReader reader = repository.newObjectReader()) {
+ ObjectLoader loader = reader.open(blobId,
+ Constants.OBJ_BLOB);
+ try {
+ return RawText.isCrLfText(loader.getCachedBytes());
+ } catch (LargeObjectException e) {
+ try (InputStream in = loader.openStream()) {
+ return RawText.isCrLfText(in);
+ }
}
+ } catch (IOException e) {
+ // Ignore and return false below
}
- } catch (IOException e) {
- // Ignore and return false below
}
}
return false;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
index 716711e067..faef9fd0f4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
@@ -48,7 +48,8 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.nio.charset.Charset;
-import java.nio.file.AccessDeniedException;
+import java.nio.file.FileStore;
+import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -57,9 +58,11 @@ import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.errors.JGitInternalException;
@@ -84,7 +87,7 @@ public class FS_POSIX extends FS {
private static final int DEFAULT_UMASK = 0022;
private volatile int umask = -1;
- private volatile boolean supportsUnixNLink = true;
+ private static final Map<FileStore, Boolean> CAN_HARD_LINK = new ConcurrentHashMap<>();
private volatile AtomicFileCreation supportsAtomicCreateNewFile = AtomicFileCreation.UNDEFINED;
@@ -388,12 +391,18 @@ public class FS_POSIX extends FS {
if (!lock.createNewFile()) {
return false;
}
- if (supportsAtomicCreateNewFile() || !supportsUnixNLink) {
+ if (supportsAtomicCreateNewFile()) {
return true;
}
Path lockPath = lock.toPath();
Path link = null;
+ FileStore store = Files.getFileStore(lockPath);
try {
+ Boolean canLink = CAN_HARD_LINK.computeIfAbsent(store,
+ s -> Boolean.TRUE);
+ if (Boolean.FALSE.equals(canLink)) {
+ return true;
+ }
link = Files.createLink(
Paths.get(lock.getAbsolutePath() + ".lnk"), //$NON-NLS-1$
lockPath);
@@ -405,11 +414,11 @@ public class FS_POSIX extends FS {
nlink));
return false;
} else if (nlink < 2) {
- supportsUnixNLink = false;
+ CAN_HARD_LINK.put(store, Boolean.FALSE);
}
return true;
} catch (UnsupportedOperationException | IllegalArgumentException e) {
- supportsUnixNLink = false;
+ CAN_HARD_LINK.put(store, Boolean.FALSE);
return true;
} finally {
if (link != null) {
@@ -448,12 +457,18 @@ public class FS_POSIX extends FS {
if (!file.createNewFile()) {
return token(false, null);
}
- if (supportsAtomicCreateNewFile() || !supportsUnixNLink) {
+ if (supportsAtomicCreateNewFile()) {
return token(true, null);
}
Path link = null;
Path path = file.toPath();
+ FileStore store = Files.getFileStore(path);
try {
+ Boolean canLink = CAN_HARD_LINK.computeIfAbsent(store,
+ s -> Boolean.TRUE);
+ if (Boolean.FALSE.equals(canLink)) {
+ return token(true, null);
+ }
link = Files.createLink(Paths.get(uniqueLinkPath(file)), path);
Integer nlink = (Integer) (Files.getAttribute(path,
"unix:nlink")); //$NON-NLS-1$
@@ -462,12 +477,12 @@ public class FS_POSIX extends FS {
JGitText.get().failedAtomicFileCreation, path, nlink));
return token(false, link);
} else if (nlink.intValue() < 2) {
- supportsUnixNLink = false;
+ CAN_HARD_LINK.put(store, Boolean.FALSE);
}
return token(true, link);
} catch (UnsupportedOperationException | IllegalArgumentException
- | AccessDeniedException | SecurityException e) {
- supportsUnixNLink = false;
+ | FileSystemException | SecurityException e) {
+ CAN_HARD_LINK.put(store, Boolean.FALSE);
return token(true, link);
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
index 98797dc64f..3ccbd72806 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java
@@ -205,18 +205,21 @@ public class FS_Win32 extends FS {
@Override
protected File userHomeImpl() {
String home = SystemReader.getInstance().getenv("HOME"); //$NON-NLS-1$
- if (home != null)
+ if (home != null) {
return resolve(null, home);
+ }
String homeDrive = SystemReader.getInstance().getenv("HOMEDRIVE"); //$NON-NLS-1$
if (homeDrive != null) {
String homePath = SystemReader.getInstance().getenv("HOMEPATH"); //$NON-NLS-1$
- if (homePath != null)
+ if (homePath != null) {
return new File(homeDrive, homePath);
+ }
}
String homeShare = SystemReader.getInstance().getenv("HOMESHARE"); //$NON-NLS-1$
- if (homeShare != null)
+ if (homeShare != null) {
return new File(homeShare);
+ }
return super.userHomeImpl();
}
@@ -237,8 +240,9 @@ public class FS_Win32 extends FS {
/** {@inheritDoc} */
@Override
public boolean supportsSymlinks() {
- if (supportSymlinks == null)
+ if (supportSymlinks == null) {
detectSymlinkSupport();
+ }
return Boolean.TRUE.equals(supportSymlinks);
}
@@ -254,12 +258,13 @@ public class FS_Win32 extends FS {
| InternalError e) {
supportSymlinks = Boolean.FALSE;
} finally {
- if (tempFile != null)
+ if (tempFile != null) {
try {
FileUtils.delete(tempFile);
} catch (IOException e) {
throw new RuntimeException(e); // panic
}
+ }
}
}
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 54e4ee01fd..640670debc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/HttpSupport.java
@@ -170,6 +170,27 @@ public class HttpSupport {
public static final String HDR_WWW_AUTHENTICATE = "WWW-Authenticate"; //$NON-NLS-1$
/**
+ * The {@code Cookie} header.
+ *
+ * @since 5.4
+ */
+ public static final String HDR_COOKIE = "Cookie"; //$NON-NLS-1$
+
+ /**
+ * The {@code Set-Cookie} header.
+ *
+ * @since 5.4
+ */
+ public static final String HDR_SET_COOKIE = "Set-Cookie"; //$NON-NLS-1$
+
+ /**
+ * The {@code Set-Cookie2} header.
+ *
+ * @since 5.4
+ */
+ public static final String HDR_SET_COOKIE2 = "Set-Cookie2"; //$NON-NLS-1$
+
+ /**
* URL encode a value string into an output buffer.
*
* @param urlstr
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/LRUMap.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/LRUMap.java
new file mode 100644
index 0000000000..41c15363f8
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/LRUMap.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018, Konrad Windszus <konrad_w@gmx.de>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.util;
+
+import java.util.LinkedHashMap;
+
+/**
+ * Map with only up to n entries. If a new entry is added so that the map
+ * contains more than those n entries the least-recently used entry is removed
+ * from the map.
+ *
+ * @param <K>
+ * the type of keys maintained by this map
+ * @param <V>
+ * the type of mapped values
+ *
+ * @since 5.4
+ */
+public class LRUMap<K, V> extends LinkedHashMap<K, V> {
+
+ private static final long serialVersionUID = 4329609127403759486L;
+
+ private final int limit;
+
+ /**
+ * Constructs an empty map which may contain at most the given amount of
+ * entries.
+ *
+ * @param initialCapacity
+ * the initial capacity
+ * @param limit
+ * the number of entries the map should have at most
+ */
+ public LRUMap(int initialCapacity, int limit) {
+ super(initialCapacity, 0.75f, true);
+ this.limit = limit;
+ }
+
+ @Override
+ protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {
+ return size() > limit;
+ }
+}