Prevent alternates loop

When looping through alternates, prevent visiting the same object
directory twice. This could happen when the objects/info/alternates file
includes itself directly or indirectly via a another repo and its
alternates file.

Change-Id: I79bb3da099ebc3c262d2e6c61ed4578eb1aa3474
Signed-off-by: James Melvin <jmelvin@codeaurora.org>
Signed-off-by: Martin Fick <mfick@codeaurora.org>
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
This commit is contained in:
Martin Fick 2017-01-24 11:00:54 -07:00 committed by Matthias Sohn
parent c80d8c5901
commit e4714a2a5f
3 changed files with 221 additions and 63 deletions

View File

@ -47,8 +47,10 @@
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jgit.internal.storage.file.ObjectDirectory.AlternateHandle;
import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
import org.eclipse.jgit.internal.storage.pack.PackWriter;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
@ -160,43 +162,70 @@ private CachedObjectDirectory[] myAlternates() {
return alts;
}
private Set<AlternateHandle.Id> skipMe(Set<AlternateHandle.Id> skips) {
Set<AlternateHandle.Id> withMe = new HashSet<>();
if (skips != null) {
withMe.addAll(skips);
}
withMe.add(getAlternateId());
return withMe;
}
@Override
void resolve(Set<ObjectId> matches, AbbreviatedObjectId id)
throws IOException {
// In theory we could accelerate the loose object scan using our
// unpackedObjects map, but its not worth the huge code complexity.
// Scanning a single loose directory is fast enough, and this is
// unlikely to be called anyway.
//
wrapped.resolve(matches, id);
}
@Override
public boolean has(final AnyObjectId objectId) throws IOException {
if (unpackedObjects.contains(objectId))
return has(objectId, null);
}
private boolean has(final AnyObjectId objectId, Set<AlternateHandle.Id> skips)
throws IOException {
if (unpackedObjects.contains(objectId)) {
return true;
if (wrapped.hasPackedObject(objectId))
}
if (wrapped.hasPackedObject(objectId)) {
return true;
}
skips = skipMe(skips);
for (CachedObjectDirectory alt : myAlternates()) {
if (alt.has(objectId))
return true;
if (!skips.contains(alt.getAlternateId())) {
if (alt.has(objectId, skips)) {
return true;
}
}
}
return false;
}
@Override
ObjectLoader openObject(final WindowCursor curs,
final AnyObjectId objectId) throws IOException {
ObjectLoader openObject(final WindowCursor curs, final AnyObjectId objectId)
throws IOException {
return openObject(curs, objectId, null);
}
private ObjectLoader openObject(final WindowCursor curs,
final AnyObjectId objectId, Set<AlternateHandle.Id> skips)
throws IOException {
ObjectLoader ldr = openLooseObject(curs, objectId);
if (ldr != null)
if (ldr != null) {
return ldr;
}
ldr = wrapped.openPackedObject(curs, objectId);
if (ldr != null)
if (ldr != null) {
return ldr;
}
skips = skipMe(skips);
for (CachedObjectDirectory alt : myAlternates()) {
ldr = alt.openObject(curs, objectId);
if (ldr != null)
return ldr;
if (!skips.contains(alt.getAlternateId())) {
ldr = alt.openObject(curs, objectId, skips);
if (ldr != null) {
return ldr;
}
}
}
return null;
}
@ -260,4 +289,8 @@ private static class UnpackedObjectId extends ObjectIdOwnerMap.Entry {
super(id);
}
}
private AlternateHandle.Id getAlternateId() {
return wrapped.getAlternateId();
}
}

View File

