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.

WalkFetchConnection.java 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871
  1. /*
  2. * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
  3. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
  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.transport;
  45. import java.io.File;
  46. import java.io.FileNotFoundException;
  47. import java.io.FileOutputStream;
  48. import java.io.IOException;
  49. import java.text.MessageFormat;
  50. import java.util.ArrayList;
  51. import java.util.Collection;
  52. import java.util.HashMap;
  53. import java.util.HashSet;
  54. import java.util.Iterator;
  55. import java.util.LinkedList;
  56. import java.util.List;
  57. import java.util.Set;
  58. import org.eclipse.jgit.JGitText;
  59. import org.eclipse.jgit.errors.CompoundException;
  60. import org.eclipse.jgit.errors.CorruptObjectException;
  61. import org.eclipse.jgit.errors.MissingObjectException;
  62. import org.eclipse.jgit.errors.TransportException;
  63. import org.eclipse.jgit.lib.AnyObjectId;
  64. import org.eclipse.jgit.lib.Constants;
  65. import org.eclipse.jgit.lib.FileMode;
  66. import org.eclipse.jgit.lib.MutableObjectId;
  67. import org.eclipse.jgit.lib.ObjectChecker;
  68. import org.eclipse.jgit.lib.ObjectId;
  69. import org.eclipse.jgit.lib.ObjectInserter;
  70. import org.eclipse.jgit.lib.ObjectLoader;
  71. import org.eclipse.jgit.lib.ObjectReader;
  72. import org.eclipse.jgit.lib.ProgressMonitor;
  73. import org.eclipse.jgit.lib.Ref;
  74. import org.eclipse.jgit.lib.Repository;
  75. import org.eclipse.jgit.revwalk.DateRevQueue;
  76. import org.eclipse.jgit.revwalk.RevCommit;
  77. import org.eclipse.jgit.revwalk.RevFlag;
  78. import org.eclipse.jgit.revwalk.RevObject;
  79. import org.eclipse.jgit.revwalk.RevTag;
  80. import org.eclipse.jgit.revwalk.RevTree;
  81. import org.eclipse.jgit.revwalk.RevWalk;
  82. import org.eclipse.jgit.storage.file.ObjectDirectory;
  83. import org.eclipse.jgit.storage.file.PackIndex;
  84. import org.eclipse.jgit.storage.file.PackLock;
  85. import org.eclipse.jgit.storage.file.UnpackedObject;
  86. import org.eclipse.jgit.treewalk.TreeWalk;
  87. import org.eclipse.jgit.util.FileUtils;
  88. /**
  89. * Generic fetch support for dumb transport protocols.
  90. * <p>
  91. * Since there are no Git-specific smarts on the remote side of the connection
  92. * the client side must determine which objects it needs to copy in order to
  93. * completely fetch the requested refs and their history. The generic walk
  94. * support in this class parses each individual object (once it has been copied
  95. * to the local repository) and examines the list of objects that must also be
  96. * copied to create a complete history. Objects which are already available
  97. * locally are retained (and not copied), saving bandwidth for incremental
  98. * fetches. Pack files are copied from the remote repository only as a last
  99. * resort, as the entire pack must be copied locally in order to access any
  100. * single object.
  101. * <p>
  102. * This fetch connection does not actually perform the object data transfer.
  103. * Instead it delegates the transfer to a {@link WalkRemoteObjectDatabase},
  104. * which knows how to read individual files from the remote repository and
  105. * supply the data as a standard Java InputStream.
  106. *
  107. * @see WalkRemoteObjectDatabase
  108. */
  109. class WalkFetchConnection extends BaseFetchConnection {
  110. /** The repository this transport fetches into, or pushes out of. */
  111. private final Repository local;
  112. /** If not null the validator for received objects. */
  113. private final ObjectChecker objCheck;
  114. /**
  115. * List of all remote repositories we may need to get objects out of.
  116. * <p>
  117. * The first repository in the list is the one we were asked to fetch from;
  118. * the remaining repositories point to the alternate locations we can fetch
  119. * objects through.
  120. */
  121. private final List<WalkRemoteObjectDatabase> remotes;
  122. /** Most recently used item in {@link #remotes}. */
  123. private int lastRemoteIdx;
  124. private final RevWalk revWalk;
  125. private final TreeWalk treeWalk;
  126. /** Objects whose direct dependents we know we have (or will have). */
  127. private final RevFlag COMPLETE;
  128. /** Objects that have already entered {@link #workQueue}. */
  129. private final RevFlag IN_WORK_QUEUE;
  130. /** Commits that have already entered {@link #localCommitQueue}. */
  131. private final RevFlag LOCALLY_SEEN;
  132. /** Commits already reachable from all local refs. */
  133. private final DateRevQueue localCommitQueue;
  134. /** Objects we need to copy from the remote repository. */
  135. private LinkedList<ObjectId> workQueue;
  136. /** Databases we have not yet obtained the list of packs from. */
  137. private final LinkedList<WalkRemoteObjectDatabase> noPacksYet;
  138. /** Databases we have not yet obtained the alternates from. */
  139. private final LinkedList<WalkRemoteObjectDatabase> noAlternatesYet;
  140. /** Packs we have discovered, but have not yet fetched locally. */
  141. private final LinkedList<RemotePack> unfetchedPacks;
  142. /**
  143. * Packs whose indexes we have looked at in {@link #unfetchedPacks}.
  144. * <p>
  145. * We try to avoid getting duplicate copies of the same pack through
  146. * multiple alternates by only looking at packs whose names are not yet in
  147. * this collection.
  148. */
  149. private final Set<String> packsConsidered;
  150. private final MutableObjectId idBuffer = new MutableObjectId();
  151. /**
  152. * Errors received while trying to obtain an object.
  153. * <p>
  154. * If the fetch winds up failing because we cannot locate a specific object
  155. * then we need to report all errors related to that object back to the
  156. * caller as there may be cascading failures.
  157. */
  158. private final HashMap<ObjectId, List<Throwable>> fetchErrors;
  159. private String lockMessage;
  160. private final List<PackLock> packLocks;
  161. /** Inserter to write objects onto {@link #local}. */
  162. private final ObjectInserter inserter;
  163. /** Inserter to read objects from {@link #local}. */
  164. private final ObjectReader reader;
  165. WalkFetchConnection(final WalkTransport t, final WalkRemoteObjectDatabase w) {
  166. Transport wt = (Transport)t;
  167. local = wt.local;
  168. objCheck = wt.isCheckFetchedObjects() ? new ObjectChecker() : null;
  169. inserter = local.newObjectInserter();
  170. reader = local.newObjectReader();
  171. remotes = new ArrayList<WalkRemoteObjectDatabase>();
  172. remotes.add(w);
  173. unfetchedPacks = new LinkedList<RemotePack>();
  174. packsConsidered = new HashSet<String>();
  175. noPacksYet = new LinkedList<WalkRemoteObjectDatabase>();
  176. noPacksYet.add(w);
  177. noAlternatesYet = new LinkedList<WalkRemoteObjectDatabase>();
  178. noAlternatesYet.add(w);
  179. fetchErrors = new HashMap<ObjectId, List<Throwable>>();
  180. packLocks = new ArrayList<PackLock>(4);
  181. revWalk = new RevWalk(reader);
  182. revWalk.setRetainBody(false);
  183. treeWalk = new TreeWalk(reader);
  184. COMPLETE = revWalk.newFlag("COMPLETE");
  185. IN_WORK_QUEUE = revWalk.newFlag("IN_WORK_QUEUE");
  186. LOCALLY_SEEN = revWalk.newFlag("LOCALLY_SEEN");
  187. localCommitQueue = new DateRevQueue();
  188. workQueue = new LinkedList<ObjectId>();
  189. }
  190. public boolean didFetchTestConnectivity() {
  191. return true;
  192. }
  193. @Override
  194. protected void doFetch(final ProgressMonitor monitor,
  195. final Collection<Ref> want, final Set<ObjectId> have)
  196. throws TransportException {
  197. markLocalRefsComplete(have);
  198. queueWants(want);
  199. while (!monitor.isCancelled() && !workQueue.isEmpty()) {
  200. final ObjectId id = workQueue.removeFirst();
  201. if (!(id instanceof RevObject) || !((RevObject) id).has(COMPLETE))
  202. downloadObject(monitor, id);
  203. process(id);
  204. }
  205. }
  206. public Collection<PackLock> getPackLocks() {
  207. return packLocks;
  208. }
  209. public void setPackLockMessage(final String message) {
  210. lockMessage = message;
  211. }
  212. @Override
  213. public void close() {
  214. inserter.release();
  215. reader.release();
  216. for (final RemotePack p : unfetchedPacks) {
  217. if (p.tmpIdx != null)
  218. p.tmpIdx.delete();
  219. }
  220. for (final WalkRemoteObjectDatabase r : remotes)
  221. r.close();
  222. }
  223. private void queueWants(final Collection<Ref> want)
  224. throws TransportException {
  225. final HashSet<ObjectId> inWorkQueue = new HashSet<ObjectId>();
  226. for (final Ref r : want) {
  227. final ObjectId id = r.getObjectId();
  228. try {
  229. final RevObject obj = revWalk.parseAny(id);
  230. if (obj.has(COMPLETE))
  231. continue;
  232. if (inWorkQueue.add(id)) {
  233. obj.add(IN_WORK_QUEUE);
  234. workQueue.add(obj);
  235. }
  236. } catch (MissingObjectException e) {
  237. if (inWorkQueue.add(id))
  238. workQueue.add(id);
  239. } catch (IOException e) {
  240. throw new TransportException(MessageFormat.format(JGitText.get().cannotRead, id.name()), e);
  241. }
  242. }
  243. }
  244. private void process(final ObjectId id) throws TransportException {
  245. final RevObject obj;
  246. try {
  247. if (id instanceof RevObject) {
  248. obj = (RevObject) id;
  249. if (obj.has(COMPLETE))
  250. return;
  251. revWalk.parseHeaders(obj);
  252. } else {
  253. obj = revWalk.parseAny(id);
  254. if (obj.has(COMPLETE))
  255. return;
  256. }
  257. } catch (IOException e) {
  258. throw new TransportException(MessageFormat.format(JGitText.get().cannotRead, id.name()), e);
  259. }
  260. switch (obj.getType()) {
  261. case Constants.OBJ_BLOB:
  262. processBlob(obj);
  263. break;
  264. case Constants.OBJ_TREE:
  265. processTree(obj);
  266. break;
  267. case Constants.OBJ_COMMIT:
  268. processCommit(obj);
  269. break;
  270. case Constants.OBJ_TAG:
  271. processTag(obj);
  272. break;
  273. default:
  274. throw new TransportException(MessageFormat.format(JGitText.get().unknownObjectType, id.name()));
  275. }
  276. // If we had any prior errors fetching this object they are
  277. // now resolved, as the object was parsed successfully.
  278. //
  279. fetchErrors.remove(id);
  280. }
  281. private void processBlob(final RevObject obj) throws TransportException {
  282. try {
  283. if (reader.has(obj, Constants.OBJ_BLOB))
  284. obj.add(COMPLETE);
  285. else
  286. throw new TransportException(MessageFormat.format(JGitText
  287. .get().cannotReadBlob, obj.name()),
  288. new MissingObjectException(obj, Constants.TYPE_BLOB));
  289. } catch (IOException error) {
  290. throw new TransportException(MessageFormat.format(
  291. JGitText.get().cannotReadBlob, obj.name()), error);
  292. }
  293. }
  294. private void processTree(final RevObject obj) throws TransportException {
  295. try {
  296. treeWalk.reset(obj);
  297. while (treeWalk.next()) {
  298. final FileMode mode = treeWalk.getFileMode(0);
  299. final int sType = mode.getObjectType();
  300. switch (sType) {
  301. case Constants.OBJ_BLOB:
  302. case Constants.OBJ_TREE:
  303. treeWalk.getObjectId(idBuffer, 0);
  304. needs(revWalk.lookupAny(idBuffer, sType));
  305. continue;
  306. default:
  307. if (FileMode.GITLINK.equals(mode))
  308. continue;
  309. treeWalk.getObjectId(idBuffer, 0);
  310. throw new CorruptObjectException(MessageFormat.format(JGitText.get().invalidModeFor
  311. , mode, idBuffer.name(), treeWalk.getPathString(), obj.getId().name()));
  312. }
  313. }
  314. } catch (IOException ioe) {
  315. throw new TransportException(MessageFormat.format(JGitText.get().cannotReadTree, obj.name()), ioe);
  316. }
  317. obj.add(COMPLETE);
  318. }
  319. private void processCommit(final RevObject obj) throws TransportException {
  320. final RevCommit commit = (RevCommit) obj;
  321. markLocalCommitsComplete(commit.getCommitTime());
  322. needs(commit.getTree());
  323. for (final RevCommit p : commit.getParents())
  324. needs(p);
  325. obj.add(COMPLETE);
  326. }
  327. private void processTag(final RevObject obj) {
  328. final RevTag tag = (RevTag) obj;
  329. needs(tag.getObject());
  330. obj.add(COMPLETE);
  331. }
  332. private void needs(final RevObject obj) {
  333. if (obj.has(COMPLETE))
  334. return;
  335. if (!obj.has(IN_WORK_QUEUE)) {
  336. obj.add(IN_WORK_QUEUE);
  337. workQueue.add(obj);
  338. }
  339. }
  340. private void downloadObject(final ProgressMonitor pm, final AnyObjectId id)
  341. throws TransportException {
  342. if (alreadyHave(id))
  343. return;
  344. for (;;) {
  345. // Try a pack file we know about, but don't have yet. Odds are
  346. // that if it has this object, it has others related to it so
  347. // getting the pack is a good bet.
  348. //
  349. if (downloadPackedObject(pm, id))
  350. return;
  351. // Search for a loose object over all alternates, starting
  352. // from the one we last successfully located an object through.
  353. //
  354. final String idStr = id.name();
  355. final String subdir = idStr.substring(0, 2);
  356. final String file = idStr.substring(2);
  357. final String looseName = subdir + "/" + file;
  358. for (int i = lastRemoteIdx; i < remotes.size(); i++) {
  359. if (downloadLooseObject(id, looseName, remotes.get(i))) {
  360. lastRemoteIdx = i;
  361. return;
  362. }
  363. }
  364. for (int i = 0; i < lastRemoteIdx; i++) {
  365. if (downloadLooseObject(id, looseName, remotes.get(i))) {
  366. lastRemoteIdx = i;
  367. return;
  368. }
  369. }
  370. // Try to obtain more pack information and search those.
  371. //
  372. while (!noPacksYet.isEmpty()) {
  373. final WalkRemoteObjectDatabase wrr = noPacksYet.removeFirst();
  374. final Collection<String> packNameList;
  375. try {
  376. pm.beginTask("Listing packs", ProgressMonitor.UNKNOWN);
  377. packNameList = wrr.getPackNames();
  378. } catch (IOException e) {
  379. // Try another repository.
  380. //
  381. recordError(id, e);
  382. continue;
  383. } finally {
  384. pm.endTask();
  385. }
  386. if (packNameList == null || packNameList.isEmpty())
  387. continue;
  388. for (final String packName : packNameList) {
  389. if (packsConsidered.add(packName))
  390. unfetchedPacks.add(new RemotePack(wrr, packName));
  391. }
  392. if (downloadPackedObject(pm, id))
  393. return;
  394. }
  395. // Try to expand the first alternate we haven't expanded yet.
  396. //
  397. Collection<WalkRemoteObjectDatabase> al = expandOneAlternate(id, pm);
  398. if (al != null && !al.isEmpty()) {
  399. for (final WalkRemoteObjectDatabase alt : al) {
  400. remotes.add(alt);
  401. noPacksYet.add(alt);
  402. noAlternatesYet.add(alt);
  403. }
  404. continue;
  405. }
  406. // We could not obtain the object. There may be reasons why.
  407. //
  408. List<Throwable> failures = fetchErrors.get(id);
  409. final TransportException te;
  410. te = new TransportException(MessageFormat.format(JGitText.get().cannotGet, id.name()));
  411. if (failures != null && !failures.isEmpty()) {
  412. if (failures.size() == 1)
  413. te.initCause(failures.get(0));
  414. else
  415. te.initCause(new CompoundException(failures));
  416. }
  417. throw te;
  418. }
  419. }
  420. private boolean alreadyHave(final AnyObjectId id) throws TransportException {
  421. try {
  422. return reader.has(id);
  423. } catch (IOException error) {
  424. throw new TransportException(MessageFormat.format(
  425. JGitText.get().cannotReadObject, id.name()), error);
  426. }
  427. }
  428. private boolean downloadPackedObject(final ProgressMonitor monitor,
  429. final AnyObjectId id) throws TransportException {
  430. // Search for the object in a remote pack whose index we have,
  431. // but whose pack we do not yet have.
  432. //
  433. final Iterator<RemotePack> packItr = unfetchedPacks.iterator();
  434. while (packItr.hasNext() && !monitor.isCancelled()) {
  435. final RemotePack pack = packItr.next();
  436. try {
  437. pack.openIndex(monitor);
  438. } catch (IOException err) {
  439. // If the index won't open its either not found or
  440. // its a format we don't recognize. In either case
  441. // we may still be able to obtain the object from
  442. // another source, so don't consider it a failure.
  443. //
  444. recordError(id, err);
  445. packItr.remove();
  446. continue;
  447. }
  448. if (monitor.isCancelled()) {
  449. // If we were cancelled while the index was opening
  450. // the open may have aborted. We can't search an
  451. // unopen index.
  452. //
  453. return false;
  454. }
  455. if (!pack.index.hasObject(id)) {
  456. // Not in this pack? Try another.
  457. //
  458. continue;
  459. }
  460. // It should be in the associated pack. Download that
  461. // and attach it to the local repository so we can use
  462. // all of the contained objects.
  463. //
  464. try {
  465. pack.downloadPack(monitor);
  466. } catch (IOException err) {
  467. // If the pack failed to download, index correctly,
  468. // or open in the local repository we may still be
  469. // able to obtain this object from another pack or
  470. // an alternate.
  471. //
  472. recordError(id, err);
  473. continue;
  474. } finally {
  475. // If the pack was good its in the local repository
  476. // and Repository.hasObject(id) will succeed in the
  477. // future, so we do not need this data anymore. If
  478. // it failed the index and pack are unusable and we
  479. // shouldn't consult them again.
  480. //
  481. try {
  482. if (pack.tmpIdx != null)
  483. FileUtils.delete(pack.tmpIdx);
  484. } catch (IOException e) {
  485. throw new TransportException(e.getMessage(), e);
  486. }
  487. packItr.remove();
  488. }
  489. if (!alreadyHave(id)) {
  490. // What the hell? This pack claimed to have
  491. // the object, but after indexing we didn't
  492. // actually find it in the pack.
  493. //
  494. recordError(id, new FileNotFoundException(MessageFormat.format(
  495. JGitText.get().objectNotFoundIn, id.name(), pack.packName)));
  496. continue;
  497. }
  498. // Complete any other objects that we can.
  499. //
  500. final Iterator<ObjectId> pending = swapFetchQueue();
  501. while (pending.hasNext()) {
  502. final ObjectId p = pending.next();
  503. if (pack.index.hasObject(p)) {
  504. pending.remove();
  505. process(p);
  506. } else {
  507. workQueue.add(p);
  508. }
  509. }
  510. return true;
  511. }
  512. return false;
  513. }
  514. private Iterator<ObjectId> swapFetchQueue() {
  515. final Iterator<ObjectId> r = workQueue.iterator();
  516. workQueue = new LinkedList<ObjectId>();
  517. return r;
  518. }
  519. private boolean downloadLooseObject(final AnyObjectId id,
  520. final String looseName, final WalkRemoteObjectDatabase remote)
  521. throws TransportException {
  522. try {
  523. final byte[] compressed = remote.open(looseName).toArray();
  524. verifyAndInsertLooseObject(id, compressed);
  525. return true;
  526. } catch (FileNotFoundException e) {
  527. // Not available in a loose format from this alternate?
  528. // Try another strategy to get the object.
  529. //
  530. recordError(id, e);
  531. return false;
  532. } catch (IOException e) {
  533. throw new TransportException(MessageFormat.format(JGitText.get().cannotDownload, id.name()), e);
  534. }
  535. }
  536. private void verifyAndInsertLooseObject(final AnyObjectId id,
  537. final byte[] compressed) throws IOException {
  538. final ObjectLoader uol;
  539. try {
  540. uol = UnpackedObject.parse(compressed, id);
  541. } catch (CorruptObjectException parsingError) {
  542. // Some HTTP servers send back a "200 OK" status with an HTML
  543. // page that explains the requested file could not be found.
  544. // These servers are most certainly misconfigured, but many
  545. // of them exist in the world, and many of those are hosting
  546. // Git repositories.
  547. //
  548. // Since an HTML page is unlikely to hash to one of our loose
  549. // objects we treat this condition as a FileNotFoundException
  550. // and attempt to recover by getting the object from another
  551. // source.
  552. //
  553. final FileNotFoundException e;
  554. e = new FileNotFoundException(id.name());
  555. e.initCause(parsingError);
  556. throw e;
  557. }
  558. final int type = uol.getType();
  559. final byte[] raw = uol.getCachedBytes();
  560. if (objCheck != null) {
  561. try {
  562. objCheck.check(type, raw);
  563. } catch (CorruptObjectException e) {
  564. throw new TransportException(MessageFormat.format(JGitText.get().transportExceptionInvalid
  565. , Constants.typeString(type), id.name(), e.getMessage()));
  566. }
  567. }
  568. ObjectId act = inserter.insert(type, raw);
  569. if (!AnyObjectId.equals(id, act)) {
  570. throw new TransportException(MessageFormat.format(JGitText.get().incorrectHashFor
  571. , id.name(), act.name(), Constants.typeString(type), compressed.length));
  572. }
  573. inserter.flush();
  574. }
  575. private Collection<WalkRemoteObjectDatabase> expandOneAlternate(
  576. final AnyObjectId id, final ProgressMonitor pm) {
  577. while (!noAlternatesYet.isEmpty()) {
  578. final WalkRemoteObjectDatabase wrr = noAlternatesYet.removeFirst();
  579. try {
  580. pm.beginTask(JGitText.get().listingAlternates, ProgressMonitor.UNKNOWN);
  581. Collection<WalkRemoteObjectDatabase> altList = wrr
  582. .getAlternates();
  583. if (altList != null && !altList.isEmpty())
  584. return altList;
  585. } catch (IOException e) {
  586. // Try another repository.
  587. //
  588. recordError(id, e);
  589. } finally {
  590. pm.endTask();
  591. }
  592. }
  593. return null;
  594. }
  595. private void markLocalRefsComplete(final Set<ObjectId> have) throws TransportException {
  596. for (final Ref r : local.getAllRefs().values()) {
  597. try {
  598. markLocalObjComplete(revWalk.parseAny(r.getObjectId()));
  599. } catch (IOException readError) {
  600. throw new TransportException(MessageFormat.format(JGitText.get().localRefIsMissingObjects, r.getName()), readError);
  601. }
  602. }
  603. for (final ObjectId id : have) {
  604. try {
  605. markLocalObjComplete(revWalk.parseAny(id));
  606. } catch (IOException readError) {
  607. throw new TransportException(MessageFormat.format(JGitText.get().transportExceptionMissingAssumed, id.name()), readError);
  608. }
  609. }
  610. }
  611. private void markLocalObjComplete(RevObject obj) throws IOException {
  612. while (obj.getType() == Constants.OBJ_TAG) {
  613. obj.add(COMPLETE);
  614. obj = ((RevTag) obj).getObject();
  615. revWalk.parseHeaders(obj);
  616. }
  617. switch (obj.getType()) {
  618. case Constants.OBJ_BLOB:
  619. obj.add(COMPLETE);
  620. break;
  621. case Constants.OBJ_COMMIT:
  622. pushLocalCommit((RevCommit) obj);
  623. break;
  624. case Constants.OBJ_TREE:
  625. markTreeComplete((RevTree) obj);
  626. break;
  627. }
  628. }
  629. private void markLocalCommitsComplete(final int until)
  630. throws TransportException {
  631. try {
  632. for (;;) {
  633. final RevCommit c = localCommitQueue.peek();
  634. if (c == null || c.getCommitTime() < until)
  635. return;
  636. localCommitQueue.next();
  637. markTreeComplete(c.getTree());
  638. for (final RevCommit p : c.getParents())
  639. pushLocalCommit(p);
  640. }
  641. } catch (IOException err) {
  642. throw new TransportException(JGitText.get().localObjectsIncomplete, err);
  643. }
  644. }
  645. private void pushLocalCommit(final RevCommit p)
  646. throws MissingObjectException, IOException {
  647. if (p.has(LOCALLY_SEEN))
  648. return;
  649. revWalk.parseHeaders(p);
  650. p.add(LOCALLY_SEEN);
  651. p.add(COMPLETE);
  652. p.carry(COMPLETE);
  653. localCommitQueue.add(p);
  654. }
  655. private void markTreeComplete(final RevTree tree) throws IOException {
  656. if (tree.has(COMPLETE))
  657. return;
  658. tree.add(COMPLETE);
  659. treeWalk.reset(tree);
  660. while (treeWalk.next()) {
  661. final FileMode mode = treeWalk.getFileMode(0);
  662. final int sType = mode.getObjectType();
  663. switch (sType) {
  664. case Constants.OBJ_BLOB:
  665. treeWalk.getObjectId(idBuffer, 0);
  666. revWalk.lookupAny(idBuffer, sType).add(COMPLETE);
  667. continue;
  668. case Constants.OBJ_TREE: {
  669. treeWalk.getObjectId(idBuffer, 0);
  670. final RevObject o = revWalk.lookupAny(idBuffer, sType);
  671. if (!o.has(COMPLETE)) {
  672. o.add(COMPLETE);
  673. treeWalk.enterSubtree();
  674. }
  675. continue;
  676. }
  677. default:
  678. if (FileMode.GITLINK.equals(mode))
  679. continue;
  680. treeWalk.getObjectId(idBuffer, 0);
  681. throw new CorruptObjectException(MessageFormat.format(JGitText.get().corruptObjectInvalidMode3
  682. , mode, idBuffer.name(), treeWalk.getPathString(), tree.name()));
  683. }
  684. }
  685. }
  686. private void recordError(final AnyObjectId id, final Throwable what) {
  687. final ObjectId objId = id.copy();
  688. List<Throwable> errors = fetchErrors.get(objId);
  689. if (errors == null) {
  690. errors = new ArrayList<Throwable>(2);
  691. fetchErrors.put(objId, errors);
  692. }
  693. errors.add(what);
  694. }
  695. private class RemotePack {
  696. final WalkRemoteObjectDatabase connection;
  697. final String packName;
  698. final String idxName;
  699. File tmpIdx;
  700. PackIndex index;
  701. RemotePack(final WalkRemoteObjectDatabase c, final String pn) {
  702. connection = c;
  703. packName = pn;
  704. idxName = packName.substring(0, packName.length() - 5) + ".idx";
  705. String tn = idxName;
  706. if (tn.startsWith("pack-"))
  707. tn = tn.substring(5);
  708. if (tn.endsWith(".idx"))
  709. tn = tn.substring(0, tn.length() - 4);
  710. if (local.getObjectDatabase() instanceof ObjectDirectory) {
  711. tmpIdx = new File(((ObjectDirectory) local.getObjectDatabase())
  712. .getDirectory(), "walk-" + tn + ".walkidx");
  713. }
  714. }
  715. void openIndex(final ProgressMonitor pm) throws IOException {
  716. if (index != null)
  717. return;
  718. if (tmpIdx == null)
  719. tmpIdx = File.createTempFile("jgit-walk-", ".idx");
  720. else if (tmpIdx.isFile()) {
  721. try {
  722. index = PackIndex.open(tmpIdx);
  723. return;
  724. } catch (FileNotFoundException err) {
  725. // Fall through and get the file.
  726. }
  727. }
  728. final WalkRemoteObjectDatabase.FileStream s;
  729. s = connection.open("pack/" + idxName);
  730. pm.beginTask("Get " + idxName.substring(0, 12) + "..idx",
  731. s.length < 0 ? ProgressMonitor.UNKNOWN
  732. : (int) (s.length / 1024));
  733. try {
  734. final FileOutputStream fos = new FileOutputStream(tmpIdx);
  735. try {
  736. final byte[] buf = new byte[2048];
  737. int cnt;
  738. while (!pm.isCancelled() && (cnt = s.in.read(buf)) >= 0) {
  739. fos.write(buf, 0, cnt);
  740. pm.update(cnt / 1024);
  741. }
  742. } finally {
  743. fos.close();
  744. }
  745. } catch (IOException err) {
  746. FileUtils.delete(tmpIdx);
  747. throw err;
  748. } finally {
  749. s.in.close();
  750. }
  751. pm.endTask();
  752. if (pm.isCancelled()) {
  753. FileUtils.delete(tmpIdx);
  754. return;
  755. }
  756. try {
  757. index = PackIndex.open(tmpIdx);
  758. } catch (IOException e) {
  759. FileUtils.delete(tmpIdx);
  760. throw e;
  761. }
  762. }
  763. void downloadPack(final ProgressMonitor monitor) throws IOException {
  764. String name = "pack/" + packName;
  765. WalkRemoteObjectDatabase.FileStream s = connection.open(name);
  766. PackParser parser = inserter.newPackParser(s.in);
  767. parser.setAllowThin(false);
  768. parser.setObjectChecker(objCheck);
  769. parser.setLockMessage(lockMessage);
  770. PackLock lock = parser.parse(monitor);
  771. if (lock != null)
  772. packLocks.add(lock);
  773. inserter.flush();
  774. }
  775. }
  776. }