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 27KB

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