Browse Source

Merge "Introduce PostUploadHook to replace UploadPackLogger"

stable-4.1
Shawn Pearce 10 years ago committed by Gerrit Code Review @ Eclipse.org
parent
commit
d399a56e66
  1. 9
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java
  2. 9
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java
  3. 8
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackDescription.java
  4. 163
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
  5. 462
      org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java
  6. 74
      org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java
  7. 90
      org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHookChain.java
  8. 57
      org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
  9. 3
      org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java
  10. 5
      org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java

9
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java

@ -72,6 +72,7 @@ import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.pack.PackConfig; import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.storage.pack.PackStatistics;
import org.eclipse.jgit.util.io.CountingOutputStream; import org.eclipse.jgit.util.io.CountingOutputStream;
/** Repack and garbage collect a repository. */ /** Repack and garbage collect a repository. */
@ -84,7 +85,7 @@ public class DfsGarbageCollector {
private final List<DfsPackDescription> newPackDesc; private final List<DfsPackDescription> newPackDesc;
private final List<PackWriter.Statistics> newPackStats; private final List<PackStatistics> newPackStats;
private final List<PackWriter.ObjectIdSet> newPackObj; private final List<PackWriter.ObjectIdSet> newPackObj;
@ -115,7 +116,7 @@ public class DfsGarbageCollector {
refdb = repo.getRefDatabase(); refdb = repo.getRefDatabase();
objdb = repo.getObjectDatabase(); objdb = repo.getObjectDatabase();
newPackDesc = new ArrayList<DfsPackDescription>(4); newPackDesc = new ArrayList<DfsPackDescription>(4);
newPackStats = new ArrayList<PackWriter.Statistics>(4); newPackStats = new ArrayList<PackStatistics>(4);
newPackObj = new ArrayList<PackWriter.ObjectIdSet>(4); newPackObj = new ArrayList<PackWriter.ObjectIdSet>(4);
packConfig = new PackConfig(repo); packConfig = new PackConfig(repo);
@ -258,7 +259,7 @@ public class DfsGarbageCollector {
} }
/** @return statistics corresponding to the {@link #getNewPacks()}. */ /** @return statistics corresponding to the {@link #getNewPacks()}. */
public List<PackWriter.Statistics> getNewPackStatistics() { public List<PackStatistics> getNewPackStatistics() {
return newPackStats; return newPackStats;
} }
@ -396,7 +397,7 @@ public class DfsGarbageCollector {
} }
}); });
PackWriter.Statistics stats = pw.getStatistics(); PackStatistics stats = pw.getStatistics();
pack.setPackStats(stats); pack.setPackStats(stats);
newPackStats.add(stats); newPackStats.add(stats);

9
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackCompactor.java

@ -67,6 +67,7 @@ import org.eclipse.jgit.revwalk.RevFlag;
import org.eclipse.jgit.revwalk.RevObject; import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.pack.PackConfig; import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.storage.pack.PackStatistics;
import org.eclipse.jgit.util.BlockList; import org.eclipse.jgit.util.BlockList;
import org.eclipse.jgit.util.io.CountingOutputStream; import org.eclipse.jgit.util.io.CountingOutputStream;
@ -94,7 +95,7 @@ public class DfsPackCompactor {
private final List<DfsPackDescription> newPacks; private final List<DfsPackDescription> newPacks;
private final List<PackWriter.Statistics> newStats; private final List<PackStatistics> newStats;
private int autoAddSize; private int autoAddSize;
@ -114,7 +115,7 @@ public class DfsPackCompactor {
srcPacks = new ArrayList<DfsPackFile>(); srcPacks = new ArrayList<DfsPackFile>();
exclude = new ArrayList<PackWriter.ObjectIdSet>(4); exclude = new ArrayList<PackWriter.ObjectIdSet>(4);
newPacks = new ArrayList<DfsPackDescription>(1); newPacks = new ArrayList<DfsPackDescription>(1);
newStats = new ArrayList<PackWriter.Statistics>(1); newStats = new ArrayList<PackStatistics>(1);
} }
/** /**
@ -231,7 +232,7 @@ public class DfsPackCompactor {
writePack(objdb, pack, pw, pm); writePack(objdb, pack, pw, pm);
writeIndex(objdb, pack, pw); writeIndex(objdb, pack, pw);
PackWriter.Statistics stats = pw.getStatistics(); PackStatistics stats = pw.getStatistics();
pw.close(); pw.close();
pw = null; pw = null;
@ -264,7 +265,7 @@ public class DfsPackCompactor {
} }
/** @return statistics corresponding to the {@link #getNewPacks()}. */ /** @return statistics corresponding to the {@link #getNewPacks()}. */
public List<PackWriter.Statistics> getNewPackStatistics() { public List<PackStatistics> getNewPackStatistics() {
return newStats; return newStats;
} }

8
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackDescription.java

