UploadPackLogger is incorrectly named--it can be used to trigger any post upload action, such as GC/compaction. This change introduces PostUploadHook/PostUploadHookChain to replace UploadPackLogger/UploadPackLoggerChain and deprecates the latter. It also introduces PackStatistics as a replacement for PackWriter.Statistics, since the latter is not public API. It changes PackWriter to use PackStatistics and reimplements PackWriter.Statistics to delegate to PackStatistics. Change-Id: Ic51df1613e471f568ffee25ae67e118425b38986 Signed-off-by: Terry Parker <tparker@google.com>tags/v4.1.0.201509280440-r
@@ -72,6 +72,7 @@ import org.eclipse.jgit.lib.ProgressMonitor; | |||
import org.eclipse.jgit.lib.Ref; | |||
import org.eclipse.jgit.revwalk.RevWalk; | |||
import org.eclipse.jgit.storage.pack.PackConfig; | |||
import org.eclipse.jgit.storage.pack.PackStatistics; | |||
import org.eclipse.jgit.util.io.CountingOutputStream; | |||
/** Repack and garbage collect a repository. */ | |||
@@ -84,7 +85,7 @@ public class DfsGarbageCollector { | |||
private final List<DfsPackDescription> newPackDesc; | |||
private final List<PackWriter.Statistics> newPackStats; | |||
private final List<PackStatistics> newPackStats; | |||
private final List<PackWriter.ObjectIdSet> newPackObj; | |||
@@ -115,7 +116,7 @@ public class DfsGarbageCollector { | |||
refdb = repo.getRefDatabase(); | |||
objdb = repo.getObjectDatabase(); | |||
newPackDesc = new ArrayList<DfsPackDescription>(4); | |||
newPackStats = new ArrayList<PackWriter.Statistics>(4); | |||
newPackStats = new ArrayList<PackStatistics>(4); | |||
newPackObj = new ArrayList<PackWriter.ObjectIdSet>(4); | |||
packConfig = new PackConfig(repo); | |||
@@ -258,7 +259,7 @@ public class DfsGarbageCollector { | |||
} | |||
/** @return statistics corresponding to the {@link #getNewPacks()}. */ | |||
public List<PackWriter.Statistics> getNewPackStatistics() { | |||
public List<PackStatistics> getNewPackStatistics() { | |||
return newPackStats; | |||
} | |||
@@ -396,7 +397,7 @@ public class DfsGarbageCollector { | |||
} | |||
}); | |||
PackWriter.Statistics stats = pw.getStatistics(); | |||
PackStatistics stats = pw.getStatistics(); | |||
pack.setPackStats(stats); | |||
newPackStats.add(stats); | |||
@@ -67,6 +67,7 @@ import org.eclipse.jgit.revwalk.RevFlag; | |||
import org.eclipse.jgit.revwalk.RevObject; | |||
import org.eclipse.jgit.revwalk.RevWalk; | |||
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.io.CountingOutputStream; | |||
@@ -94,7 +95,7 @@ public class DfsPackCompactor { | |||
private final List<DfsPackDescription> newPacks; | |||
private final List<PackWriter.Statistics> newStats; | |||
private final List<PackStatistics> newStats; | |||
private int autoAddSize; | |||
@@ -114,7 +115,7 @@ public class DfsPackCompactor { | |||
srcPacks = new ArrayList<DfsPackFile>(); | |||
exclude = new ArrayList<PackWriter.ObjectIdSet>(4); | |||
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); | |||
writeIndex(objdb, pack, pw); | |||
PackWriter.Statistics stats = pw.getStatistics(); | |||
PackStatistics stats = pw.getStatistics(); | |||
pw.close(); | |||
pw = null; | |||
@@ -264,7 +265,7 @@ public class DfsPackCompactor { | |||
} | |||
/** @return statistics corresponding to the {@link #getNewPacks()}. */ | |||
public List<PackWriter.Statistics> getNewPackStatistics() { | |||
public List<PackStatistics> getNewPackStatistics() { | |||
return newStats; | |||
} | |||
@@ -50,7 +50,7 @@ import java.util.Map; | |||
import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource; | |||
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. | |||
@@ -75,7 +75,7 @@ public class DfsPackDescription implements Comparable<DfsPackDescription> { | |||
private long deltaCount; | |||
private PackWriter.Statistics stats; | |||
private PackStatistics stats; | |||
private int extensions; | |||
@@ -225,11 +225,11 @@ public class DfsPackDescription implements Comparable<DfsPackDescription> { | |||
* DfsGarbageCollector or DfsPackCompactor, and only when the pack | |||
* is being committed to the repository. | |||
*/ | |||
public PackWriter.Statistics getPackStats() { | |||
public PackStatistics getPackStats() { | |||
return stats; | |||
} | |||
DfsPackDescription setPackStats(PackWriter.Statistics stats) { | |||
DfsPackDescription setPackStats(PackStatistics stats) { | |||
this.stats = stats; | |||
setFileSize(PACK, stats.getTotalBytes()); | |||
setObjectCount(stats.getTotalObjects()); |
@@ -114,6 +114,7 @@ import org.eclipse.jgit.revwalk.RevSort; | |||
import org.eclipse.jgit.revwalk.RevTag; | |||
import org.eclipse.jgit.revwalk.RevTree; | |||
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.WriteAbortedException; | |||
import org.eclipse.jgit.util.BlockList; | |||
@@ -247,13 +248,13 @@ public class PackWriter implements AutoCloseable { | |||
private final PackConfig config; | |||
private final Statistics stats; | |||
private final PackStatistics.Accumulator stats; | |||
private final MutableState state; | |||
private final WeakReference<PackWriter> selfRef; | |||
private Statistics.ObjectType typeStats; | |||
private PackStatistics.ObjectType.Accumulator typeStats; | |||
private List<ObjectToPack> sortedByName; | |||
@@ -356,7 +357,7 @@ public class PackWriter implements AutoCloseable { | |||
deltaBaseAsOffset = config.isDeltaBaseAsOffset(); | |||
reuseDeltas = config.isReuseDeltas(); | |||
reuseValidate = true; // be paranoid by default | |||
stats = new Statistics(); | |||
stats = new PackStatistics.Accumulator(); | |||
state = new MutableState(); | |||
selfRef = new WeakReference<PackWriter>(this); | |||
instances.put(selfRef, Boolean.TRUE); | |||
@@ -981,7 +982,7 @@ public class PackWriter implements AutoCloseable { | |||
writeObjects(out); | |||
if (!edgeObjects.isEmpty() || !cachedPacks.isEmpty()) { | |||
for (Statistics.ObjectType typeStat : stats.objectTypes) { | |||
for (PackStatistics.ObjectType.Accumulator typeStat : stats.objectTypes) { | |||
if (typeStat == null) | |||
continue; | |||
stats.thinPackBytes += typeStat.bytes; | |||
@@ -1002,7 +1003,7 @@ public class PackWriter implements AutoCloseable { | |||
stats.timeWriting = System.currentTimeMillis() - writeStart; | |||
stats.depth = depth; | |||
for (Statistics.ObjectType typeStat : stats.objectTypes) { | |||
for (PackStatistics.ObjectType.Accumulator typeStat : stats.objectTypes) { | |||
if (typeStat == null) | |||
continue; | |||
typeStat.cntDeltas += typeStat.reusedDeltas; | |||
@@ -1022,8 +1023,8 @@ public class PackWriter implements AutoCloseable { | |||
* final pack stream. The object is only available to callers after | |||
* {@link #writePack(ProgressMonitor, ProgressMonitor, OutputStream)} | |||
*/ | |||
public Statistics getStatistics() { | |||
return stats; | |||
public PackStatistics getStatistics() { | |||
return new PackStatistics(stats); | |||
} | |||
/** @return snapshot of the current state of this PackWriter. */ | |||
@@ -2028,28 +2029,36 @@ public class PackWriter implements AutoCloseable { | |||
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 { | |||
/** Statistics about a single class of object. */ | |||
public static class ObjectType { | |||
long cntObjects; | |||
long cntDeltas; | |||
// All requests are forwarded to this object. | |||
private PackStatistics.ObjectType objectType; | |||
long reusedObjects; | |||
long reusedDeltas; | |||
long bytes; | |||
long deltaBytes; | |||
/** | |||
* Wraps an | |||
* {@link org.eclipse.jgit.storage.pack.PackStatistics.ObjectType} | |||
* instance to maintain backwards compatibility with existing API. | |||
* | |||
* @param type | |||
* the wrapped instance | |||
*/ | |||
public ObjectType(PackStatistics.ObjectType type) { | |||
objectType = type; | |||
} | |||
/** | |||
* @return total number of objects output. This total includes the | |||
* value of {@link #getDeltas()}. | |||
*/ | |||
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. | |||
*/ | |||
public long getDeltas() { | |||
return cntDeltas; | |||
return objectType.getDeltas(); | |||
} | |||
/** | |||
@@ -2066,7 +2075,7 @@ public class PackWriter implements AutoCloseable { | |||
* {@link #getReusedDeltas()}. | |||
*/ | |||
public long getReusedObjects() { | |||
return reusedObjects; | |||
return objectType.getReusedObjects(); | |||
} | |||
/** | |||
@@ -2077,7 +2086,7 @@ public class PackWriter implements AutoCloseable { | |||
* was reused. | |||
*/ | |||
public long getReusedDeltas() { | |||
return reusedDeltas; | |||
return objectType.getReusedDeltas(); | |||
} | |||
/** | |||
@@ -2086,7 +2095,7 @@ public class PackWriter implements AutoCloseable { | |||
* also includes all of {@link #getDeltaBytes()}. | |||
*/ | |||
public long getBytes() { | |||
return bytes; | |||
return objectType.getBytes(); | |||
} | |||
/** | |||
@@ -2094,54 +2103,22 @@ public class PackWriter implements AutoCloseable { | |||
* object headers for the delta objects. | |||
*/ | |||
public long getDeltaBytes() { | |||
return deltaBytes; | |||
return objectType.getDeltaBytes(); | |||
} | |||
} | |||
Set<ObjectId> interestingObjects; | |||
Set<ObjectId> uninterestingObjects; | |||
Collection<CachedPack> reusedPacks; | |||
int depth; | |||
int deltaSearchNonEdgeObjects; | |||
int deltasFound; | |||
long totalObjects; | |||
// All requests are forwarded to this object. | |||
private PackStatistics statistics; | |||
long bitmapIndexMisses; | |||
long totalDeltas; | |||
long reusedObjects; | |||
long reusedDeltas; | |||
long totalBytes; | |||
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(); | |||
/** | |||
* Wraps a {@link PackStatistics} object to maintain backwards | |||
* compatibility with existing API. | |||
* | |||
* @param stats | |||
* the wrapped PackStatitics object | |||
*/ | |||
public Statistics(PackStatistics stats) { | |||
statistics = stats; | |||
} | |||
/** | |||
@@ -2150,7 +2127,7 @@ public class PackWriter implements AutoCloseable { | |||
* test. | |||
*/ | |||
public Set<ObjectId> getInterestingObjects() { | |||
return interestingObjects; | |||
return statistics.getInterestingObjects(); | |||
} | |||
/** | |||
@@ -2159,7 +2136,7 @@ public class PackWriter implements AutoCloseable { | |||
* has these objects. | |||
*/ | |||
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. | |||
*/ | |||
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. | |||
*/ | |||
public int getDeltaSearchNonEdgeObjects() { | |||
return deltaSearchNonEdgeObjects; | |||
return statistics.getDeltaSearchNonEdgeObjects(); | |||
} | |||
/** | |||
@@ -2184,7 +2161,7 @@ public class PackWriter implements AutoCloseable { | |||
* {@link #getDeltaSearchNonEdgeObjects()}. | |||
*/ | |||
public int getDeltasFound() { | |||
return deltasFound; | |||
return statistics.getDeltasFound(); | |||
} | |||
/** | |||
@@ -2192,7 +2169,7 @@ public class PackWriter implements AutoCloseable { | |||
* of {@link #getTotalDeltas()}. | |||
*/ | |||
public long getTotalObjects() { | |||
return totalObjects; | |||
return statistics.getTotalObjects(); | |||
} | |||
/** | |||
@@ -2203,7 +2180,7 @@ public class PackWriter implements AutoCloseable { | |||
* @since 4.0 | |||
*/ | |||
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. | |||
*/ | |||
public long getTotalDeltas() { | |||
return totalDeltas; | |||
return statistics.getTotalDeltas(); | |||
} | |||
/** | |||
@@ -2219,7 +2196,7 @@ public class PackWriter implements AutoCloseable { | |||
* the output. This count includes {@link #getReusedDeltas()}. | |||
*/ | |||
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. | |||
*/ | |||
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). | |||
*/ | |||
public long getTotalBytes() { | |||
return totalBytes; | |||
return statistics.getTotalBytes(); | |||
} | |||
/** | |||
@@ -2249,7 +2226,7 @@ public class PackWriter implements AutoCloseable { | |||
* pack header or trailer. | |||
*/ | |||
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. | |||
*/ | |||
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. */ | |||
public boolean isShallow() { | |||
return depth > 0; | |||
return statistics.isShallow(); | |||
} | |||
/** @return depth (in commits) the pack includes if shallow. */ | |||
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. | |||
*/ | |||
public long getTimeCounting() { | |||
return timeCounting; | |||
return statistics.getTimeCounting(); | |||
} | |||
/** | |||
@@ -2286,7 +2263,7 @@ public class PackWriter implements AutoCloseable { | |||
* can be assumed to already have. | |||
*/ | |||
public long getTimeSearchingForReuse() { | |||
return timeSearchingForReuse; | |||
return statistics.getTimeSearchingForReuse(); | |||
} | |||
/** | |||
@@ -2296,7 +2273,7 @@ public class PackWriter implements AutoCloseable { | |||
* together and improve delta compression ratios. | |||
*/ | |||
public long getTimeSearchingForSizes() { | |||
return timeSearchingForSizes; | |||
return statistics.getTimeSearchingForSizes(); | |||
} | |||
/** | |||
@@ -2306,7 +2283,7 @@ public class PackWriter implements AutoCloseable { | |||
* delta compression. | |||
*/ | |||
public long getTimeCompressing() { | |||
return timeCompressing; | |||
return statistics.getTimeCompressing(); | |||
} | |||
/** | |||
@@ -2316,16 +2293,12 @@ public class PackWriter implements AutoCloseable { | |||
* value. | |||
*/ | |||
public long getTimeWriting() { | |||
return timeWriting; | |||
return statistics.getTimeWriting(); | |||
} | |||
/** @return total time spent processing this pack. */ | |||
public long getTimeTotal() { | |||
return timeCounting | |||
+ timeSearchingForReuse | |||
+ timeSearchingForSizes | |||
+ timeCompressing | |||
+ timeWriting; | |||
return statistics.getTimeTotal(); | |||
} | |||
/** | |||
@@ -2333,14 +2306,12 @@ public class PackWriter implements AutoCloseable { | |||
* {@code getTotalBytes() / (getTimeWriting() / 1000.0)}. | |||
*/ | |||
public double getTransferRate() { | |||
return getTotalBytes() / (getTimeWriting() / 1000.0); | |||
return statistics.getTransferRate(); | |||
} | |||
/** @return formatted message string for display to clients. */ | |||
public String getMessage() { | |||
return MessageFormat.format(JGitText.get().packWriterStatistics, // | |||
Long.valueOf(totalObjects), Long.valueOf(totalDeltas), // | |||
Long.valueOf(reusedObjects), Long.valueOf(reusedDeltas)); | |||
return statistics.getMessage(); | |||
} | |||
} | |||
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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; | |||
} | |||
} |
@@ -94,6 +94,7 @@ import org.eclipse.jgit.revwalk.RevTag; | |||
import org.eclipse.jgit.revwalk.RevWalk; | |||
import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter; | |||
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.RefAdvertiser.PacketLineOutRefAdvertiser; | |||
import org.eclipse.jgit.util.io.InterruptTimer; | |||
@@ -253,6 +254,9 @@ public class UploadPack { | |||
/** Hook handling the various upload phases. */ | |||
private PreUploadHook preUploadHook = PreUploadHook.NULL; | |||
/** Hook for taking post upload actions. */ | |||
private PostUploadHook postUploadHook = PostUploadHook.NULL; | |||
/** Capabilities requested by the client. */ | |||
private Set<String> options; | |||
String userAgent; | |||
@@ -306,7 +310,7 @@ public class UploadPack { | |||
private boolean noDone; | |||
private PackWriter.Statistics statistics; | |||
private PackStatistics statistics; | |||
private UploadPackLogger logger = UploadPackLogger.NULL; | |||
@@ -519,7 +523,7 @@ public class UploadPack { | |||
this.refFilter = refFilter != null ? refFilter : RefFilter.DEFAULT; | |||
} | |||
/** @return the configured upload hook. */ | |||
/** @return the configured pre upload hook. */ | |||
public PreUploadHook getPreUploadHook() { | |||
return preUploadHook; | |||
} | |||
@@ -534,6 +538,25 @@ public class UploadPack { | |||
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. | |||
* | |||
@@ -562,7 +585,12 @@ public class UploadPack { | |||
} | |||
} | |||
/** @return the configured logger. */ | |||
/** | |||
* @return the configured logger. | |||
* | |||
* @deprecated Use {@link #getPreUploadHook()}. | |||
*/ | |||
@Deprecated | |||
public UploadPackLogger getLogger() { | |||
return logger; | |||
} | |||
@@ -572,7 +600,9 @@ public class UploadPack { | |||
* | |||
* @param logger | |||
* the logger instance. If null, no logging occurs. | |||
* @deprecated Use {@link #setPreUploadHook(PreUploadHook)}. | |||
*/ | |||
@Deprecated | |||
public void setLogger(UploadPackLogger logger) { | |||
this.logger = logger; | |||
} | |||
@@ -654,8 +684,23 @@ public class UploadPack { | |||
* was sent, such as during the negotation phase of a smart HTTP | |||
* connection, or if the client was already up-to-date. | |||
* @since 3.0 | |||
* @deprecated Use {@link #getStatistics()}. | |||
*/ | |||
@Deprecated | |||
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; | |||
} | |||
@@ -1468,8 +1513,10 @@ public class UploadPack { | |||
} finally { | |||
statistics = pw.getStatistics(); | |||
if (statistics != null) | |||
logger.onPackStatistics(statistics); | |||
if (statistics != null) { | |||
postUploadHook.onPostUpload(statistics); | |||
logger.onPackStatistics(new PackWriter.Statistics(statistics)); | |||
} | |||
pw.close(); | |||
} | |||
@@ -52,7 +52,10 @@ import org.eclipse.jgit.internal.storage.pack.PackWriter; | |||
* 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. | |||
* | |||
* @deprecated use {@link PostUploadHook} instead | |||
*/ | |||
@Deprecated | |||
public interface UploadPackLogger { | |||
/** A simple no-op logger. */ | |||
public static final UploadPackLogger NULL = new UploadPackLogger() { |
@@ -48,10 +48,13 @@ import java.util.List; | |||
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> | |||
* loggers are run in the order passed to the constructor. | |||
* | |||
* @deprecated Use {@link PostUploadHookChain} instead. | |||
*/ | |||
@Deprecated | |||
public class UploadPackLoggerChain implements UploadPackLogger { | |||
private final UploadPackLogger[] loggers; | |||
private final int count; |