@ -490,10 +490,28 @@ private File descriptionFile() {
*/
@Override
public Set<ObjectId> getAdditionalHaves() {
return getAdditionalHaves(null);
}
/**
* Objects known to exist but not expressed by {@link #getAllRefs()}.
* <p>
* When a repository borrows objects from another repository, it can
* advertise that it safely has that other repository's references, without
* exposing any other details about the other repository. This may help
* a client trying to push changes avoid pushing more than it needs to.
*
* @param skips
* Set of AlternateHandle Ids already seen
*
* @return unmodifiable collection of other known objects.
*/
private Set<ObjectId> getAdditionalHaves(Set<AlternateHandle.Id> skips) {
HashSet<ObjectId> r = new HashSet<>();
skips = objectDatabase.addMe(skips);
for (AlternateHandle d : objectDatabase.myAlternates()) {
if (d instanceof AlternateRepository) {
Repository repo;
if (d instanceof AlternateRepository && !skips.contains(d.getId())) {
FileRepository repo;
repo = ((AlternateRepository) d).repository;
for (Ref ref : repo.getAllRefs().values()) {
@ -502,7 +520,7 @@ public Set<ObjectId> getAdditionalHaves() {
if (ref.getPeeledObjectId() != null)
r.add(ref.getPeeledObjectId());
}
r.addAll(repo.getAdditionalHaves());
r.addAll(repo.getAdditionalHaves(skips));
}
}
return r;

View File

@ -64,6 +64,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
@ -117,6 +118,8 @@ public class ObjectDirectory extends FileObjectDatabase {
/** Maximum number of candidates offered as resolutions of abbreviation. */
private static final int RESOLVE_ABBREV_LIMIT = 256;
private final AlternateHandle handle = new AlternateHandle(this);
private final Config config;
private final File objects;
@ -294,26 +297,38 @@ public String toString() {
@Override
public boolean has(AnyObjectId objectId) {
return unpackedObjectCache.isUnpacked(objectId)
|| hasPackedInSelfOrAlternate(objectId)
|| hasLooseInSelfOrAlternate(objectId);
|| hasPackedInSelfOrAlternate(objectId, null)
|| hasLooseInSelfOrAlternate(objectId, null);
}
private boolean hasPackedInSelfOrAlternate(AnyObjectId objectId) {
if (hasPackedObject(objectId))
private boolean hasPackedInSelfOrAlternate(AnyObjectId objectId,
Set<AlternateHandle.Id> skips) {
if (hasPackedObject(objectId)) {
return true;
}
skips = addMe(skips);
for (AlternateHandle alt : myAlternates()) {
if (alt.db.hasPackedInSelfOrAlternate(objectId))
return true;
if (!skips.contains(alt.getId())) {
if (alt.db.hasPackedInSelfOrAlternate(objectId, skips)) {
return true;
}
}
}
return false;
}
private boolean hasLooseInSelfOrAlternate(AnyObjectId objectId) {
if (fileFor(objectId).exists())
private boolean hasLooseInSelfOrAlternate(AnyObjectId objectId,
Set<AlternateHandle.Id> skips) {
if (fileFor(objectId).exists()) {
return true;
}
skips = addMe(skips);
for (AlternateHandle alt : myAlternates()) {
if (alt.db.hasLooseInSelfOrAlternate(objectId))
return true;
if (!skips.contains(alt.getId())) {
if (alt.db.hasLooseInSelfOrAlternate(objectId, skips)) {
return true;
}
}
}
return false;
}
@ -340,6 +355,12 @@ boolean hasPackedObject(AnyObjectId objectId) {
@Override
void resolve(Set<ObjectId> matches, AbbreviatedObjectId id)
throws IOException {
resolve(matches, id, null);
}
private void resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
Set<AlternateHandle.Id> skips)
throws IOException {
// Go through the packs once. If we didn't find any resolutions
// scan for new packs and check once more.
int oldSize = matches.size();
@ -376,10 +397,14 @@ void resolve(Set<ObjectId> matches, AbbreviatedObjectId id)
}
}
skips = addMe(skips);
for (AlternateHandle alt : myAlternates()) {
alt.db.resolve(matches, id);
if (matches.size() > RESOLVE_ABBREV_LIMIT)
return;
if (!skips.contains(alt.getId())) {
alt.db.resolve(matches, id, skips);
if (matches.size() > RESOLVE_ABBREV_LIMIT) {
return;
}
}
}
}
@ -388,37 +413,50 @@ ObjectLoader openObject(WindowCursor curs, AnyObjectId objectId)
throws IOException {
if (unpackedObjectCache.isUnpacked(objectId)) {
ObjectLoader ldr = openLooseObject(curs, objectId);
if (ldr != null)
if (ldr != null) {
return ldr;
}
}
ObjectLoader ldr = openPackedFromSelfOrAlternate(curs, objectId);
if (ldr != null)
ObjectLoader ldr = openPackedFromSelfOrAlternate(curs, objectId, null);
if (ldr != null) {
return ldr;
return openLooseFromSelfOrAlternate(curs, objectId);
}
return openLooseFromSelfOrAlternate(curs, objectId, null);
}
private ObjectLoader openPackedFromSelfOrAlternate(WindowCursor curs,
AnyObjectId objectId) {
AnyObjectId objectId, Set<AlternateHandle.Id> skips) {
ObjectLoader ldr = openPackedObject(curs, objectId);
if (ldr != null)
if (ldr != null) {
return ldr;
}
skips = addMe(skips);
for (AlternateHandle alt : myAlternates()) {
ldr = alt.db.openPackedFromSelfOrAlternate(curs, objectId);
if (ldr != null)
return ldr;
if (!skips.contains(alt.getId())) {
ldr = alt.db.openPackedFromSelfOrAlternate(curs, objectId, skips);
if (ldr != null) {
return ldr;
}
}
}
return null;
}
private ObjectLoader openLooseFromSelfOrAlternate(WindowCursor curs,
AnyObjectId objectId) throws IOException {
AnyObjectId objectId, Set<AlternateHandle.Id> skips)
throws IOException {
ObjectLoader ldr = openLooseObject(curs, objectId);
if (ldr != null)
if (ldr != null) {
return ldr;
}
skips = addMe(skips);
for (AlternateHandle alt : myAlternates()) {
ldr = alt.db.openLooseFromSelfOrAlternate(curs, objectId);
if (ldr != null)
return ldr;
if (!skips.contains(alt.getId())) {
ldr = alt.db.openLooseFromSelfOrAlternate(curs, objectId, skips);
if (ldr != null) {
return ldr;
}
}
}
return null;
}
@ -469,37 +507,49 @@ long getObjectSize(WindowCursor curs, AnyObjectId id)
throws IOException {
if (unpackedObjectCache.isUnpacked(id)) {
long len = getLooseObjectSize(curs, id);
if (0 <= len)
if (0 <= len) {
return len;
}
}
long len = getPackedSizeFromSelfOrAlternate(curs, id);
if (0 <= len)
long len = getPackedSizeFromSelfOrAlternate(curs, id, null);
if (0 <= len) {
return len;
return getLooseSizeFromSelfOrAlternate(curs, id);
}
return getLooseSizeFromSelfOrAlternate(curs, id, null);
}
private long getPackedSizeFromSelfOrAlternate(WindowCursor curs,
AnyObjectId id) {
AnyObjectId id, Set<AlternateHandle.Id> skips) {
long len = getPackedObjectSize(curs, id);
if (0 <= len)
if (0 <= len) {
return len;
}
skips = addMe(skips);
for (AlternateHandle alt : myAlternates()) {
len = alt.db.getPackedSizeFromSelfOrAlternate(curs, id);
if (0 <= len)
return len;
if (!skips.contains(alt.getId())) {
len = alt.db.getPackedSizeFromSelfOrAlternate(curs, id, skips);
if (0 <= len) {
return len;
}
}
}
return -1;
}
private long getLooseSizeFromSelfOrAlternate(WindowCursor curs,
AnyObjectId id) throws IOException {
AnyObjectId id, Set<AlternateHandle.Id> skips) throws IOException {
long len = getLooseObjectSize(curs, id);
if (0 <= len)
if (0 <= len) {
return len;
}
skips = addMe(skips);
for (AlternateHandle alt : myAlternates()) {
len = alt.db.getLooseSizeFromSelfOrAlternate(curs, id);
if (0 <= len)
return len;
if (!skips.contains(alt.getId())) {
len = alt.db.getLooseSizeFromSelfOrAlternate(curs, id, skips);
if (0 <= len) {
return len;
}
}
}
return -1;
}
@ -546,7 +596,12 @@ private long getLooseObjectSize(WindowCursor curs, AnyObjectId id)
@Override
void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
WindowCursor curs) throws IOException {
WindowCursor curs) throws IOException {
selectObjectRepresentation(packer, otp, curs, null);
}
private void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
WindowCursor curs, Set<AlternateHandle.Id> skips) throws IOException {
PackList pList = packList.get();
SEARCH: for (;;) {
for (final PackFile p : pList.packs) {
@ -567,8 +622,12 @@ void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
break SEARCH;
}
for (AlternateHandle h : myAlternates())
h.db.selectObjectRepresentation(packer, otp, curs);
skips = addMe(skips);
for (AlternateHandle h : myAlternates()) {
if (!skips.contains(h.getId())) {
h.db.selectObjectRepresentation(packer, otp, curs, skips);
}
}
}
private void handlePackError(IOException e, PackFile p) {
@ -930,6 +989,14 @@ AlternateHandle[] myAlternates() {
return alt;
}
Set<AlternateHandle.Id> addMe(Set<AlternateHandle.Id> skips) {
if (skips == null) {
skips = new HashSet<>();
}
skips.add(handle.getId());
return skips;
}
private AlternateHandle[] loadAlternates() throws IOException {
final List<AlternateHandle> l = new ArrayList<>(4);
final BufferedReader br = open(alternatesFile);
@ -996,6 +1063,38 @@ private static final class PackList {
}
static class AlternateHandle {
static class Id {
String alternateId;
public Id(File object) {
try {
this.alternateId = object.getCanonicalPath();
} catch (Exception e) {
alternateId = null;
}
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o == null || !(o instanceof Id)) {
return false;
}
Id aId = (Id) o;
return Objects.equals(alternateId, aId.alternateId);
}
@Override
public int hashCode() {
if (alternateId == null) {
return 1;
}
return alternateId.hashCode();
}
}
final ObjectDirectory db;
AlternateHandle(ObjectDirectory db) {
@ -1005,6 +1104,10 @@ static class AlternateHandle {
void close() {
db.close();
}
public Id getId(){
return db.getAlternateId();
}
}
static class AlternateRepository extends AlternateHandle {
@ -1029,4 +1132,8 @@ public ObjectDatabase newCachedDatabase() {
CachedObjectDirectory newCachedFileObjectDatabase() {
return new CachedObjectDirectory(this);
}
AlternateHandle.Id getAlternateId() {
return new AlternateHandle.Id(objects);
}
}