This increases type-safety and is ground work for support of the "tree:<depth>" filter. Change-Id: Id19eacdcdaddb9132064c642f6d554b1060efe9f Signed-off-by: Matthew DeVore <matvore@gmail.com>tags/v5.4.0.201905081430-m2
assertThat(request.getWantIds(), | assertThat(request.getWantIds(), | ||||
hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e", | hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e", | ||||
"f900c8326a43303685c46b279b9f70411bff1a4b")); | "f900c8326a43303685c46b279b9f70411bff1a4b")); | ||||
assertEquals(13000, request.getFilterBlobLimit()); | |||||
assertEquals(13000, request.getFilterSpec().getBlobLimit()); | |||||
} | } | ||||
} | } |
ProtocolV2Parser parser = new ProtocolV2Parser( | ProtocolV2Parser parser = new ProtocolV2Parser( | ||||
ConfigBuilder.start().allowFilter().done()); | ConfigBuilder.start().allowFilter().done()); | ||||
FetchV2Request request = parser.parseFetchRequest(pckIn); | FetchV2Request request = parser.parseFetchRequest(pckIn); | ||||
assertEquals(0, request.getFilterBlobLimit()); | |||||
assertEquals(0, request.getFilterSpec().getBlobLimit()); | |||||
} | } | ||||
@Test | @Test | ||||
ProtocolV2Parser parser = new ProtocolV2Parser( | ProtocolV2Parser parser = new ProtocolV2Parser( | ||||
ConfigBuilder.start().allowFilter().done()); | ConfigBuilder.start().allowFilter().done()); | ||||
FetchV2Request request = parser.parseFetchRequest(pckIn); | FetchV2Request request = parser.parseFetchRequest(pckIn); | ||||
assertEquals(15, request.getFilterBlobLimit()); | |||||
assertEquals(15, request.getFilterSpec().getBlobLimit()); | |||||
} | } | ||||
@Test | @Test |
final Set<ObjectId> clientShallowCommits; | final Set<ObjectId> clientShallowCommits; | ||||
final long filterBlobLimit; | |||||
final FilterSpec filterSpec; | |||||
final Set<String> clientCapabilities; | final Set<String> clientCapabilities; | ||||
* how deep to go in the tree | * how deep to go in the tree | ||||
* @param clientShallowCommits | * @param clientShallowCommits | ||||
* commits the client has without history | * commits the client has without history | ||||
* @param filterBlobLimit | |||||
* to exclude blobs on certain conditions | |||||
* @param filterSpec | |||||
* the filter spec | |||||
* @param clientCapabilities | * @param clientCapabilities | ||||
* capabilities sent in the request | * capabilities sent in the request | ||||
* @param deepenNotRefs | * @param deepenNotRefs | ||||
* agent as reported by the client in the request body | * agent as reported by the client in the request body | ||||
*/ | */ | ||||
FetchRequest(@NonNull Set<ObjectId> wantIds, int depth, | FetchRequest(@NonNull Set<ObjectId> wantIds, int depth, | ||||
@NonNull Set<ObjectId> clientShallowCommits, long filterBlobLimit, | |||||
@NonNull Set<ObjectId> clientShallowCommits, | |||||
@NonNull FilterSpec filterSpec, | |||||
@NonNull Set<String> clientCapabilities, int deepenSince, | @NonNull Set<String> clientCapabilities, int deepenSince, | ||||
@NonNull List<String> deepenNotRefs, @Nullable String agent) { | @NonNull List<String> deepenNotRefs, @Nullable String agent) { | ||||
this.wantIds = requireNonNull(wantIds); | this.wantIds = requireNonNull(wantIds); | ||||
this.depth = depth; | this.depth = depth; | ||||
this.clientShallowCommits = requireNonNull(clientShallowCommits); | this.clientShallowCommits = requireNonNull(clientShallowCommits); | ||||
this.filterBlobLimit = filterBlobLimit; | |||||
this.filterSpec = requireNonNull(filterSpec); | |||||
this.clientCapabilities = requireNonNull(clientCapabilities); | this.clientCapabilities = requireNonNull(clientCapabilities); | ||||
this.deepenSince = deepenSince; | this.deepenSince = deepenSince; | ||||
this.deepenNotRefs = requireNonNull(deepenNotRefs); | this.deepenNotRefs = requireNonNull(deepenNotRefs); | ||||
} | } | ||||
/** | /** | ||||
* @return the blob limit set in a "filter" line (-1 if not set) | |||||
* @return the filter spec given in a "filter" line | |||||
*/ | */ | ||||
long getFilterBlobLimit() { | |||||
return filterBlobLimit; | |||||
@NonNull | |||||
FilterSpec getFilterSpec() { | |||||
return filterSpec; | |||||
} | } | ||||
/** | /** | ||||
String getAgent() { | String getAgent() { | ||||
return agent; | return agent; | ||||
} | } | ||||
} | |||||
} |
*/ | */ | ||||
package org.eclipse.jgit.transport; | package org.eclipse.jgit.transport; | ||||
import static java.util.Objects.requireNonNull; | |||||
import java.util.Collection; | import java.util.Collection; | ||||
import java.util.Collections; | import java.util.Collections; | ||||
import java.util.HashSet; | import java.util.HashSet; | ||||
final class FetchV0Request extends FetchRequest { | final class FetchV0Request extends FetchRequest { | ||||
FetchV0Request(@NonNull Set<ObjectId> wantIds, int depth, | FetchV0Request(@NonNull Set<ObjectId> wantIds, int depth, | ||||
@NonNull Set<ObjectId> clientShallowCommits, long filterBlobLimit, | |||||
@NonNull Set<ObjectId> clientShallowCommits, | |||||
@NonNull FilterSpec filterSpec, | |||||
@NonNull Set<String> clientCapabilities, @Nullable String agent) { | @NonNull Set<String> clientCapabilities, @Nullable String agent) { | ||||
super(wantIds, depth, clientShallowCommits, filterBlobLimit, | |||||
clientCapabilities, 0, Collections.emptyList(), agent); | |||||
super(wantIds, depth, clientShallowCommits, filterSpec, | |||||
clientCapabilities, 0, Collections.emptyList(), agent); | |||||
} | } | ||||
static final class Builder { | static final class Builder { | ||||
final Set<ObjectId> clientShallowCommits = new HashSet<>(); | final Set<ObjectId> clientShallowCommits = new HashSet<>(); | ||||
long filterBlobLimit = -1; | |||||
FilterSpec filterSpec = FilterSpec.NO_FILTER; | |||||
final Set<String> clientCaps = new HashSet<>(); | final Set<String> clientCaps = new HashSet<>(); | ||||
} | } | ||||
/** | /** | ||||
* @param filterBlobLim | |||||
* blob limit set in a "filter" line | |||||
* @param filter | |||||
* the filter set in a filter line | |||||
* @return this builder | * @return this builder | ||||
*/ | */ | ||||
Builder setFilterBlobLimit(long filterBlobLim) { | |||||
filterBlobLimit = filterBlobLim; | |||||
Builder setFilterSpec(@NonNull FilterSpec filter) { | |||||
filterSpec = requireNonNull(filter); | |||||
return this; | return this; | ||||
} | } | ||||
FetchV0Request build() { | FetchV0Request build() { | ||||
return new FetchV0Request(wantIds, depth, clientShallowCommits, | return new FetchV0Request(wantIds, depth, clientShallowCommits, | ||||
filterBlobLimit, clientCaps, agent); | |||||
filterSpec, clientCaps, agent); | |||||
} | } | ||||
} | } |
@NonNull Set<ObjectId> wantIds, | @NonNull Set<ObjectId> wantIds, | ||||
@NonNull Set<ObjectId> clientShallowCommits, int deepenSince, | @NonNull Set<ObjectId> clientShallowCommits, int deepenSince, | ||||
@NonNull List<String> deepenNotRefs, int depth, | @NonNull List<String> deepenNotRefs, int depth, | ||||
long filterBlobLimit, | |||||
@NonNull FilterSpec filterSpec, | |||||
boolean doneReceived, @NonNull Set<String> clientCapabilities, | boolean doneReceived, @NonNull Set<String> clientCapabilities, | ||||
@Nullable String agent, @NonNull List<String> serverOptions) { | @Nullable String agent, @NonNull List<String> serverOptions) { | ||||
super(wantIds, depth, clientShallowCommits, filterBlobLimit, | |||||
clientCapabilities, deepenSince, deepenNotRefs, agent); | |||||
super(wantIds, depth, clientShallowCommits, filterSpec, | |||||
clientCapabilities, deepenSince, | |||||
deepenNotRefs, agent); | |||||
this.peerHas = requireNonNull(peerHas); | this.peerHas = requireNonNull(peerHas); | ||||
this.wantedRefs = requireNonNull(wantedRefs); | this.wantedRefs = requireNonNull(wantedRefs); | ||||
this.doneReceived = doneReceived; | this.doneReceived = doneReceived; | ||||
int deepenSince; | int deepenSince; | ||||
long filterBlobLimit = -1; | |||||
FilterSpec filterSpec = FilterSpec.NO_FILTER; | |||||
boolean doneReceived; | boolean doneReceived; | ||||
} | } | ||||
/** | /** | ||||
* @param filterBlobLim | |||||
* set in a "filter" line | |||||
* @param filter | |||||
* spec set in a "filter" line | |||||
* @return this builder | * @return this builder | ||||
*/ | */ | ||||
Builder setFilterBlobLimit(long filterBlobLim) { | |||||
filterBlobLimit = filterBlobLim; | |||||
Builder setFilterSpec(@NonNull FilterSpec filter) { | |||||
filterSpec = requireNonNull(filter); | |||||
return this; | return this; | ||||
} | } | ||||
FetchV2Request build() { | FetchV2Request build() { | ||||
return new FetchV2Request(peerHas, wantedRefs, wantIds, | return new FetchV2Request(peerHas, wantedRefs, wantIds, | ||||
clientShallowCommits, deepenSince, deepenNotRefs, | clientShallowCommits, deepenSince, deepenNotRefs, | ||||
depth, filterBlobLimit, doneReceived, clientCapabilities, | |||||
depth, filterSpec, doneReceived, clientCapabilities, | |||||
agent, Collections.unmodifiableList(serverOptions)); | agent, Collections.unmodifiableList(serverOptions)); | ||||
} | } | ||||
} | } |
import org.eclipse.jgit.internal.JGitText; | import org.eclipse.jgit.internal.JGitText; | ||||
/** | /** | ||||
* Utility code for dealing with filter lines. | |||||
* Represents either a filter specified in a protocol "filter" line, or a | |||||
* placeholder to indicate no filtering. | |||||
* | |||||
* @since 5.4 | |||||
*/ | */ | ||||
final class FilterSpec { | |||||
public final class FilterSpec { | |||||
private final long blobLimit; | |||||
private FilterSpec() {} | |||||
private FilterSpec(long blobLimit) { | |||||
this.blobLimit = blobLimit; | |||||
} | |||||
/* | |||||
/** | |||||
* Process the content of "filter" line from the protocol. It has a shape | * Process the content of "filter" line from the protocol. It has a shape | ||||
* like "blob:none" or "blob:limit=N", with limit a positive number. | * like "blob:none" or "blob:limit=N", with limit a positive number. | ||||
* | * | ||||
* @param filterLine | * @param filterLine | ||||
* the content of the "filter" line in the protocol | * the content of the "filter" line in the protocol | ||||
* @return N, the limit, defaulting to 0 if "none" | |||||
* @return a FilterSpec representing the given filter | |||||
* @throws PackProtocolException | * @throws PackProtocolException | ||||
* invalid filter because due to unrecognized format or | * invalid filter because due to unrecognized format or | ||||
* negative/non-numeric filter. | * negative/non-numeric filter. | ||||
*/ | */ | ||||
static long parseFilterLine(String filterLine) | |||||
public static FilterSpec fromFilterLine(String filterLine) | |||||
throws PackProtocolException { | throws PackProtocolException { | ||||
long blobLimit = -1; | long blobLimit = -1; | ||||
JGitText.get().invalidFilter, filterLine)); | JGitText.get().invalidFilter, filterLine)); | ||||
} | } | ||||
return new FilterSpec(blobLimit); | |||||
} | |||||
/** | |||||
* @param blobLimit | |||||
* the blob limit in a "blob:[limit]" or "blob:none" filter line | |||||
* @return a filter spec which filters blobs above a certain size | |||||
*/ | |||||
static FilterSpec withBlobLimit(long blobLimit) { | |||||
if (blobLimit < 0) { | |||||
throw new IllegalArgumentException( | |||||
"blobLimit cannot be negative: " + blobLimit); //$NON-NLS-1$ | |||||
} | |||||
return new FilterSpec(blobLimit); | |||||
} | |||||
/** | |||||
* A placeholder that indicates no filtering. | |||||
*/ | |||||
public static final FilterSpec NO_FILTER = new FilterSpec(-1); | |||||
/** | |||||
* @return -1 if this filter does not filter blobs based on size, or a | |||||
* non-negative integer representing the max size of blobs to allow | |||||
*/ | |||||
public long getBlobLimit() { | |||||
return blobLimit; | return blobLimit; | ||||
} | } | ||||
/** | |||||
* @return true if this filter doesn't filter out anything | |||||
*/ | |||||
public boolean isNoOp() { | |||||
return blobLimit == -1; | |||||
} | |||||
} | } |
} | } | ||||
filterReceived = true; | filterReceived = true; | ||||
reqBuilder.setFilterBlobLimit(FilterSpec.parseFilterLine(arg)); | |||||
reqBuilder.setFilterSpec(FilterSpec.fromFilterLine(arg)); | |||||
continue; | continue; | ||||
} | } | ||||
JGitText.get().tooManyFilters); | JGitText.get().tooManyFilters); | ||||
} | } | ||||
filterReceived = true; | filterReceived = true; | ||||
reqBuilder.setFilterBlobLimit(FilterSpec.parseFilterLine( | |||||
reqBuilder.setFilterSpec(FilterSpec.fromFilterLine( | |||||
line.substring(OPTION_FILTER.length() + 1))); | line.substring(OPTION_FILTER.length() + 1))); | ||||
} else { | } else { | ||||
throw new PackProtocolException(MessageFormat | throw new PackProtocolException(MessageFormat |
if (currentRequest == null) { | if (currentRequest == null) { | ||||
throw new RequestNotYetReadException(); | throw new RequestNotYetReadException(); | ||||
} | } | ||||
return currentRequest.getFilterBlobLimit(); | |||||
return currentRequest.getFilterSpec().getBlobLimit(); | |||||
} | } | ||||
/** | /** | ||||
accumulator); | accumulator); | ||||
try { | try { | ||||
pw.setIndexDisabled(true); | pw.setIndexDisabled(true); | ||||
if (req.getFilterBlobLimit() >= 0) { | |||||
pw.setFilterBlobLimit(req.getFilterBlobLimit()); | |||||
pw.setUseCachedPacks(false); | |||||
} else { | |||||
if (req.getFilterSpec().isNoOp()) { | |||||
pw.setUseCachedPacks(true); | pw.setUseCachedPacks(true); | ||||
} else { | |||||
pw.setFilterBlobLimit(req.getFilterSpec().getBlobLimit()); | |||||
pw.setUseCachedPacks(false); | |||||
} | } | ||||
pw.setUseBitmaps( | pw.setUseBitmaps( | ||||
req.getDepth() == 0 | req.getDepth() == 0 |