@ -50,7 +50,7 @@ import java.util.Map;
import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource; import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
import org.eclipse.jgit.internal.storage.pack.PackExt; import org.eclipse.jgit.internal.storage.pack.PackExt;
import org.eclipse.jgit.internal.storage.pack.PackWriter; import org.eclipse.jgit.storage.pack.PackStatistics;
/** /**
* Description of a DFS stored pack/index file. * Description of a DFS stored pack/index file.
@ -75,7 +75,7 @@ public class DfsPackDescription implements Comparable<DfsPackDescription> {
private long deltaCount; private long deltaCount;
private PackWriter.Statistics stats; private PackStatistics stats;
private int extensions; private int extensions;
@ -225,11 +225,11 @@ public class DfsPackDescription implements Comparable<DfsPackDescription> {
* DfsGarbageCollector or DfsPackCompactor, and only when the pack * DfsGarbageCollector or DfsPackCompactor, and only when the pack
* is being committed to the repository. * is being committed to the repository.
*/ */
public PackWriter.Statistics getPackStats() { public PackStatistics getPackStats() {
return stats; return stats;
} }
DfsPackDescription setPackStats(PackWriter.Statistics stats) { DfsPackDescription setPackStats(PackStatistics stats) {
this.stats = stats; this.stats = stats;
setFileSize(PACK, stats.getTotalBytes()); setFileSize(PACK, stats.getTotalBytes());
setObjectCount(stats.getTotalObjects()); setObjectCount(stats.getTotalObjects());

163
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java

@ -114,6 +114,7 @@ import org.eclipse.jgit.revwalk.RevSort;
import org.eclipse.jgit.revwalk.RevTag; import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevTree; import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.storage.pack.PackConfig; import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.storage.pack.PackStatistics;
import org.eclipse.jgit.transport.ObjectCountCallback; import org.eclipse.jgit.transport.ObjectCountCallback;
import org.eclipse.jgit.transport.WriteAbortedException; import org.eclipse.jgit.transport.WriteAbortedException;
import org.eclipse.jgit.util.BlockList; import org.eclipse.jgit.util.BlockList;
@ -247,13 +248,13 @@ public class PackWriter implements AutoCloseable {
private final PackConfig config; private final PackConfig config;
private final Statistics stats; private final PackStatistics.Accumulator stats;
private final MutableState state; private final MutableState state;
private final WeakReference<PackWriter> selfRef; private final WeakReference<PackWriter> selfRef;
private Statistics.ObjectType typeStats; private PackStatistics.ObjectType.Accumulator typeStats;
private List<ObjectToPack> sortedByName; private List<ObjectToPack> sortedByName;
@ -356,7 +357,7 @@ public class PackWriter implements AutoCloseable {
deltaBaseAsOffset = config.isDeltaBaseAsOffset(); deltaBaseAsOffset = config.isDeltaBaseAsOffset();
reuseDeltas = config.isReuseDeltas(); reuseDeltas = config.isReuseDeltas();
reuseValidate = true; // be paranoid by default reuseValidate = true; // be paranoid by default
stats = new Statistics(); stats = new PackStatistics.Accumulator();
state = new MutableState(); state = new MutableState();
selfRef = new WeakReference<PackWriter>(this); selfRef = new WeakReference<PackWriter>(this);
instances.put(selfRef, Boolean.TRUE); instances.put(selfRef, Boolean.TRUE);
@ -981,7 +982,7 @@ public class PackWriter implements AutoCloseable {
writeObjects(out); writeObjects(out);
if (!edgeObjects.isEmpty() || !cachedPacks.isEmpty()) { if (!edgeObjects.isEmpty() || !cachedPacks.isEmpty()) {
for (Statistics.ObjectType typeStat : stats.objectTypes) { for (PackStatistics.ObjectType.Accumulator typeStat : stats.objectTypes) {
if (typeStat == null) if (typeStat == null)
continue; continue;
stats.thinPackBytes += typeStat.bytes; stats.thinPackBytes += typeStat.bytes;
@ -1002,7 +1003,7 @@ public class PackWriter implements AutoCloseable {
stats.timeWriting = System.currentTimeMillis() - writeStart; stats.timeWriting = System.currentTimeMillis() - writeStart;
stats.depth = depth; stats.depth = depth;
for (Statistics.ObjectType typeStat : stats.objectTypes) { for (PackStatistics.ObjectType.Accumulator typeStat : stats.objectTypes) {
if (typeStat == null) if (typeStat == null)
continue; continue;
typeStat.cntDeltas += typeStat.reusedDeltas; typeStat.cntDeltas += typeStat.reusedDeltas;
@ -1022,8 +1023,8 @@ public class PackWriter implements AutoCloseable {
* final pack stream. The object is only available to callers after * final pack stream. The object is only available to callers after
* {@link #writePack(ProgressMonitor, ProgressMonitor, OutputStream)} * {@link #writePack(ProgressMonitor, ProgressMonitor, OutputStream)}
*/ */
public Statistics getStatistics() { public PackStatistics getStatistics() {
return stats; return new PackStatistics(stats);
} }
/** @return snapshot of the current state of this PackWriter. */ /** @return snapshot of the current state of this PackWriter. */
@ -2028,28 +2029,36 @@ public class PackWriter implements AutoCloseable {
return true; return true;
} }
/** Summary of how PackWriter created the pack. */ /**
* Summary of how PackWriter created the pack.
*
* @deprecated Use {@link PackStatistics} instead.
*/
@Deprecated
public static class Statistics { public static class Statistics {
/** Statistics about a single class of object. */ /** Statistics about a single class of object. */
public static class ObjectType { public static class ObjectType {
long cntObjects; // All requests are forwarded to this object.
private PackStatistics.ObjectType objectType;
long cntDeltas;
long reusedObjects; /**
* Wraps an
long reusedDeltas; * {@link org.eclipse.jgit.storage.pack.PackStatistics.ObjectType}
* instance to maintain backwards compatibility with existing API.
long bytes; *
* @param type
long deltaBytes; * the wrapped instance
*/
public ObjectType(PackStatistics.ObjectType type) {
objectType = type;
}
/** /**
* @return total number of objects output. This total includes the * @return total number of objects output. This total includes the
* value of {@link #getDeltas()}. * value of {@link #getDeltas()}.
*/ */
public long getObjects() { public long getObjects() {
return cntObjects; return objectType.getObjects();
} }
/** /**
@ -2057,7 +2066,7 @@ public class PackWriter implements AutoCloseable {
* actual number of deltas if a cached pack was reused. * actual number of deltas if a cached pack was reused.
*/ */
public long getDeltas() { public long getDeltas() {
return cntDeltas; return objectType.getDeltas();
} }
/** /**
@ -2066,7 +2075,7 @@ public class PackWriter implements AutoCloseable {
* {@link #getReusedDeltas()}. * {@link #getReusedDeltas()}.
*/ */
public long getReusedObjects() { public long getReusedObjects() {
return reusedObjects; return objectType.getReusedObjects();
} }
/** /**
@ -2077,7 +2086,7 @@ public class PackWriter implements AutoCloseable {
* was reused. * was reused.
*/ */
public long getReusedDeltas() { public long getReusedDeltas() {
return reusedDeltas; return objectType.getReusedDeltas();
} }
/** /**
@ -2086,7 +2095,7 @@ public class PackWriter implements AutoCloseable {
* also includes all of {@link #getDeltaBytes()}. * also includes all of {@link #getDeltaBytes()}.
*/ */
public long getBytes() { public long getBytes() {
return bytes; return objectType.getBytes();
} }
/** /**
@ -2094,54 +2103,22 @@ public class PackWriter implements AutoCloseable {
* object headers for the delta objects. * object headers for the delta objects.
*/ */
public long getDeltaBytes() { public long getDeltaBytes() {
return deltaBytes; return objectType.getDeltaBytes();
} }
} }
Set<ObjectId> interestingObjects; // All requests are forwarded to this object.
private PackStatistics statistics;
Set<ObjectId> uninterestingObjects;
Collection<CachedPack> reusedPacks;
int depth;
int deltaSearchNonEdgeObjects;
int deltasFound;
long totalObjects;
long bitmapIndexMisses; /**
* Wraps a {@link PackStatistics} object to maintain backwards
long totalDeltas; * compatibility with existing API.
*
long reusedObjects; * @param stats
* the wrapped PackStatitics object
long reusedDeltas; */
public Statistics(PackStatistics stats) {
long totalBytes; statistics = stats;
long thinPackBytes;
long timeCounting;
long timeSearchingForReuse;
long timeSearchingForSizes;
long timeCompressing;
long timeWriting;
ObjectType[] objectTypes;
{
objectTypes = new ObjectType[5];
objectTypes[OBJ_COMMIT] = new ObjectType();
objectTypes[OBJ_TREE] = new ObjectType();
objectTypes[OBJ_BLOB] = new ObjectType();
objectTypes[OBJ_TAG] = new ObjectType();
} }
/** /**
@ -2150,7 +2127,7 @@ public class PackWriter implements AutoCloseable {
* test. * test.
*/ */
public Set<ObjectId> getInterestingObjects() { public Set<ObjectId> getInterestingObjects() {
return interestingObjects; return statistics.getInterestingObjects();
} }
/** /**
@ -2159,7 +2136,7 @@ public class PackWriter implements AutoCloseable {
* has these objects. * has these objects.
*/ */
public Set<ObjectId> getUninterestingObjects() { public Set<ObjectId> getUninterestingObjects() {
return uninterestingObjects; return statistics.getUninterestingObjects();
} }
/** /**
@ -2167,7 +2144,7 @@ public class PackWriter implements AutoCloseable {
* in the output, if any were selected for reuse. * in the output, if any were selected for reuse.
*/ */
public Collection<CachedPack> getReusedPacks() { public Collection<CachedPack> getReusedPacks() {
return reusedPacks; return statistics.getReusedPacks();
} }
/** /**
@ -2175,7 +2152,7 @@ public class PackWriter implements AutoCloseable {
* delta search process in order to find a potential delta base. * delta search process in order to find a potential delta base.
*/ */
public int getDeltaSearchNonEdgeObjects() { public int getDeltaSearchNonEdgeObjects() {
return deltaSearchNonEdgeObjects; return statistics.getDeltaSearchNonEdgeObjects();
} }
/** /**
@ -2184,7 +2161,7 @@ public class PackWriter implements AutoCloseable {
* {@link #getDeltaSearchNonEdgeObjects()}. * {@link #getDeltaSearchNonEdgeObjects()}.
*/ */
public int getDeltasFound() { public int getDeltasFound() {
return deltasFound; return statistics.getDeltasFound();
} }
/** /**
@ -2192,7 +2169,7 @@ public class PackWriter implements AutoCloseable {
* of {@link #getTotalDeltas()}. * of {@link #getTotalDeltas()}.
*/ */
public long getTotalObjects() { public long getTotalObjects() {
return totalObjects; return statistics.getTotalObjects();
} }
/** /**
@ -2203,7 +2180,7 @@ public class PackWriter implements AutoCloseable {
* @since 4.0 * @since 4.0
*/ */
public long getBitmapIndexMisses() { public long getBitmapIndexMisses() {
return bitmapIndexMisses; return statistics.getBitmapIndexMisses();
} }
/** /**
@ -2211,7 +2188,7 @@ public class PackWriter implements AutoCloseable {
* actual number of deltas if a cached pack was reused. * actual number of deltas if a cached pack was reused.
*/ */
public long getTotalDeltas() { public long getTotalDeltas() {
return totalDeltas; return statistics.getTotalDeltas();
} }
/** /**
@ -2219,7 +2196,7 @@ public class PackWriter implements AutoCloseable {
* the output. This count includes {@link #getReusedDeltas()}. * the output. This count includes {@link #getReusedDeltas()}.
*/ */
public long getReusedObjects() { public long getReusedObjects() {
return reusedObjects; return statistics.getReusedObjects();
} }
/** /**
@ -2229,7 +2206,7 @@ public class PackWriter implements AutoCloseable {
* actual number of reused deltas if a cached pack was reused. * actual number of reused deltas if a cached pack was reused.
*/ */
public long getReusedDeltas() { public long getReusedDeltas() {
return reusedDeltas; return statistics.getReusedDeltas();
} }
/** /**
@ -2237,7 +2214,7 @@ public class PackWriter implements AutoCloseable {
* header, trailer, thin pack, and reused cached pack(s). * header, trailer, thin pack, and reused cached pack(s).
*/ */
public long getTotalBytes() { public long getTotalBytes() {
return totalBytes; return statistics.getTotalBytes();
} }
/** /**
@ -2249,7 +2226,7 @@ public class PackWriter implements AutoCloseable {
* pack header or trailer. * pack header or trailer.
*/ */
public long getThinPackBytes() { public long getThinPackBytes() {
return thinPackBytes; return statistics.getThinPackBytes();
} }
/** /**
@ -2258,17 +2235,17 @@ public class PackWriter implements AutoCloseable {
* @return information about this type of object in the pack. * @return information about this type of object in the pack.
*/ */
public ObjectType byObjectType(int typeCode) { public ObjectType byObjectType(int typeCode) {
return objectTypes[typeCode]; return new ObjectType(statistics.byObjectType(typeCode));
} }
/** @return true if the resulting pack file was a shallow pack. */ /** @return true if the resulting pack file was a shallow pack. */
public boolean isShallow() { public boolean isShallow() {
return depth > 0; return statistics.isShallow();
} }
/** @return depth (in commits) the pack includes if shallow. */ /** @return depth (in commits) the pack includes if shallow. */
public int getDepth() { public int getDepth() {
return depth; return statistics.getDepth();
} }
/** /**
@ -2277,7 +2254,7 @@ public class PackWriter implements AutoCloseable {
* that occur when a cached pack is selected for reuse. * that occur when a cached pack is selected for reuse.
*/ */
public long getTimeCounting() { public long getTimeCounting() {
return timeCounting; return statistics.getTimeCounting();
} }
/** /**
@ -2286,7 +2263,7 @@ public class PackWriter implements AutoCloseable {
* can be assumed to already have. * can be assumed to already have.
*/ */
public long getTimeSearchingForReuse() { public long getTimeSearchingForReuse() {
return timeSearchingForReuse; return statistics.getTimeSearchingForReuse();
} }
/** /**
@ -2296,7 +2273,7 @@ public class PackWriter implements AutoCloseable {
* together and improve delta compression ratios. * together and improve delta compression ratios.
*/ */
public long getTimeSearchingForSizes() { public long getTimeSearchingForSizes() {
return timeSearchingForSizes; return statistics.getTimeSearchingForSizes();
} }
/** /**
@ -2306,7 +2283,7 @@ public class PackWriter implements AutoCloseable {
* delta compression. * delta compression.
*/ */
public long getTimeCompressing() { public long getTimeCompressing() {
return timeCompressing; return statistics.getTimeCompressing();
} }
/** /**
@ -2316,16 +2293,12 @@ public class PackWriter implements AutoCloseable {
* value. * value.
*/ */
public long getTimeWriting() { public long getTimeWriting() {
return timeWriting; return statistics.getTimeWriting();
} }
/** @return total time spent processing this pack. */ /** @return total time spent processing this pack. */
public long getTimeTotal() { public long getTimeTotal() {
return timeCounting return statistics.getTimeTotal();
+ timeSearchingForReuse
+ timeSearchingForSizes
+ timeCompressing
+ timeWriting;
} }
/** /**
@ -2333,14 +2306,12 @@ public class PackWriter implements AutoCloseable {
* {@code getTotalBytes() / (getTimeWriting() / 1000.0)}. * {@code getTotalBytes() / (getTimeWriting() / 1000.0)}.
*/ */
public double getTransferRate() { public double getTransferRate() {
return getTotalBytes() / (getTimeWriting() / 1000.0); return statistics.getTransferRate();
} }
/** @return formatted message string for display to clients. */ /** @return formatted message string for display to clients. */
public String getMessage() { public String getMessage() {
return MessageFormat.format(JGitText.get().packWriterStatistics, // return statistics.getMessage();
Long.valueOf(totalObjects), Long.valueOf(totalDeltas), //
Long.valueOf(reusedObjects), Long.valueOf(reusedDeltas));
} }
} }

462
org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackStatistics.java

@ -0,0 +1,462 @@
/*
* Copyright (C) 2015, Google Inc.
*
* 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.storage.pack;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
import static org.eclipse.jgit.lib.Constants.OBJ_TAG;
import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.pack.CachedPack;
import org.eclipse.jgit.lib.ObjectId;
/**
* Statistics about {@link org.eclipse.jgit.internal.storage.pack.PackWriter}
* pack creation.
*
* @since 4.1
*/
public class PackStatistics {
/**
* Statistics about a single type of object (commits, tags, trees and
* blobs).
*/
public static class ObjectType {
/**
* POJO for accumulating the ObjectType statistics.
*/
public static class Accumulator {
/** Count of objects of this type. */
public long cntObjects;
/** Count of deltas of this type. */
public long cntDeltas;
/** Count of reused objects of this type. */
public long reusedObjects;
/** Count of reused deltas of this type. */
public long reusedDeltas;
/** Count of bytes for all objects of this type. */
public long bytes;
/** Count of delta bytes for objects of this type. */
public long deltaBytes;
}
private ObjectType.Accumulator objectType;
/**
* Creates a new {@link ObjectType} object from the accumulator.
*
* @param accumulator
* the accumulator of the statistics
*/
public ObjectType(ObjectType.Accumulator accumulator) {
objectType = accumulator;
}
/**
* @return total number of objects output. This total includes the value
* of {@link #getDeltas()}.
*/
public long getObjects() {
return objectType.cntObjects;
}
/**
* @return total number of deltas output. This may be lower than the
* actual number of deltas if a cached pack was reused.
*/
public long getDeltas() {
return objectType.cntDeltas;
}
/**
* @return number of objects whose existing representation was reused in
* the output. This count includes {@link #getReusedDeltas()}.
*/
public long getReusedObjects() {
return objectType.reusedObjects;
}
/**
* @return number of deltas whose existing representation was reused in
* the output, as their base object was also output or was
* assumed present for a thin pack. This may be lower than the
* actual number of reused deltas if a cached pack was reused.
*/
public long getReusedDeltas() {
return objectType.reusedDeltas;
}
/**
* @return total number of bytes written. This size includes the object
* headers as well as the compressed data. This size also
* includes all of {@link #getDeltaBytes()}.
*/
public long getBytes() {
return objectType.bytes;
}
/**
* @return number of delta bytes written. This size includes the object
* headers for the delta objects.
*/
public long getDeltaBytes() {
return objectType.deltaBytes;
}
}
/**
* POJO for accumulating the statistics.
*/
public static class Accumulator {
/** The set of objects to be included in the pack. */
public Set<ObjectId> interestingObjects;
/** The set of objects to be excluded from the pack. */
public Set<ObjectId> uninterestingObjects;
/** The collection of reused packs in the upload. */
public List<CachedPack> reusedPacks;
/** If a shallow pack, the depth in commits. */
public int depth;
/**
* The count of objects in the pack that went through the delta search
* process in order to find a potential delta base.
*/
public int deltaSearchNonEdgeObjects;
/**
* The count of objects in the pack that went through delta base search
* and found a suitable base. This is a subset of
* deltaSearchNonEdgeObjects.
*/
public int deltasFound;
/** The total count of objects in the pack. */
public long totalObjects;
/**
* The count of objects that needed to be discovered through an object
* walk because they were not found in bitmap indices.
*/
public long bitmapIndexMisses;
/** The total count of deltas output. */
public long totalDeltas;
/** The count of reused objects in the pack. */
public long reusedObjects;
/** The count of reused deltas in the pack. */
public long reusedDeltas;
/** The count of total bytes in the pack. */
public long totalBytes;
/** The size of the thin pack in bytes, if a thin pack was generated. */
public long thinPackBytes;
/** Time in ms spent counting the objects that will go into the pack. */
public long timeCounting;
/** Time in ms spent searching for objects to reuse. */
public long timeSearchingForReuse;
/** Time in ms spent searching for sizes of objects. */
public long timeSearchingForSizes;
/** Time in ms spent compressing the pack. */
public long timeCompressing;
/** Time in ms spent writing the pack. */
public long timeWriting;
/**
* Statistics about each object type in the pack (commits, tags, trees
* and blobs.)
*/
public ObjectType.Accumulator[] objectTypes;
{
objectTypes = new ObjectType.Accumulator[5];
objectTypes[OBJ_COMMIT] = new ObjectType.Accumulator();
objectTypes[OBJ_TREE] = new ObjectType.Accumulator();
objectTypes[OBJ_BLOB] = new ObjectType.Accumulator();
objectTypes[OBJ_TAG] = new ObjectType.Accumulator();
}
}
private Accumulator statistics;
/**
* Creates a new {@link PackStatistics} object from the accumulator.
*
* @param accumulator
* the accumulator of the statistics
*/
public PackStatistics(Accumulator accumulator) {
// Note: PackStatistics directly serves up the collections in the
// accumulator.
statistics = accumulator;
}
/**
* @return unmodifiable collection of objects to be included in the pack.
* May be {@code null} if the pack was hand-crafted in a unit test.
*/
public Set<ObjectId> getInterestingObjects() {
return statistics.interestingObjects;
}
/**
* @return unmodifiable collection of objects that should be excluded from
* the pack, as the peer that will receive the pack already has
* these objects.
*/
public Set<ObjectId> getUninterestingObjects() {
return statistics.uninterestingObjects;
}
/**
* @return unmodifiable list of the cached packs that were reused in the
* output, if any were selected for reuse.
*/
public List<CachedPack> getReusedPacks() {
return statistics.reusedPacks;
}
/**
* @return number of objects in the output pack that went through the delta
* search process in order to find a potential delta base.
*/
public int getDeltaSearchNonEdgeObjects() {
return statistics.deltaSearchNonEdgeObjects;
}
/**
* @return number of objects in the output pack that went through delta base
* search and found a suitable base. This is a subset of
* {@link #getDeltaSearchNonEdgeObjects()}.
*/
public int getDeltasFound() {
return statistics.deltasFound;
}
/**
* @return total number of objects output. This total includes the value of
* {@link #getTotalDeltas()}.
*/
public long getTotalObjects() {
return statistics.totalObjects;
}
/**
* @return the count of objects that needed to be discovered through an
* object walk because they were not found in bitmap indices.
* Returns -1 if no bitmap indices were found.
*/
public long getBitmapIndexMisses() {
return statistics.bitmapIndexMisses;
}
/**
* @return total number of deltas output. This may be lower than the actual
* number of deltas if a cached pack was reused.
*/
public long getTotalDeltas() {
return statistics.totalDeltas;
}
/**
* @return number of objects whose existing representation was reused in the
* output. This count includes {@link #getReusedDeltas()}.
*/
public long getReusedObjects() {
return statistics.reusedObjects;
}
/**
* @return number of deltas whose existing representation was reused in the
* output, as their base object was also output or was assumed
* present for a thin pack. This may be lower than the actual number
* of reused deltas if a cached pack was reused.
*/
public long getReusedDeltas() {
return statistics.reusedDeltas;
}
/**
* @return total number of bytes written. This size includes the pack
* header, trailer, thin pack, and reused cached pack(s).
*/
public long getTotalBytes() {
return statistics.totalBytes;
}
/**
* @return size of the thin pack in bytes, if a thin pack was generated. A
* thin pack is created when the client already has objects and some
* deltas are created against those objects, or if a cached pack is
* being used and some deltas will reference objects in the cached
* pack. This size does not include the pack header or trailer.
*/
public long getThinPackBytes() {
return statistics.thinPackBytes;
}
/**
* @param typeCode
* object type code, e.g. OBJ_COMMIT or OBJ_TREE.
* @return information about this type of object in the pack.
*/
public ObjectType byObjectType(int typeCode) {
return new ObjectType(statistics.objectTypes[typeCode]);
}
/** @return true if the resulting pack file was a shallow pack. */
public boolean isShallow() {
return statistics.depth > 0;
}
/** @return depth (in commits) the pack includes if shallow. */
public int getDepth() {
return statistics.depth;
}
/**
* @return time in milliseconds spent enumerating the objects that need to
* be included in the output. This time includes any restarts that
* occur when a cached pack is selected for reuse.
*/
public long getTimeCounting() {
return statistics.timeCounting;
}
/**
* @return time in milliseconds spent matching existing representations
* against objects that will be transmitted, or that the client can
* be assumed to already have.
*/
public long getTimeSearchingForReuse() {
return statistics.timeSearchingForReuse;
}
/**
* @return time in milliseconds spent finding the sizes of all objects that
* will enter the delta compression search window. The sizes need to
* be known to better match similar objects together and improve
* delta compression ratios.
*/
public long getTimeSearchingForSizes() {
return statistics.timeSearchingForSizes;
}
/**
* @return time in milliseconds spent on delta compression. This is observed
* wall-clock time and does not accurately track CPU time used when
* multiple threads were used to perform the delta compression.
*/
public long getTimeCompressing() {
return statistics.timeCompressing;
}
/**
* @return time in milliseconds spent writing the pack output, from start of
* header until end of trailer. The transfer speed can be
* approximated by dividing {@link #getTotalBytes()} by this value.
*/
public long getTimeWriting() {
return statistics.timeWriting;
}
/** @return total time spent processing this pack. */
public long getTimeTotal() {
return statistics.timeCounting + statistics.timeSearchingForReuse
+ statistics.timeSearchingForSizes + statistics.timeCompressing
+ statistics.timeWriting;
}
/**
* @return get the average output speed in terms of bytes-per-second.
* {@code getTotalBytes() / (getTimeWriting() / 1000.0)}.
*/
public double getTransferRate() {
return getTotalBytes() / (getTimeWriting() / 1000.0);
}
/** @return formatted message string for display to clients. */
public String getMessage() {
return MessageFormat.format(JGitText.get().packWriterStatistics,
Long.valueOf(statistics.totalObjects),
Long.valueOf(statistics.totalDeltas),
Long.valueOf(statistics.reusedObjects),
Long.valueOf(statistics.reusedDeltas));
}
/** @return a map containing ObjectType statistics. */
public Map<Integer, ObjectType> getObjectTypes() {
HashMap<Integer, ObjectType> map = new HashMap<>();
map.put(Integer.valueOf(OBJ_BLOB), new ObjectType(
statistics.objectTypes[OBJ_BLOB]));
map.put(Integer.valueOf(OBJ_COMMIT), new ObjectType(
statistics.objectTypes[OBJ_COMMIT]));
map.put(Integer.valueOf(OBJ_TAG), new ObjectType(
statistics.objectTypes[OBJ_TAG]));
map.put(Integer.valueOf(OBJ_TREE), new ObjectType(
statistics.objectTypes[OBJ_TREE]));
return map;
}
}

74
org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHook.java

@ -0,0 +1,74 @@
/*
* Copyright (C) 2015, Google Inc.
*
* 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.transport;
import org.eclipse.jgit.internal.storage.pack.PackWriter;
import org.eclipse.jgit.storage.pack.PackStatistics;
/**
* Hook invoked by {@link UploadPack} after the pack has been uploaded.
* <p>
* Implementors of the interface are responsible for associating the current
* thread to a particular connection, if they need to also include connection
* information. One method is to use a {@link java.lang.ThreadLocal} to remember
* the connection information before invoking UploadPack.
*
* @since 4.1
*/
public interface PostUploadHook {
/** A simple no-op hook. */
public static final PostUploadHook NULL = new PostUploadHook() {
public void onPostUpload(PackStatistics stats) {
// Do nothing.
}
};
/**
* Notifies the hook that a pack has been sent.
*
* @param stats
* the statistics gathered by {@link PackWriter} for the uploaded
* pack
*/
public void onPostUpload(PackStatistics stats);
}

90
org.eclipse.jgit/src/org/eclipse/jgit/transport/PostUploadHookChain.java

@ -0,0 +1,90 @@
/*
* Copyright (C) 2015, Google Inc.
*
* 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.transport;
import java.util.List;
import org.eclipse.jgit.storage.pack.PackStatistics;
/**
* {@link PostUploadHook} that delegates to a list of other hooks.
* <p>
* Hooks are run in the order passed to the constructor.
*
* @since 4.1
*/
public class PostUploadHookChain implements PostUploadHook {
private final PostUploadHook[] hooks;
private final int count;
/**
* Create a new hook chaining the given hooks together.
*
* @param hooks
* hooks to execute, in order.
* @return a new chain of the given hooks.
*/
public static PostUploadHook newChain(List<? extends PostUploadHook> hooks) {
PostUploadHook[] newHooks = new PostUploadHook[hooks.size()];
int i = 0;
for (PostUploadHook hook : hooks)
if (hook != PostUploadHook.NULL)
newHooks[i++] = hook;
if (i == 0)
return PostUploadHook.NULL;
else if (i == 1)
return newHooks[0];
else
return new PostUploadHookChain(newHooks, i);
}
public void onPostUpload(PackStatistics stats) {
for (int i = 0; i < count; i++)
hooks[i].onPostUpload(stats);
}
private PostUploadHookChain(PostUploadHook[] hooks, int count) {
this.hooks = hooks;
this.count = count;
}
}

57
org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java

@ -94,6 +94,7 @@ import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter; import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter;
import org.eclipse.jgit.storage.pack.PackConfig; import org.eclipse.jgit.storage.pack.PackConfig;
import org.eclipse.jgit.storage.pack.PackStatistics;
import org.eclipse.jgit.transport.GitProtocolConstants.MultiAck; import org.eclipse.jgit.transport.GitProtocolConstants.MultiAck;
import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser; import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
import org.eclipse.jgit.util.io.InterruptTimer; import org.eclipse.jgit.util.io.InterruptTimer;
@ -253,6 +254,9 @@ public class UploadPack {
/** Hook handling the various upload phases. */ /** Hook handling the various upload phases. */
private PreUploadHook preUploadHook = PreUploadHook.NULL; private PreUploadHook preUploadHook = PreUploadHook.NULL;
/** Hook for taking post upload actions. */
private PostUploadHook postUploadHook = PostUploadHook.NULL;
/** Capabilities requested by the client. */ /** Capabilities requested by the client. */
private Set<String> options; private Set<String> options;
String userAgent; String userAgent;
@ -306,7 +310,7 @@ public class UploadPack {
private boolean noDone; private boolean noDone;
private PackWriter.Statistics statistics; private PackStatistics statistics;
private UploadPackLogger logger = UploadPackLogger.NULL; private UploadPackLogger logger = UploadPackLogger.NULL;
@ -519,7 +523,7 @@ public class UploadPack {
this.refFilter = refFilter != null ? refFilter : RefFilter.DEFAULT; this.refFilter = refFilter != null ? refFilter : RefFilter.DEFAULT;
} }
/** @return the configured upload hook. */ /** @return the configured pre upload hook. */
public PreUploadHook getPreUploadHook() { public PreUploadHook getPreUploadHook() {
return preUploadHook; return preUploadHook;
} }
@ -534,6 +538,25 @@ public class UploadPack {
preUploadHook = hook != null ? hook : PreUploadHook.NULL; preUploadHook = hook != null ? hook : PreUploadHook.NULL;
} }
/**
* @return the configured post upload hook.
* @since 4.1
*/
public PostUploadHook getPostUploadHook() {
return postUploadHook;
}
/**
* Set the hook for post upload actions (logging, repacking).
*
* @param hook
* the hook; if null no special actions are taken.
* @since 4.1
*/
public void setPostUploadHook(PostUploadHook hook) {
postUploadHook = hook != null ? hook : PostUploadHook.NULL;
}
/** /**
* Set the configuration used by the pack generator. * Set the configuration used by the pack generator.
* *
@ -562,7 +585,12 @@ public class UploadPack {
} }
} }
/** @return the configured logger. */ /**
* @return the configured logger.
*
* @deprecated Use {@link #getPreUploadHook()}.
*/
@Deprecated
public UploadPackLogger getLogger() { public UploadPackLogger getLogger() {
return logger; return logger;
} }
@ -572,7 +600,9 @@ public class UploadPack {
* *
* @param logger * @param logger
* the logger instance. If null, no logging occurs. * the logger instance. If null, no logging occurs.
* @deprecated Use {@link #setPreUploadHook(PreUploadHook)}.
*/ */
@Deprecated
public void setLogger(UploadPackLogger logger) { public void setLogger(UploadPackLogger logger) {
this.logger = logger; this.logger = logger;
} }
@ -654,8 +684,23 @@ public class UploadPack {
* was sent, such as during the negotation phase of a smart HTTP * was sent, such as during the negotation phase of a smart HTTP
* connection, or if the client was already up-to-date. * connection, or if the client was already up-to-date.
* @since 3.0 * @since 3.0
* @deprecated Use {@link #getStatistics()}.
*/ */
@Deprecated
public PackWriter.Statistics getPackStatistics() { public PackWriter.Statistics getPackStatistics() {
return statistics == null ? null
: new PackWriter.Statistics(statistics);
}
/**
* Get the PackWriter's statistics if a pack was sent to the client.
*
* @return statistics about pack output, if a pack was sent. Null if no pack
* was sent, such as during the negotation phase of a smart HTTP
* connection, or if the client was already up-to-date.
* @since 4.1
*/
public PackStatistics getStatistics() {
return statistics; return statistics;
} }
@ -1468,8 +1513,10 @@ public class UploadPack {
} finally { } finally {
statistics = pw.getStatistics(); statistics = pw.getStatistics();
if (statistics != null) if (statistics != null) {
logger.onPackStatistics(statistics); postUploadHook.onPostUpload(statistics);
logger.onPackStatistics(new PackWriter.Statistics(statistics));
}
pw.close(); pw.close();
} }

3
org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLogger.java

@ -52,7 +52,10 @@ import org.eclipse.jgit.internal.storage.pack.PackWriter;
* thread to a particular connection, if they need to also include connection * thread to a particular connection, if they need to also include connection
* information. One method is to use a {@link java.lang.ThreadLocal} to remember * information. One method is to use a {@link java.lang.ThreadLocal} to remember
* the connection information before invoking UploadPack. * the connection information before invoking UploadPack.
*
* @deprecated use {@link PostUploadHook} instead
*/ */
@Deprecated
public interface UploadPackLogger { public interface UploadPackLogger {
/** A simple no-op logger. */ /** A simple no-op logger. */
public static final UploadPackLogger NULL = new UploadPackLogger() { public static final UploadPackLogger NULL = new UploadPackLogger() {

5
org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPackLoggerChain.java

@ -48,10 +48,13 @@ import java.util.List;
import org.eclipse.jgit.internal.storage.pack.PackWriter; import org.eclipse.jgit.internal.storage.pack.PackWriter;
/** /**
* {@link UploadPackLogger} that delegates to a list of other loggers. * UploadPackLogger that delegates to a list of other loggers.
* <p> * <p>
* loggers are run in the order passed to the constructor. * loggers are run in the order passed to the constructor.
*
* @deprecated Use {@link PostUploadHookChain} instead.
*/ */
@Deprecated
public class UploadPackLoggerChain implements UploadPackLogger { public class UploadPackLoggerChain implements UploadPackLogger {
private final UploadPackLogger[] loggers; private final UploadPackLogger[] loggers;
private final int count; private final int count;

Loading…
Cancel
Save