ref = it.next();
+ next = ref.get();
+ if (next != null) {
+ return true;
}
+ it.remove();
+ }
+ return false;
+ }
- @Override
- public PackWriter next() {
- if (hasNext()) {
- PackWriter result = next;
- next = null;
- return result;
- }
- throw new NoSuchElementException();
- }
+ @Override
+ public PackWriter next() {
+ if (hasNext()) {
+ PackWriter result = next;
+ next = null;
+ return result;
+ }
+ throw new NoSuchElementException();
+ }
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
- };
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
}
};
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/LogCursor.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/LogCursor.java
index 9d410aef9..f78975af3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/LogCursor.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/LogCursor.java
@@ -12,6 +12,7 @@
import java.io.IOException;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.lib.ReflogEntry;
/**
@@ -45,8 +46,9 @@ public abstract class LogCursor implements AutoCloseable {
/**
* Get current log entry.
*
- * @return current log entry.
+ * @return current log entry. Maybe null if we are producing deletions.
*/
+ @Nullable
public abstract ReflogEntry getReflogEntry();
/** {@inheritDoc} */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java
index 1e7f3b1f0..9210ec172 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/AmazonS3.java
@@ -33,6 +33,7 @@
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
@@ -44,6 +45,8 @@
import java.util.SortedMap;
import java.util.TimeZone;
import java.util.TreeMap;
+import java.util.stream.Collectors;
+import java.time.Instant;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
@@ -288,6 +291,8 @@ public InputStream decrypt(URLConnection u) throws IOException {
*
* This method is primarily meant for obtaining a "recursive directory
* listing" rooted under the specified bucket and prefix location.
+ * It returns the keys sorted in reverse order of LastModified time
+ * (freshest keys first).
*
* @param bucket
* name of the bucket whose objects should be listed.
@@ -311,7 +316,10 @@ public List list(String bucket, String prefix)
do {
lp.list();
} while (lp.truncated);
- return lp.entries;
+
+ Comparator comparator = Comparator.comparingLong(KeyInfo::getLastModifiedSecs);
+ return lp.entries.stream().sorted(comparator.reversed())
+ .map(KeyInfo::getName).collect(Collectors.toList());
}
/**
@@ -620,8 +628,26 @@ static Properties properties(File authFile)
return p;
}
+ /**
+ * KeyInfo enables sorting of keys by lastModified time
+ */
+ private static final class KeyInfo {
+ private final String name;
+ private final long lastModifiedSecs;
+ public KeyInfo(String aname, long lsecs) {
+ name = aname;
+ lastModifiedSecs = lsecs;
+ }
+ public String getName() {
+ return name;
+ }
+ public long getLastModifiedSecs() {
+ return lastModifiedSecs;
+ }
+ }
+
private final class ListParser extends DefaultHandler {
- final List entries = new ArrayList<>();
+ final List entries = new ArrayList<>();
private final String bucket;
@@ -630,6 +656,8 @@ private final class ListParser extends DefaultHandler {
boolean truncated;
private StringBuilder data;
+ private String keyName;
+ private Instant keyLastModified;
ListParser(String bn, String p) {
bucket = bn;
@@ -641,7 +669,7 @@ void list() throws IOException {
if (prefix.length() > 0)
args.put("prefix", prefix); //$NON-NLS-1$
if (!entries.isEmpty())
- args.put("marker", prefix + entries.get(entries.size() - 1)); //$NON-NLS-1$
+ args.put("marker", prefix + entries.get(entries.size() - 1).getName()); //$NON-NLS-1$
for (int curAttempt = 0; curAttempt < maxAttempts; curAttempt++) {
final HttpURLConnection c = open("GET", bucket, "", args); //$NON-NLS-1$ //$NON-NLS-2$
@@ -650,6 +678,8 @@ void list() throws IOException {
case HttpURLConnection.HTTP_OK:
truncated = false;
data = null;
+ keyName = null;
+ keyLastModified = null;
final XMLReader xr;
try {
@@ -683,8 +713,13 @@ void list() throws IOException {
public void startElement(final String uri, final String name,
final String qName, final Attributes attributes)
throws SAXException {
- if ("Key".equals(name) || "IsTruncated".equals(name)) //$NON-NLS-1$ //$NON-NLS-2$
+ if ("Key".equals(name) || "IsTruncated".equals(name) || "LastModified".equals(name)) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
data = new StringBuilder();
+ }
+ if ("Contents".equals(name)) { //$NON-NLS-1$
+ keyName = null;
+ keyLastModified = null;
+ }
}
@Override
@@ -704,10 +739,16 @@ public void characters(char[] ch, int s, int n)
@Override
public void endElement(final String uri, final String name,
final String qName) throws SAXException {
- if ("Key".equals(name)) //$NON-NLS-1$
- entries.add(data.toString().substring(prefix.length()));
- else if ("IsTruncated".equals(name)) //$NON-NLS-1$
+ if ("Key".equals(name)) { //$NON-NLS-1$
+ keyName = data.toString().substring(prefix.length());
+ } else if ("IsTruncated".equals(name)) { //$NON-NLS-1$
truncated = StringUtils.equalsIgnoreCase("true", data.toString()); //$NON-NLS-1$
+ } else if ("LastModified".equals(name)) { //$NON-NLS-1$
+ keyLastModified = Instant.parse(data.toString());
+ } else if ("Contents".equals(name)) { //$NON-NLS-1$
+ entries.add(new KeyInfo(keyName, keyLastModified.getEpochSecond()));
+ }
+
data = null;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
index 94f36d285..784f56615 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java
@@ -23,6 +23,7 @@
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
@@ -241,11 +242,14 @@ WalkRemoteObjectDatabase openAlternate(String location)
@Override
Collection getPackNames() throws IOException {
+ // s3.list returns most recently modified packs first.
+ // These are the packs most likely to contain missing refs.
+ final List packList = s3.list(bucket, resolveKey("pack")); //$NON-NLS-1$
final HashSet have = new HashSet<>();
- have.addAll(s3.list(bucket, resolveKey("pack"))); //$NON-NLS-1$
+ have.addAll(packList);
final Collection packs = new ArrayList<>();
- for (String n : have) {
+ for (String n : packList) {
if (!n.startsWith("pack-") || !n.endsWith(".pack")) //$NON-NLS-1$ //$NON-NLS-2$
continue;
diff --git a/pom.xml b/pom.xml
index 06e2f308a..d0a5c83a1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -163,7 +163,7 @@
4.3.1
3.1.0
9.4.25.v20191220
- 0.14.1
+ 0.14.3
4.5.10
4.4.12
1.7.2
@@ -175,7 +175,7 @@
3.1.12.2
3.0.0
3.0.0
- 3.0.0-M3
+ 3.0.0-M4
${maven-surefire-plugin-version}
3.8.1
@@ -298,7 +298,7 @@
org.apache.maven.plugins
maven-pmd-plugin
- 3.12.0
+ 3.13.0
utf-8
100