You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PackWriter.java 44KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409
  1. /*
  2. * Copyright (C) 2008-2010, Google Inc.
  3. * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
  4. * and other copyright owners as documented in the project's IP log.
  5. *
  6. * This program and the accompanying materials are made available
  7. * under the terms of the Eclipse Distribution License v1.0 which
  8. * accompanies this distribution, is reproduced below, and is
  9. * available at http://www.eclipse.org/org/documents/edl-v10.php
  10. *
  11. * All rights reserved.
  12. *
  13. * Redistribution and use in source and binary forms, with or
  14. * without modification, are permitted provided that the following
  15. * conditions are met:
  16. *
  17. * - Redistributions of source code must retain the above copyright
  18. * notice, this list of conditions and the following disclaimer.
  19. *
  20. * - Redistributions in binary form must reproduce the above
  21. * copyright notice, this list of conditions and the following
  22. * disclaimer in the documentation and/or other materials provided
  23. * with the distribution.
  24. *
  25. * - Neither the name of the Eclipse Foundation, Inc. nor the
  26. * names of its contributors may be used to endorse or promote
  27. * products derived from this software without specific prior
  28. * written permission.
  29. *
  30. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  31. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  32. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  33. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  34. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  35. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  36. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  37. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  38. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  39. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  40. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  41. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  42. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  43. */
  44. package org.eclipse.jgit.storage.pack;
  45. import static org.eclipse.jgit.storage.pack.StoredObjectRepresentation.PACK_DELTA;
  46. import static org.eclipse.jgit.storage.pack.StoredObjectRepresentation.PACK_WHOLE;
  47. import java.io.IOException;
  48. import java.io.InputStream;
  49. import java.io.OutputStream;
  50. import java.security.MessageDigest;
  51. import java.util.ArrayList;
  52. import java.util.Arrays;
  53. import java.util.Collection;
  54. import java.util.Collections;
  55. import java.util.Comparator;
  56. import java.util.Iterator;
  57. import java.util.List;
  58. import java.util.concurrent.ExecutorService;
  59. import java.util.concurrent.Executors;
  60. import java.util.concurrent.TimeUnit;
  61. import java.util.zip.Deflater;
  62. import java.util.zip.DeflaterOutputStream;
  63. import org.eclipse.jgit.JGitText;
  64. import org.eclipse.jgit.errors.CorruptObjectException;
  65. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  66. import org.eclipse.jgit.errors.LargeObjectException;
  67. import org.eclipse.jgit.errors.MissingObjectException;
  68. import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
  69. import org.eclipse.jgit.lib.AnyObjectId;
  70. import org.eclipse.jgit.lib.Config;
  71. import org.eclipse.jgit.lib.Constants;
  72. import org.eclipse.jgit.lib.NullProgressMonitor;
  73. import org.eclipse.jgit.lib.ObjectId;
  74. import org.eclipse.jgit.lib.ObjectIdSubclassMap;
  75. import org.eclipse.jgit.lib.ObjectLoader;
  76. import org.eclipse.jgit.lib.ObjectReader;
  77. import org.eclipse.jgit.lib.ProgressMonitor;
  78. import org.eclipse.jgit.lib.Repository;
  79. import org.eclipse.jgit.lib.ThreadSafeProgressMonitor;
  80. import org.eclipse.jgit.revwalk.ObjectWalk;
  81. import org.eclipse.jgit.revwalk.RevFlag;
  82. import org.eclipse.jgit.revwalk.RevObject;
  83. import org.eclipse.jgit.revwalk.RevSort;
  84. import org.eclipse.jgit.storage.file.PackIndexWriter;
  85. import org.eclipse.jgit.util.IO;
  86. import org.eclipse.jgit.util.TemporaryBuffer;
  87. /**
  88. * <p>
  89. * PackWriter class is responsible for generating pack files from specified set
  90. * of objects from repository. This implementation produce pack files in format
  91. * version 2.
  92. * </p>
  93. * <p>
  94. * Source of objects may be specified in two ways:
  95. * <ul>
  96. * <li>(usually) by providing sets of interesting and uninteresting objects in
  97. * repository - all interesting objects and their ancestors except uninteresting
  98. * objects and their ancestors will be included in pack, or</li>
  99. * <li>by providing iterator of {@link RevObject} specifying exact list and
  100. * order of objects in pack</li>
  101. * </ul>
  102. * Typical usage consists of creating instance intended for some pack,
  103. * configuring options, preparing the list of objects by calling
  104. * {@link #preparePack(Iterator)} or
  105. * {@link #preparePack(ProgressMonitor, Collection, Collection)}, and finally
  106. * producing the stream with {@link #writePack(ProgressMonitor, ProgressMonitor, OutputStream)}.
  107. * </p>
  108. * <p>
  109. * Class provide set of configurable options and {@link ProgressMonitor}
  110. * support, as operations may take a long time for big repositories. Deltas
  111. * searching algorithm is <b>NOT IMPLEMENTED</b> yet - this implementation
  112. * relies only on deltas and objects reuse.
  113. * </p>
  114. * <p>
  115. * This class is not thread safe, it is intended to be used in one thread, with
  116. * one instance per created pack. Subsequent calls to writePack result in
  117. * undefined behavior.
  118. * </p>
  119. */
  120. public class PackWriter {
  121. /**
  122. * Title of {@link ProgressMonitor} task used during counting objects to
  123. * pack.
  124. *
  125. * @see #preparePack(ProgressMonitor, Collection, Collection)
  126. */
  127. public static final String COUNTING_OBJECTS_PROGRESS = JGitText.get().countingObjects;
  128. /**
  129. * Title of {@link ProgressMonitor} task used during compression.
  130. *
  131. * @see #writePack(ProgressMonitor, ProgressMonitor, OutputStream)
  132. */
  133. public static final String COMPRESSING_OBJECTS_PROGRESS = JGitText.get().compressingObjects;
  134. /**
  135. * Title of {@link ProgressMonitor} task used during writing out pack
  136. * (objects)
  137. *
  138. * @see #writePack(ProgressMonitor, ProgressMonitor, OutputStream)
  139. */
  140. public static final String WRITING_OBJECTS_PROGRESS = JGitText.get().writingObjects;
  141. /**
  142. * Default value of deltas reuse option.
  143. *
  144. * @see #setReuseDeltas(boolean)
  145. */
  146. public static final boolean DEFAULT_REUSE_DELTAS = true;
  147. /**
  148. * Default value of objects reuse option.
  149. *
  150. * @see #setReuseObjects(boolean)
  151. */
  152. public static final boolean DEFAULT_REUSE_OBJECTS = true;
  153. /**
  154. * Default value of delta base as offset option.
  155. *
  156. * @see #setDeltaBaseAsOffset(boolean)
  157. */
  158. public static final boolean DEFAULT_DELTA_BASE_AS_OFFSET = false;
  159. /**
  160. * Default value of maximum delta chain depth.
  161. *
  162. * @see #setMaxDeltaDepth(int)
  163. */
  164. public static final int DEFAULT_MAX_DELTA_DEPTH = 50;
  165. /**
  166. * Default window size during packing.
  167. *
  168. * @see #setDeltaSearchWindowSize(int)
  169. */
  170. public static final int DEFAULT_DELTA_SEARCH_WINDOW_SIZE = 10;
  171. static final long DEFAULT_BIG_FILE_THRESHOLD = 50 * 1024 * 1024;
  172. static final long DEFAULT_DELTA_CACHE_SIZE = 50 * 1024 * 1024;
  173. static final int DEFAULT_DELTA_CACHE_LIMIT = 100;
  174. private static final int PACK_VERSION_GENERATED = 2;
  175. @SuppressWarnings("unchecked")
  176. private final List<ObjectToPack> objectsLists[] = new List[Constants.OBJ_TAG + 1];
  177. {
  178. objectsLists[0] = Collections.<ObjectToPack> emptyList();
  179. objectsLists[Constants.OBJ_COMMIT] = new ArrayList<ObjectToPack>();
  180. objectsLists[Constants.OBJ_TREE] = new ArrayList<ObjectToPack>();
  181. objectsLists[Constants.OBJ_BLOB] = new ArrayList<ObjectToPack>();
  182. objectsLists[Constants.OBJ_TAG] = new ArrayList<ObjectToPack>();
  183. }
  184. private final ObjectIdSubclassMap<ObjectToPack> objectsMap = new ObjectIdSubclassMap<ObjectToPack>();
  185. // edge objects for thin packs
  186. private final ObjectIdSubclassMap<ObjectToPack> edgeObjects = new ObjectIdSubclassMap<ObjectToPack>();
  187. private int compressionLevel;
  188. private Deflater myDeflater;
  189. private final ObjectReader reader;
  190. /** {@link #reader} recast to the reuse interface, if it supports it. */
  191. private final ObjectReuseAsIs reuseSupport;
  192. private List<ObjectToPack> sortedByName;
  193. private byte packcsum[];
  194. private boolean reuseDeltas = DEFAULT_REUSE_DELTAS;
  195. private boolean reuseObjects = DEFAULT_REUSE_OBJECTS;
  196. private boolean deltaBaseAsOffset = DEFAULT_DELTA_BASE_AS_OFFSET;
  197. private boolean deltaCompress = true;
  198. private int maxDeltaDepth = DEFAULT_MAX_DELTA_DEPTH;
  199. private int deltaSearchWindowSize = DEFAULT_DELTA_SEARCH_WINDOW_SIZE;
  200. private long deltaCacheSize = DEFAULT_DELTA_CACHE_SIZE;
  201. private int deltaCacheLimit = DEFAULT_DELTA_CACHE_LIMIT;
  202. private int indexVersion;
  203. private long bigFileThreshold = DEFAULT_BIG_FILE_THRESHOLD;
  204. private int threads = 1;
  205. private boolean thin;
  206. private boolean ignoreMissingUninteresting = true;
  207. /**
  208. * Create writer for specified repository.
  209. * <p>
  210. * Objects for packing are specified in {@link #preparePack(Iterator)} or
  211. * {@link #preparePack(ProgressMonitor, Collection, Collection)}.
  212. *
  213. * @param repo
  214. * repository where objects are stored.
  215. */
  216. public PackWriter(final Repository repo) {
  217. this(repo, repo.newObjectReader());
  218. }
  219. /**
  220. * Create a writer to load objects from the specified reader.
  221. * <p>
  222. * Objects for packing are specified in {@link #preparePack(Iterator)} or
  223. * {@link #preparePack(ProgressMonitor, Collection, Collection)}.
  224. *
  225. * @param reader
  226. * reader to read from the repository with.
  227. */
  228. public PackWriter(final ObjectReader reader) {
  229. this(null, reader);
  230. }
  231. /**
  232. * Create writer for specified repository.
  233. * <p>
  234. * Objects for packing are specified in {@link #preparePack(Iterator)} or
  235. * {@link #preparePack(ProgressMonitor, Collection, Collection)}.
  236. *
  237. * @param repo
  238. * repository where objects are stored.
  239. * @param reader
  240. * reader to read from the repository with.
  241. */
  242. public PackWriter(final Repository repo, final ObjectReader reader) {
  243. this.reader = reader;
  244. if (reader instanceof ObjectReuseAsIs)
  245. reuseSupport = ((ObjectReuseAsIs) reader);
  246. else
  247. reuseSupport = null;
  248. final PackConfig pc = configOf(repo).get(PackConfig.KEY);
  249. deltaSearchWindowSize = pc.deltaWindow;
  250. deltaCacheSize = pc.deltaCacheSize;
  251. deltaCacheLimit = pc.deltaCacheLimit;
  252. maxDeltaDepth = pc.deltaDepth;
  253. compressionLevel = pc.compression;
  254. indexVersion = pc.indexVersion;
  255. bigFileThreshold = pc.bigFileThreshold;
  256. threads = pc.threads;
  257. }
  258. private static Config configOf(final Repository repo) {
  259. if (repo == null)
  260. return new Config();
  261. return repo.getConfig();
  262. }
  263. /**
  264. * Check whether object is configured to reuse deltas existing in
  265. * repository.
  266. * <p>
  267. * Default setting: {@value #DEFAULT_REUSE_DELTAS}
  268. * </p>
  269. *
  270. * @return true if object is configured to reuse deltas; false otherwise.
  271. */
  272. public boolean isReuseDeltas() {
  273. return reuseDeltas;
  274. }
  275. /**
  276. * Set reuse deltas configuration option for this writer. When enabled,
  277. * writer will search for delta representation of object in repository and
  278. * use it if possible. Normally, only deltas with base to another object
  279. * existing in set of objects to pack will be used. Exception is however
  280. * thin-pack (see
  281. * {@link #preparePack(ProgressMonitor, Collection, Collection)} and
  282. * {@link #preparePack(Iterator)}) where base object must exist on other
  283. * side machine.
  284. * <p>
  285. * When raw delta data is directly copied from a pack file, checksum is
  286. * computed to verify data.
  287. * </p>
  288. * <p>
  289. * Default setting: {@value #DEFAULT_REUSE_DELTAS}
  290. * </p>
  291. *
  292. * @param reuseDeltas
  293. * boolean indicating whether or not try to reuse deltas.
  294. */
  295. public void setReuseDeltas(boolean reuseDeltas) {
  296. this.reuseDeltas = reuseDeltas;
  297. }
  298. /**
  299. * Checks whether object is configured to reuse existing objects
  300. * representation in repository.
  301. * <p>
  302. * Default setting: {@value #DEFAULT_REUSE_OBJECTS}
  303. * </p>
  304. *
  305. * @return true if writer is configured to reuse objects representation from
  306. * pack; false otherwise.
  307. */
  308. public boolean isReuseObjects() {
  309. return reuseObjects;
  310. }
  311. /**
  312. * Set reuse objects configuration option for this writer. If enabled,
  313. * writer searches for representation in a pack file. If possible,
  314. * compressed data is directly copied from such a pack file. Data checksum
  315. * is verified.
  316. * <p>
  317. * Default setting: {@value #DEFAULT_REUSE_OBJECTS}
  318. * </p>
  319. *
  320. * @param reuseObjects
  321. * boolean indicating whether or not writer should reuse existing
  322. * objects representation.
  323. */
  324. public void setReuseObjects(boolean reuseObjects) {
  325. this.reuseObjects = reuseObjects;
  326. }
  327. /**
  328. * Check whether writer can store delta base as an offset (new style
  329. * reducing pack size) or should store it as an object id (legacy style,
  330. * compatible with old readers).
  331. * <p>
  332. * Default setting: {@value #DEFAULT_DELTA_BASE_AS_OFFSET}
  333. * </p>
  334. *
  335. * @return true if delta base is stored as an offset; false if it is stored
  336. * as an object id.
  337. */
  338. public boolean isDeltaBaseAsOffset() {
  339. return deltaBaseAsOffset;
  340. }
  341. /**
  342. * Set writer delta base format. Delta base can be written as an offset in a
  343. * pack file (new approach reducing file size) or as an object id (legacy
  344. * approach, compatible with old readers).
  345. * <p>
  346. * Default setting: {@value #DEFAULT_DELTA_BASE_AS_OFFSET}
  347. * </p>
  348. *
  349. * @param deltaBaseAsOffset
  350. * boolean indicating whether delta base can be stored as an
  351. * offset.
  352. */
  353. public void setDeltaBaseAsOffset(boolean deltaBaseAsOffset) {
  354. this.deltaBaseAsOffset = deltaBaseAsOffset;
  355. }
  356. /**
  357. * Check whether the writer will create new deltas on the fly.
  358. * <p>
  359. * Default setting: true
  360. * </p>
  361. *
  362. * @return true if the writer will create a new delta when either
  363. * {@link #isReuseDeltas()} is false, or no suitable delta is
  364. * available for reuse.
  365. */
  366. public boolean isDeltaCompress() {
  367. return deltaCompress;
  368. }
  369. /**
  370. * Set whether or not the writer will create new deltas on the fly.
  371. *
  372. * @param deltaCompress
  373. * true to create deltas when {@link #isReuseDeltas()} is false,
  374. * or when a suitable delta isn't available for reuse. Set to
  375. * false to write whole objects instead.
  376. */
  377. public void setDeltaCompress(boolean deltaCompress) {
  378. this.deltaCompress = deltaCompress;
  379. }
  380. /**
  381. * Get maximum depth of delta chain set up for this writer. Generated chains
  382. * are not longer than this value.
  383. * <p>
  384. * Default setting: {@value #DEFAULT_MAX_DELTA_DEPTH}
  385. * </p>
  386. *
  387. * @return maximum delta chain depth.
  388. */
  389. public int getMaxDeltaDepth() {
  390. return maxDeltaDepth;
  391. }
  392. /**
  393. * Set up maximum depth of delta chain for this writer. Generated chains are
  394. * not longer than this value. Too low value causes low compression level,
  395. * while too big makes unpacking (reading) longer.
  396. * <p>
  397. * Default setting: {@value #DEFAULT_MAX_DELTA_DEPTH}
  398. * </p>
  399. *
  400. * @param maxDeltaDepth
  401. * maximum delta chain depth.
  402. */
  403. public void setMaxDeltaDepth(int maxDeltaDepth) {
  404. this.maxDeltaDepth = maxDeltaDepth;
  405. }
  406. /**
  407. * Get the number of objects to try when looking for a delta base.
  408. * <p>
  409. * This limit is per thread, if 4 threads are used the actual memory
  410. * used will be 4 times this value.
  411. *
  412. * @return the object count to be searched.
  413. */
  414. public int getDeltaSearchWindowSize() {
  415. return deltaSearchWindowSize;
  416. }
  417. /**
  418. * Set the number of objects considered when searching for a delta base.
  419. * <p>
  420. * Default setting: {@value #DEFAULT_DELTA_SEARCH_WINDOW_SIZE}
  421. * </p>
  422. *
  423. * @param objectCount
  424. * number of objects to search at once. Must be at least 2.
  425. */
  426. public void setDeltaSearchWindowSize(int objectCount) {
  427. if (objectCount <= 2)
  428. setDeltaCompress(false);
  429. else
  430. deltaSearchWindowSize = objectCount;
  431. }
  432. /**
  433. * Get the size of the in-memory delta cache.
  434. * <p>
  435. * This limit is for the entire writer, even if multiple threads are used.
  436. *
  437. * @return maximum number of bytes worth of delta data to cache in memory.
  438. * If 0 the cache is infinite in size (up to the JVM heap limit
  439. * anyway). A very tiny size such as 1 indicates the cache is
  440. * effectively disabled.
  441. */
  442. public long getDeltaCacheSize() {
  443. return deltaCacheSize;
  444. }
  445. /**
  446. * Set the maximum number of bytes of delta data to cache.
  447. * <p>
  448. * During delta search, up to this many bytes worth of small or hard to
  449. * compute deltas will be stored in memory. This cache speeds up writing by
  450. * allowing the cached entry to simply be dumped to the output stream.
  451. *
  452. * @param size
  453. * number of bytes to cache. Set to 0 to enable an infinite
  454. * cache, set to 1 (an impossible size for any delta) to disable
  455. * the cache.
  456. */
  457. public void setDeltaCacheSize(long size) {
  458. deltaCacheSize = size;
  459. }
  460. /**
  461. * Maximum size in bytes of a delta to cache.
  462. *
  463. * @return maximum size (in bytes) of a delta that should be cached.
  464. */
  465. public int getDeltaCacheLimit() {
  466. return deltaCacheLimit;
  467. }
  468. /**
  469. * Set the maximum size of a delta that should be cached.
  470. * <p>
  471. * During delta search, any delta smaller than this size will be cached, up
  472. * to the {@link #getDeltaCacheSize()} maximum limit. This speeds up writing
  473. * by allowing these cached deltas to be output as-is.
  474. *
  475. * @param size
  476. * maximum size (in bytes) of a delta to be cached.
  477. */
  478. public void setDeltaCacheLimit(int size) {
  479. deltaCacheLimit = size;
  480. }
  481. /**
  482. * Get the maximum file size that will be delta compressed.
  483. * <p>
  484. * Files bigger than this setting will not be delta compressed, as they are
  485. * more than likely already highly compressed binary data files that do not
  486. * delta compress well, such as MPEG videos.
  487. *
  488. * @return the configured big file threshold.
  489. */
  490. public long getBigFileThreshold() {
  491. return bigFileThreshold;
  492. }
  493. /**
  494. * Set the maximum file size that should be considered for deltas.
  495. *
  496. * @param bigFileThreshold
  497. * the limit, in bytes.
  498. */
  499. public void setBigFileThreshold(long bigFileThreshold) {
  500. this.bigFileThreshold = bigFileThreshold;
  501. }
  502. /**
  503. * Get the compression level applied to objects in the pack.
  504. *
  505. * @return current compression level, see {@link java.util.zip.Deflater}.
  506. */
  507. public int getCompressionLevel() {
  508. return compressionLevel;
  509. }
  510. /**
  511. * Set the compression level applied to objects in the pack.
  512. *
  513. * @param level
  514. * compression level, must be a valid level recognized by the
  515. * {@link java.util.zip.Deflater} class. Typically this setting
  516. * is {@link java.util.zip.Deflater#BEST_SPEED}.
  517. */
  518. public void setCompressionLevel(int level) {
  519. compressionLevel = level;
  520. }
  521. /** @return number of threads used for delta compression. */
  522. public int getThreads() {
  523. return threads;
  524. }
  525. /**
  526. * Set the number of threads to use for delta compression.
  527. * <p>
  528. * During delta compression, if there are enough objects to be considered
  529. * the writer will start up concurrent threads and allow them to compress
  530. * different sections of the repository concurrently.
  531. *
  532. * @param threads
  533. * number of threads to use. If <= 0 the number of available
  534. * processors for this JVM is used.
  535. */
  536. public void setThread(int threads) {
  537. this.threads = threads;
  538. }
  539. /** @return true if this writer is producing a thin pack. */
  540. public boolean isThin() {
  541. return thin;
  542. }
  543. /**
  544. * @param packthin
  545. * a boolean indicating whether writer may pack objects with
  546. * delta base object not within set of objects to pack, but
  547. * belonging to party repository (uninteresting/boundary) as
  548. * determined by set; this kind of pack is used only for
  549. * transport; true - to produce thin pack, false - otherwise.
  550. */
  551. public void setThin(final boolean packthin) {
  552. thin = packthin;
  553. }
  554. /**
  555. * @return true to ignore objects that are uninteresting and also not found
  556. * on local disk; false to throw a {@link MissingObjectException}
  557. * out of {@link #preparePack(ProgressMonitor, Collection, Collection)} if an
  558. * uninteresting object is not in the source repository. By default,
  559. * true, permitting gracefully ignoring of uninteresting objects.
  560. */
  561. public boolean isIgnoreMissingUninteresting() {
  562. return ignoreMissingUninteresting;
  563. }
  564. /**
  565. * @param ignore
  566. * true if writer should ignore non existing uninteresting
  567. * objects during construction set of objects to pack; false
  568. * otherwise - non existing uninteresting objects may cause
  569. * {@link MissingObjectException}
  570. */
  571. public void setIgnoreMissingUninteresting(final boolean ignore) {
  572. ignoreMissingUninteresting = ignore;
  573. }
  574. /**
  575. * Set the pack index file format version this instance will create.
  576. *
  577. * @param version
  578. * the version to write. The special version 0 designates the
  579. * oldest (most compatible) format available for the objects.
  580. * @see PackIndexWriter
  581. */
  582. public void setIndexVersion(final int version) {
  583. indexVersion = version;
  584. }
  585. /**
  586. * Returns objects number in a pack file that was created by this writer.
  587. *
  588. * @return number of objects in pack.
  589. */
  590. public int getObjectsNumber() {
  591. return objectsMap.size();
  592. }
  593. /**
  594. * Prepare the list of objects to be written to the pack stream.
  595. * <p>
  596. * Iterator <b>exactly</b> determines which objects are included in a pack
  597. * and order they appear in pack (except that objects order by type is not
  598. * needed at input). This order should conform general rules of ordering
  599. * objects in git - by recency and path (type and delta-base first is
  600. * internally secured) and responsibility for guaranteeing this order is on
  601. * a caller side. Iterator must return each id of object to write exactly
  602. * once.
  603. * </p>
  604. * <p>
  605. * When iterator returns object that has {@link RevFlag#UNINTERESTING} flag,
  606. * this object won't be included in an output pack. Instead, it is recorded
  607. * as edge-object (known to remote repository) for thin-pack. In such a case
  608. * writer may pack objects with delta base object not within set of objects
  609. * to pack, but belonging to party repository - those marked with
  610. * {@link RevFlag#UNINTERESTING} flag. This type of pack is used only for
  611. * transport.
  612. * </p>
  613. *
  614. * @param objectsSource
  615. * iterator of object to store in a pack; order of objects within
  616. * each type is important, ordering by type is not needed;
  617. * allowed types for objects are {@link Constants#OBJ_COMMIT},
  618. * {@link Constants#OBJ_TREE}, {@link Constants#OBJ_BLOB} and
  619. * {@link Constants#OBJ_TAG}; objects returned by iterator may
  620. * be later reused by caller as object id and type are internally
  621. * copied in each iteration; if object returned by iterator has
  622. * {@link RevFlag#UNINTERESTING} flag set, it won't be included
  623. * in a pack, but is considered as edge-object for thin-pack.
  624. * @throws IOException
  625. * when some I/O problem occur during reading objects.
  626. */
  627. public void preparePack(final Iterator<RevObject> objectsSource)
  628. throws IOException {
  629. while (objectsSource.hasNext()) {
  630. addObject(objectsSource.next());
  631. }
  632. }
  633. /**
  634. * Prepare the list of objects to be written to the pack stream.
  635. * <p>
  636. * Basing on these 2 sets, another set of objects to put in a pack file is
  637. * created: this set consists of all objects reachable (ancestors) from
  638. * interesting objects, except uninteresting objects and their ancestors.
  639. * This method uses class {@link ObjectWalk} extensively to find out that
  640. * appropriate set of output objects and their optimal order in output pack.
  641. * Order is consistent with general git in-pack rules: sort by object type,
  642. * recency, path and delta-base first.
  643. * </p>
  644. *
  645. * @param countingMonitor
  646. * progress during object enumeration.
  647. * @param interestingObjects
  648. * collection of objects to be marked as interesting (start
  649. * points of graph traversal).
  650. * @param uninterestingObjects
  651. * collection of objects to be marked as uninteresting (end
  652. * points of graph traversal).
  653. * @throws IOException
  654. * when some I/O problem occur during reading objects.
  655. */
  656. public void preparePack(ProgressMonitor countingMonitor,
  657. final Collection<? extends ObjectId> interestingObjects,
  658. final Collection<? extends ObjectId> uninterestingObjects)
  659. throws IOException {
  660. if (countingMonitor == null)
  661. countingMonitor = NullProgressMonitor.INSTANCE;
  662. ObjectWalk walker = setUpWalker(interestingObjects,
  663. uninterestingObjects);
  664. findObjectsToPack(countingMonitor, walker);
  665. }
  666. /**
  667. * Determine if the pack file will contain the requested object.
  668. *
  669. * @param id
  670. * the object to test the existence of.
  671. * @return true if the object will appear in the output pack file.
  672. */
  673. public boolean willInclude(final AnyObjectId id) {
  674. return objectsMap.get(id) != null;
  675. }
  676. /**
  677. * Computes SHA-1 of lexicographically sorted objects ids written in this
  678. * pack, as used to name a pack file in repository.
  679. *
  680. * @return ObjectId representing SHA-1 name of a pack that was created.
  681. */
  682. public ObjectId computeName() {
  683. final byte[] buf = new byte[Constants.OBJECT_ID_LENGTH];
  684. final MessageDigest md = Constants.newMessageDigest();
  685. for (ObjectToPack otp : sortByName()) {
  686. otp.copyRawTo(buf, 0);
  687. md.update(buf, 0, Constants.OBJECT_ID_LENGTH);
  688. }
  689. return ObjectId.fromRaw(md.digest());
  690. }
  691. /**
  692. * Create an index file to match the pack file just written.
  693. * <p>
  694. * This method can only be invoked after {@link #preparePack(Iterator)} or
  695. * {@link #preparePack(ProgressMonitor, Collection, Collection)} has been
  696. * invoked and completed successfully. Writing a corresponding index is an
  697. * optional feature that not all pack users may require.
  698. *
  699. * @param indexStream
  700. * output for the index data. Caller is responsible for closing
  701. * this stream.
  702. * @throws IOException
  703. * the index data could not be written to the supplied stream.
  704. */
  705. public void writeIndex(final OutputStream indexStream) throws IOException {
  706. final List<ObjectToPack> list = sortByName();
  707. final PackIndexWriter iw;
  708. if (indexVersion <= 0)
  709. iw = PackIndexWriter.createOldestPossible(indexStream, list);
  710. else
  711. iw = PackIndexWriter.createVersion(indexStream, indexVersion);
  712. iw.write(list, packcsum);
  713. }
  714. private List<ObjectToPack> sortByName() {
  715. if (sortedByName == null) {
  716. sortedByName = new ArrayList<ObjectToPack>(objectsMap.size());
  717. for (List<ObjectToPack> list : objectsLists) {
  718. for (ObjectToPack otp : list)
  719. sortedByName.add(otp);
  720. }
  721. Collections.sort(sortedByName);
  722. }
  723. return sortedByName;
  724. }
  725. /**
  726. * Write the prepared pack to the supplied stream.
  727. * <p>
  728. * At first, this method collects and sorts objects to pack, then deltas
  729. * search is performed if set up accordingly, finally pack stream is
  730. * written. {@link ProgressMonitor} tasks {@value #COMPRESSING_OBJECTS_PROGRESS}
  731. * (only if reuseDeltas or reuseObjects is enabled) and
  732. * {@value #WRITING_OBJECTS_PROGRESS} are updated during packing.
  733. * </p>
  734. * <p>
  735. * All reused objects data checksum (Adler32/CRC32) is computed and
  736. * validated against existing checksum.
  737. * </p>
  738. *
  739. * @param compressMonitor
  740. * progress monitor to report object compression work.
  741. * @param writeMonitor
  742. * progress monitor to report the number of objects written.
  743. * @param packStream
  744. * output stream of pack data. The stream should be buffered by
  745. * the caller. The caller is responsible for closing the stream.
  746. * @throws IOException
  747. * an error occurred reading a local object's data to include in
  748. * the pack, or writing compressed object data to the output
  749. * stream.
  750. */
  751. public void writePack(ProgressMonitor compressMonitor,
  752. ProgressMonitor writeMonitor, OutputStream packStream)
  753. throws IOException {
  754. if (compressMonitor == null)
  755. compressMonitor = NullProgressMonitor.INSTANCE;
  756. if (writeMonitor == null)
  757. writeMonitor = NullProgressMonitor.INSTANCE;
  758. if ((reuseDeltas || reuseObjects) && reuseSupport != null)
  759. searchForReuse();
  760. if (deltaCompress)
  761. searchForDeltas(compressMonitor);
  762. final PackOutputStream out = new PackOutputStream(writeMonitor,
  763. packStream, this);
  764. writeMonitor.beginTask(WRITING_OBJECTS_PROGRESS, getObjectsNumber());
  765. out.writeFileHeader(PACK_VERSION_GENERATED, getObjectsNumber());
  766. writeObjects(writeMonitor, out);
  767. writeChecksum(out);
  768. reader.release();
  769. writeMonitor.endTask();
  770. }
  771. /** Release all resources used by this writer. */
  772. public void release() {
  773. reader.release();
  774. if (myDeflater != null) {
  775. myDeflater.end();
  776. myDeflater = null;
  777. }
  778. }
  779. private void searchForReuse() throws IOException {
  780. for (List<ObjectToPack> list : objectsLists) {
  781. for (ObjectToPack otp : list)
  782. reuseSupport.selectObjectRepresentation(this, otp);
  783. }
  784. }
  785. private void searchForDeltas(ProgressMonitor monitor)
  786. throws MissingObjectException, IncorrectObjectTypeException,
  787. IOException {
  788. // Commits and annotated tags tend to have too many differences to
  789. // really benefit from delta compression. Consequently just don't
  790. // bother examining those types here.
  791. //
  792. ObjectToPack[] list = new ObjectToPack[
  793. objectsLists[Constants.OBJ_TREE].size()
  794. + objectsLists[Constants.OBJ_BLOB].size()
  795. + edgeObjects.size()];
  796. int cnt = 0;
  797. cnt = findObjectsNeedingDelta(list, cnt, Constants.OBJ_TREE);
  798. cnt = findObjectsNeedingDelta(list, cnt, Constants.OBJ_BLOB);
  799. if (cnt == 0)
  800. return;
  801. // Queue up any edge objects that we might delta against. We won't
  802. // be sending these as we assume the other side has them, but we need
  803. // them in the search phase below.
  804. //
  805. for (ObjectToPack eo : edgeObjects) {
  806. try {
  807. if (loadSize(eo))
  808. list[cnt++] = eo;
  809. } catch (IOException notAvailable) {
  810. // Skip this object. Since we aren't going to write it out
  811. // the only consequence of it being unavailable to us is we
  812. // may produce a larger data stream than we could have.
  813. //
  814. if (!ignoreMissingUninteresting)
  815. throw notAvailable;
  816. }
  817. }
  818. monitor.beginTask(COMPRESSING_OBJECTS_PROGRESS, cnt);
  819. // Sort the objects by path hash so like files are near each other,
  820. // and then by size descending so that bigger files are first. This
  821. // applies "Linus' Law" which states that newer files tend to be the
  822. // bigger ones, because source files grow and hardly ever shrink.
  823. //
  824. Arrays.sort(list, 0, cnt, new Comparator<ObjectToPack>() {
  825. public int compare(ObjectToPack a, ObjectToPack b) {
  826. int cmp = a.getType() - b.getType();
  827. if (cmp == 0)
  828. cmp = (a.getPathHash() >>> 1) - (b.getPathHash() >>> 1);
  829. if (cmp == 0)
  830. cmp = (a.getPathHash() & 1) - (b.getPathHash() & 1);
  831. if (cmp == 0)
  832. cmp = b.getWeight() - a.getWeight();
  833. return cmp;
  834. }
  835. });
  836. searchForDeltas(monitor, list, cnt);
  837. monitor.endTask();
  838. }
  839. private int findObjectsNeedingDelta(ObjectToPack[] list, int cnt, int type)
  840. throws MissingObjectException, IncorrectObjectTypeException,
  841. IOException {
  842. for (ObjectToPack otp : objectsLists[type]) {
  843. if (otp.isDoNotDelta()) // delta is disabled for this path
  844. continue;
  845. if (otp.isDeltaRepresentation()) // already reusing a delta
  846. continue;
  847. if (loadSize(otp))
  848. list[cnt++] = otp;
  849. }
  850. return cnt;
  851. }
  852. private boolean loadSize(ObjectToPack e) throws MissingObjectException,
  853. IncorrectObjectTypeException, IOException {
  854. long sz = reader.getObjectSize(e, e.getType());
  855. // If its too big for us to handle, skip over it.
  856. //
  857. if (bigFileThreshold <= sz || Integer.MAX_VALUE <= sz)
  858. return false;
  859. // If its too tiny for the delta compression to work, skip it.
  860. //
  861. if (sz <= DeltaIndex.BLKSZ)
  862. return false;
  863. e.setWeight((int) sz);
  864. return true;
  865. }
  866. private void searchForDeltas(final ProgressMonitor monitor,
  867. final ObjectToPack[] list, final int cnt)
  868. throws MissingObjectException, IncorrectObjectTypeException,
  869. LargeObjectException, IOException {
  870. if (threads == 0)
  871. threads = Runtime.getRuntime().availableProcessors();
  872. if (threads <= 1 || cnt <= 2 * getDeltaSearchWindowSize()) {
  873. DeltaCache dc = new DeltaCache(this);
  874. DeltaWindow dw = new DeltaWindow(this, dc, reader);
  875. dw.search(monitor, list, 0, cnt);
  876. return;
  877. }
  878. final List<Throwable> errors = Collections
  879. .synchronizedList(new ArrayList<Throwable>());
  880. final DeltaCache dc = new ThreadSafeDeltaCache(this);
  881. final ProgressMonitor pm = new ThreadSafeProgressMonitor(monitor);
  882. final ExecutorService pool = Executors.newFixedThreadPool(threads);
  883. // Guess at the size of batch we want. Because we don't really
  884. // have a way for a thread to steal work from another thread if
  885. // it ends early, we over partition slightly so the work units
  886. // are a bit smaller.
  887. //
  888. int estSize = cnt / (threads * 2);
  889. if (estSize < 2 * getDeltaSearchWindowSize())
  890. estSize = 2 * getDeltaSearchWindowSize();
  891. for (int i = 0; i < cnt;) {
  892. final int start = i;
  893. final int batchSize;
  894. if (cnt - i < estSize) {
  895. // If we don't have enough to fill the remaining block,
  896. // schedule what is left over as a single block.
  897. //
  898. batchSize = cnt - i;
  899. } else {
  900. // Try to split the block at the end of a path.
  901. //
  902. int end = start + estSize;
  903. while (end < cnt) {
  904. ObjectToPack a = list[end - 1];
  905. ObjectToPack b = list[end];
  906. if (a.getPathHash() == b.getPathHash())
  907. end++;
  908. else
  909. break;
  910. }
  911. batchSize = end - start;
  912. }
  913. i += batchSize;
  914. pool.submit(new Runnable() {
  915. public void run() {
  916. try {
  917. final ObjectReader or = reader.newReader();
  918. try {
  919. DeltaWindow dw;
  920. dw = new DeltaWindow(PackWriter.this, dc, or);
  921. dw.search(pm, list, start, batchSize);
  922. } finally {
  923. or.release();
  924. }
  925. } catch (Throwable err) {
  926. errors.add(err);
  927. }
  928. }
  929. });
  930. }
  931. // Tell the pool to stop.
  932. //
  933. pool.shutdown();
  934. for (;;) {
  935. try {
  936. if (pool.awaitTermination(60, TimeUnit.SECONDS))
  937. break;
  938. } catch (InterruptedException e) {
  939. throw new IOException(
  940. JGitText.get().packingCancelledDuringObjectsWriting);
  941. }
  942. }
  943. // If any thread threw an error, try to report it back as
  944. // though we weren't using a threaded search algorithm.
  945. //
  946. if (!errors.isEmpty()) {
  947. Throwable err = errors.get(0);
  948. if (err instanceof Error)
  949. throw (Error) err;
  950. if (err instanceof RuntimeException)
  951. throw (RuntimeException) err;
  952. if (err instanceof IOException)
  953. throw (IOException) err;
  954. IOException fail = new IOException(err.getMessage());
  955. fail.initCause(err);
  956. throw fail;
  957. }
  958. }
  959. private void writeObjects(ProgressMonitor writeMonitor, PackOutputStream out)
  960. throws IOException {
  961. for (List<ObjectToPack> list : objectsLists) {
  962. for (ObjectToPack otp : list) {
  963. if (writeMonitor.isCancelled())
  964. throw new IOException(
  965. JGitText.get().packingCancelledDuringObjectsWriting);
  966. if (!otp.isWritten())
  967. writeObject(out, otp);
  968. }
  969. }
  970. }
  971. private void writeObject(PackOutputStream out, final ObjectToPack otp)
  972. throws IOException {
  973. if (otp.isWritten())
  974. return; // We shouldn't be here.
  975. otp.markWantWrite();
  976. if (otp.isDeltaRepresentation())
  977. writeBaseFirst(out, otp);
  978. out.resetCRC32();
  979. otp.setOffset(out.length());
  980. while (otp.isReuseAsIs()) {
  981. try {
  982. reuseSupport.copyObjectAsIs(out, otp);
  983. out.endObject();
  984. otp.setCRC(out.getCRC32());
  985. return;
  986. } catch (StoredObjectRepresentationNotAvailableException gone) {
  987. if (otp.getOffset() == out.length()) {
  988. redoSearchForReuse(otp);
  989. continue;
  990. } else {
  991. // Object writing already started, we cannot recover.
  992. //
  993. CorruptObjectException coe;
  994. coe = new CorruptObjectException(otp, "");
  995. coe.initCause(gone);
  996. throw coe;
  997. }
  998. }
  999. }
  1000. // If we reached here, reuse wasn't possible.
  1001. //
  1002. if (otp.isDeltaRepresentation())
  1003. writeDeltaObjectDeflate(out, otp);
  1004. else
  1005. writeWholeObjectDeflate(out, otp);
  1006. out.endObject();
  1007. otp.setCRC(out.getCRC32());
  1008. }
  1009. private void writeBaseFirst(PackOutputStream out, final ObjectToPack otp)
  1010. throws IOException {
  1011. ObjectToPack baseInPack = otp.getDeltaBase();
  1012. if (baseInPack != null) {
  1013. if (!baseInPack.isWritten()) {
  1014. if (baseInPack.wantWrite()) {
  1015. // There is a cycle. Our caller is trying to write the
  1016. // object we want as a base, and called us. Turn off
  1017. // delta reuse so we can find another form.
  1018. //
  1019. reuseDeltas = false;
  1020. redoSearchForReuse(otp);
  1021. reuseDeltas = true;
  1022. } else {
  1023. writeObject(out, baseInPack);
  1024. }
  1025. }
  1026. } else if (!thin) {
  1027. // This should never occur, the base isn't in the pack and
  1028. // the pack isn't allowed to reference base outside objects.
  1029. // Write the object as a whole form, even if that is slow.
  1030. //
  1031. otp.clearDeltaBase();
  1032. otp.clearReuseAsIs();
  1033. }
  1034. }
  1035. private void redoSearchForReuse(final ObjectToPack otp) throws IOException,
  1036. MissingObjectException {
  1037. otp.clearDeltaBase();
  1038. otp.clearReuseAsIs();
  1039. reuseSupport.selectObjectRepresentation(this, otp);
  1040. }
  1041. private void writeWholeObjectDeflate(PackOutputStream out,
  1042. final ObjectToPack otp) throws IOException {
  1043. final Deflater deflater = deflater();
  1044. final ObjectLoader ldr = reader.open(otp, otp.getType());
  1045. out.writeHeader(otp, ldr.getSize());
  1046. deflater.reset();
  1047. DeflaterOutputStream dst = new DeflaterOutputStream(out, deflater);
  1048. ldr.copyTo(dst);
  1049. dst.finish();
  1050. }
  1051. private void writeDeltaObjectDeflate(PackOutputStream out,
  1052. final ObjectToPack otp) throws IOException {
  1053. DeltaCache.Ref ref = otp.popCachedDelta();
  1054. if (ref != null) {
  1055. byte[] zbuf = ref.get();
  1056. if (zbuf != null) {
  1057. out.writeHeader(otp, otp.getCachedSize());
  1058. out.write(zbuf);
  1059. return;
  1060. }
  1061. }
  1062. TemporaryBuffer.Heap delta = delta(otp);
  1063. out.writeHeader(otp, delta.length());
  1064. Deflater deflater = deflater();
  1065. deflater.reset();
  1066. DeflaterOutputStream dst = new DeflaterOutputStream(out, deflater);
  1067. delta.writeTo(dst, null);
  1068. dst.finish();
  1069. }
  1070. private TemporaryBuffer.Heap delta(final ObjectToPack otp)
  1071. throws IOException {
  1072. DeltaIndex index = new DeltaIndex(buffer(reader, otp.getDeltaBaseId()));
  1073. byte[] res = buffer(reader, otp);
  1074. // We never would have proposed this pair if the delta would be
  1075. // larger than the unpacked version of the object. So using it
  1076. // as our buffer limit is valid: we will never reach it.
  1077. //
  1078. TemporaryBuffer.Heap delta = new TemporaryBuffer.Heap(res.length);
  1079. index.encode(delta, res);
  1080. return delta;
  1081. }
  1082. byte[] buffer(ObjectReader or, AnyObjectId objId) throws IOException {
  1083. ObjectLoader ldr = or.open(objId);
  1084. if (!ldr.isLarge())
  1085. return ldr.getCachedBytes();
  1086. // PackWriter should have already pruned objects that
  1087. // are above the big file threshold, so our chances of
  1088. // the object being below it are very good. We really
  1089. // shouldn't be here, unless the implementation is odd.
  1090. // If it really is too big to work with, abort out now.
  1091. //
  1092. long sz = ldr.getSize();
  1093. if (getBigFileThreshold() <= sz || Integer.MAX_VALUE < sz)
  1094. throw new LargeObjectException(objId.copy());
  1095. // Its considered to be large by the loader, but we really
  1096. // want it in byte array format. Try to make it happen.
  1097. //
  1098. byte[] buf;
  1099. try {
  1100. buf = new byte[(int) sz];
  1101. } catch (OutOfMemoryError noMemory) {
  1102. LargeObjectException e;
  1103. e = new LargeObjectException(objId.copy());
  1104. e.initCause(noMemory);
  1105. throw e;
  1106. }
  1107. InputStream in = ldr.openStream();
  1108. try {
  1109. IO.readFully(in, buf, 0, buf.length);
  1110. } finally {
  1111. in.close();
  1112. }
  1113. return buf;
  1114. }
  1115. private Deflater deflater() {
  1116. if (myDeflater == null)
  1117. myDeflater = new Deflater(compressionLevel);
  1118. return myDeflater;
  1119. }
  1120. private void writeChecksum(PackOutputStream out) throws IOException {
  1121. packcsum = out.getDigest();
  1122. out.write(packcsum);
  1123. }
  1124. private ObjectWalk setUpWalker(
  1125. final Collection<? extends ObjectId> interestingObjects,
  1126. final Collection<? extends ObjectId> uninterestingObjects)
  1127. throws MissingObjectException, IOException,
  1128. IncorrectObjectTypeException {
  1129. final ObjectWalk walker = new ObjectWalk(reader);
  1130. walker.setRetainBody(false);
  1131. walker.sort(RevSort.COMMIT_TIME_DESC);
  1132. if (thin)
  1133. walker.sort(RevSort.BOUNDARY, true);
  1134. for (ObjectId id : interestingObjects) {
  1135. RevObject o = walker.parseAny(id);
  1136. walker.markStart(o);
  1137. }
  1138. if (uninterestingObjects != null) {
  1139. for (ObjectId id : uninterestingObjects) {
  1140. final RevObject o;
  1141. try {
  1142. o = walker.parseAny(id);
  1143. } catch (MissingObjectException x) {
  1144. if (ignoreMissingUninteresting)
  1145. continue;
  1146. throw x;
  1147. }
  1148. walker.markUninteresting(o);
  1149. }
  1150. }
  1151. return walker;
  1152. }
  1153. private void findObjectsToPack(final ProgressMonitor countingMonitor,
  1154. final ObjectWalk walker) throws MissingObjectException,
  1155. IncorrectObjectTypeException, IOException {
  1156. countingMonitor.beginTask(COUNTING_OBJECTS_PROGRESS,
  1157. ProgressMonitor.UNKNOWN);
  1158. RevObject o;
  1159. while ((o = walker.next()) != null) {
  1160. addObject(o, 0);
  1161. countingMonitor.update(1);
  1162. }
  1163. while ((o = walker.nextObject()) != null) {
  1164. addObject(o, walker.getPathHashCode());
  1165. countingMonitor.update(1);
  1166. }
  1167. countingMonitor.endTask();
  1168. }
  1169. /**
  1170. * Include one object to the output file.
  1171. * <p>
  1172. * Objects are written in the order they are added. If the same object is
  1173. * added twice, it may be written twice, creating a larger than necessary
  1174. * file.
  1175. *
  1176. * @param object
  1177. * the object to add.
  1178. * @throws IncorrectObjectTypeException
  1179. * the object is an unsupported type.
  1180. */
  1181. public void addObject(final RevObject object)
  1182. throws IncorrectObjectTypeException {
  1183. addObject(object, 0);
  1184. }
  1185. private void addObject(final RevObject object, final int pathHashCode)
  1186. throws IncorrectObjectTypeException {
  1187. if (object.has(RevFlag.UNINTERESTING)) {
  1188. switch (object.getType()) {
  1189. case Constants.OBJ_TREE:
  1190. case Constants.OBJ_BLOB:
  1191. ObjectToPack otp = new ObjectToPack(object);
  1192. otp.setPathHash(pathHashCode);
  1193. otp.setDoNotDelta(true);
  1194. edgeObjects.add(otp);
  1195. thin = true;
  1196. break;
  1197. }
  1198. return;
  1199. }
  1200. final ObjectToPack otp;
  1201. if (reuseSupport != null)
  1202. otp = reuseSupport.newObjectToPack(object);
  1203. else
  1204. otp = new ObjectToPack(object);
  1205. otp.setPathHash(pathHashCode);
  1206. try {
  1207. objectsLists[object.getType()].add(otp);
  1208. } catch (ArrayIndexOutOfBoundsException x) {
  1209. throw new IncorrectObjectTypeException(object,
  1210. JGitText.get().incorrectObjectType_COMMITnorTREEnorBLOBnorTAG);
  1211. } catch (UnsupportedOperationException x) {
  1212. // index pointing to "dummy" empty list
  1213. throw new IncorrectObjectTypeException(object,
  1214. JGitText.get().incorrectObjectType_COMMITnorTREEnorBLOBnorTAG);
  1215. }
  1216. objectsMap.add(otp);
  1217. }
  1218. /**
  1219. * Select an object representation for this writer.
  1220. * <p>
  1221. * An {@link ObjectReader} implementation should invoke this method once for
  1222. * each representation available for an object, to allow the writer to find
  1223. * the most suitable one for the output.
  1224. *
  1225. * @param otp
  1226. * the object being packed.
  1227. * @param next
  1228. * the next available representation from the repository.
  1229. */
  1230. public void select(ObjectToPack otp, StoredObjectRepresentation next) {
  1231. int nFmt = next.getFormat();
  1232. int nWeight;
  1233. if (otp.isReuseAsIs()) {
  1234. // We've already chosen to reuse a packed form, if next
  1235. // cannot beat that break out early.
  1236. //
  1237. if (PACK_WHOLE < nFmt)
  1238. return; // next isn't packed
  1239. else if (PACK_DELTA < nFmt && otp.isDeltaRepresentation())
  1240. return; // next isn't a delta, but we are
  1241. nWeight = next.getWeight();
  1242. if (otp.getWeight() <= nWeight)
  1243. return; // next would be bigger
  1244. } else
  1245. nWeight = next.getWeight();
  1246. if (nFmt == PACK_DELTA && reuseDeltas) {
  1247. ObjectId baseId = next.getDeltaBase();
  1248. ObjectToPack ptr = objectsMap.get(baseId);
  1249. if (ptr != null) {
  1250. otp.setDeltaBase(ptr);
  1251. otp.setReuseAsIs();
  1252. otp.setWeight(nWeight);
  1253. } else if (thin && edgeObjects.contains(baseId)) {
  1254. otp.setDeltaBase(baseId);
  1255. otp.setReuseAsIs();
  1256. otp.setWeight(nWeight);
  1257. } else {
  1258. otp.clearDeltaBase();
  1259. otp.clearReuseAsIs();
  1260. }
  1261. } else if (nFmt == PACK_WHOLE && reuseObjects) {
  1262. otp.clearDeltaBase();
  1263. otp.setReuseAsIs();
  1264. otp.setWeight(nWeight);
  1265. } else {
  1266. otp.clearDeltaBase();
  1267. otp.clearReuseAsIs();
  1268. }
  1269. otp.select(next);
  1270. }
  1271. }