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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
  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. final Repository local;
  114. /** If not null the validator for received objects. */
  115. 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. String lockMessage;
  162. final List<PackLock> packLocks;
  163. /** Inserter to write objects onto {@link #local}. */
  164. 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 = inserter.newReader();
  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. try {
  208. inserter.flush();
  209. } catch (IOException e) {
  210. throw new TransportException(e.getMessage(), e);
  211. }
  212. }
  213. public Collection<PackLock> getPackLocks() {
  214. return packLocks;
  215. }
  216. public void setPackLockMessage(final String message) {
  217. lockMessage = message;
  218. }
  219. @Override
  220. public void close() {
  221. inserter.close();
  222. reader.close();
  223. for (final RemotePack p : unfetchedPacks) {
  224. if (p.tmpIdx != null)
  225. p.tmpIdx.delete();
  226. }
  227. for (final WalkRemoteObjectDatabase r : remotes)
  228. r.close();
  229. }
  230. private void queueWants(final Collection<Ref> want)
  231. throws TransportException {
  232. final HashSet<ObjectId> inWorkQueue = new HashSet<ObjectId>();
  233. for (final Ref r : want) {
  234. final ObjectId id = r.getObjectId();
  235. if (id == null) {
  236. throw new NullPointerException(MessageFormat.format(
  237. JGitText.get().transportProvidedRefWithNoObjectId, r.getName()));
  238. }
  239. try {
  240. final RevObject obj = revWalk.parseAny(id);
  241. if (obj.has(COMPLETE))
  242. continue;
  243. if (inWorkQueue.add(id)) {
  244. obj.add(IN_WORK_QUEUE);
  245. workQueue.add(obj);
  246. }
  247. } catch (MissingObjectException e) {
  248. if (inWorkQueue.add(id))
  249. workQueue.add(id);
  250. } catch (IOException e) {
  251. throw new TransportException(MessageFormat.format(JGitText.get().cannotRead, id.name()), e);
  252. }
  253. }
  254. }
  255. private void process(final ObjectId id) throws TransportException {
  256. final RevObject obj;
  257. try {
  258. if (id instanceof RevObject) {
  259. obj = (RevObject) id;
  260. if (obj.has(COMPLETE))
  261. return;
  262. revWalk.parseHeaders(obj);
  263. } else {
  264. obj = revWalk.parseAny(id);
  265. if (obj.has(COMPLETE))
  266. return;
  267. }
  268. } catch (IOException e) {
  269. throw new TransportException(MessageFormat.format(JGitText.get().cannotRead, id.name()), e);
  270. }
  271. switch (obj.getType()) {
  272. case Constants.OBJ_BLOB:
  273. processBlob(obj);
  274. break;
  275. case Constants.OBJ_TREE:
  276. processTree(obj);
  277. break;
  278. case Constants.OBJ_COMMIT:
  279. processCommit(obj);
  280. break;
  281. case Constants.OBJ_TAG:
  282. processTag(obj);
  283. break;
  284. default:
  285. throw new TransportException(MessageFormat.format(JGitText.get().unknownObjectType, id.name()));
  286. }
  287. // If we had any prior errors fetching this object they are
  288. // now resolved, as the object was parsed successfully.
  289. //
  290. fetchErrors.remove(id);
  291. }
  292. private void processBlob(final RevObject obj) throws TransportException {
  293. try {
  294. if (reader.has(obj, Constants.OBJ_BLOB))
  295. obj.add(COMPLETE);
  296. else
  297. throw new TransportException(MessageFormat.format(JGitText
  298. .get().cannotReadBlob, obj.name()),
  299. new MissingObjectException(obj, Constants.TYPE_BLOB));
  300. } catch (IOException error) {
  301. throw new TransportException(MessageFormat.format(
  302. JGitText.get().cannotReadBlob, obj.name()), error);
  303. }
  304. }
  305. private void processTree(final RevObject obj) throws TransportException {
  306. try {
  307. treeWalk.reset(obj);
  308. while (treeWalk.next()) {
  309. final FileMode mode = treeWalk.getFileMode(0);
  310. final int sType = mode.getObjectType();
  311. switch (sType) {
  312. case Constants.OBJ_BLOB:
  313. case Constants.OBJ_TREE:
  314. treeWalk.getObjectId(idBuffer, 0);
  315. needs(revWalk.lookupAny(idBuffer, sType));
  316. continue;
  317. default:
  318. if (FileMode.GITLINK.equals(mode))
  319. continue;
  320. treeWalk.getObjectId(idBuffer, 0);
  321. throw new CorruptObjectException(MessageFormat.format(JGitText.get().invalidModeFor
  322. , mode, idBuffer.name(), treeWalk.getPathString(), obj.getId().name()));
  323. }
  324. }
  325. } catch (IOException ioe) {
  326. throw new TransportException(MessageFormat.format(JGitText.get().cannotReadTree, obj.name()), ioe);
  327. }
  328. obj.add(COMPLETE);
  329. }
  330. private void processCommit(final RevObject obj) throws TransportException {
  331. final RevCommit commit = (RevCommit) obj;
  332. markLocalCommitsComplete(commit.getCommitTime());
  333. needs(commit.getTree());
  334. for (final RevCommit p : commit.getParents())
  335. needs(p);
  336. obj.add(COMPLETE);
  337. }
  338. private void processTag(final RevObject obj) {
  339. final RevTag tag = (RevTag) obj;
  340. needs(tag.getObject());
  341. obj.add(COMPLETE);
  342. }
  343. private void needs(final RevObject obj) {
  344. if (obj.has(COMPLETE))
  345. return;
  346. if (!obj.has(IN_WORK_QUEUE)) {
  347. obj.add(IN_WORK_QUEUE);
  348. workQueue.add(obj);
  349. }
  350. }
  351. private void downloadObject(final ProgressMonitor pm, final AnyObjectId id)
  352. throws TransportException {
  353. if (alreadyHave(id))
  354. return;
  355. for (;;) {
  356. // Try a pack file we know about, but don't have yet. Odds are
  357. // that if it has this object, it has others related to it so
  358. // getting the pack is a good bet.
  359. //
  360. if (downloadPackedObject(pm, id))
  361. return;
  362. // Search for a loose object over all alternates, starting
  363. // from the one we last successfully located an object through.
  364. //
  365. final String idStr = id.name();
  366. final String subdir = idStr.substring(0, 2);
  367. final String file = idStr.substring(2);
  368. final String looseName = subdir + "/" + file; //$NON-NLS-1$
  369. for (int i = lastRemoteIdx; i < remotes.size(); i++) {
  370. if (downloadLooseObject(id, looseName, remotes.get(i))) {
  371. lastRemoteIdx = i;
  372. return;
  373. }
  374. }
  375. for (int i = 0; i < lastRemoteIdx; i++) {
  376. if (downloadLooseObject(id, looseName, remotes.get(i))) {
  377. lastRemoteIdx = i;
  378. return;
  379. }
  380. }
  381. // Try to obtain more pack information and search those.
  382. //
  383. while (!noPacksYet.isEmpty()) {
  384. final WalkRemoteObjectDatabase wrr = noPacksYet.removeFirst();
  385. final Collection<String> packNameList;
  386. try {
  387. pm.beginTask(JGitText.get().listingPacks,
  388. ProgressMonitor.UNKNOWN);
  389. packNameList = wrr.getPackNames();
  390. } catch (IOException e) {
  391. // Try another repository.
  392. //
  393. recordError(id, e);
  394. continue;
  395. } finally {
  396. pm.endTask();
  397. }
  398. if (packNameList == null || packNameList.isEmpty())
  399. continue;
  400. for (final String packName : packNameList) {
  401. if (packsConsidered.add(packName))
  402. unfetchedPacks.add(new RemotePack(wrr, packName));
  403. }
  404. if (downloadPackedObject(pm, id))
  405. return;
  406. }
  407. // Try to expand the first alternate we haven't expanded yet.
  408. //
  409. Collection<WalkRemoteObjectDatabase> al = expandOneAlternate(id, pm);
  410. if (al != null && !al.isEmpty()) {
  411. for (final WalkRemoteObjectDatabase alt : al) {
  412. remotes.add(alt);
  413. noPacksYet.add(alt);
  414. noAlternatesYet.add(alt);
  415. }
  416. continue;
  417. }
  418. // We could not obtain the object. There may be reasons why.
  419. //
  420. List<Throwable> failures = fetchErrors.get(id);
  421. final TransportException te;
  422. te = new TransportException(MessageFormat.format(JGitText.get().cannotGet, id.name()));
  423. if (failures != null && !failures.isEmpty()) {
  424. if (failures.size() == 1)
  425. te.initCause(failures.get(0));
  426. else
  427. te.initCause(new CompoundException(failures));
  428. }
  429. throw te;
  430. }
  431. }
  432. private boolean alreadyHave(final AnyObjectId id) throws TransportException {
  433. try {
  434. return reader.has(id);
  435. } catch (IOException error) {
  436. throw new TransportException(MessageFormat.format(
  437. JGitText.get().cannotReadObject, id.name()), error);
  438. }
  439. }
  440. private boolean downloadPackedObject(final ProgressMonitor monitor,
  441. final AnyObjectId id) throws TransportException {
  442. // Search for the object in a remote pack whose index we have,
  443. // but whose pack we do not yet have.
  444. //
  445. final Iterator<RemotePack> packItr = unfetchedPacks.iterator();
  446. while (packItr.hasNext() && !monitor.isCancelled()) {
  447. final RemotePack pack = packItr.next();
  448. try {
  449. pack.openIndex(monitor);
  450. } catch (IOException err) {
  451. // If the index won't open its either not found or
  452. // its a format we don't recognize. In either case
  453. // we may still be able to obtain the object from
  454. // another source, so don't consider it a failure.
  455. //
  456. recordError(id, err);
  457. packItr.remove();
  458. continue;
  459. }
  460. if (monitor.isCancelled()) {
  461. // If we were cancelled while the index was opening
  462. // the open may have aborted. We can't search an
  463. // unopen index.
  464. //
  465. return false;
  466. }
  467. if (!pack.index.hasObject(id)) {
  468. // Not in this pack? Try another.
  469. //
  470. continue;
  471. }
  472. // It should be in the associated pack. Download that
  473. // and attach it to the local repository so we can use
  474. // all of the contained objects.
  475. //
  476. try {
  477. pack.downloadPack(monitor);
  478. } catch (IOException err) {
  479. // If the pack failed to download, index correctly,
  480. // or open in the local repository we may still be
  481. // able to obtain this object from another pack or
  482. // an alternate.
  483. //
  484. recordError(id, err);
  485. continue;
  486. } finally {
  487. // If the pack was good its in the local repository
  488. // and Repository.hasObject(id) will succeed in the
  489. // future, so we do not need this data anymore. If
  490. // it failed the index and pack are unusable and we
  491. // shouldn't consult them again.
  492. //
  493. try {
  494. if (pack.tmpIdx != null)
  495. FileUtils.delete(pack.tmpIdx);
  496. } catch (IOException e) {
  497. throw new TransportException(e.getMessage(), e);
  498. }
  499. packItr.remove();
  500. }
  501. if (!alreadyHave(id)) {
  502. // What the hell? This pack claimed to have
  503. // the object, but after indexing we didn't
  504. // actually find it in the pack.
  505. //
  506. recordError(id, new FileNotFoundException(MessageFormat.format(
  507. JGitText.get().objectNotFoundIn, id.name(), pack.packName)));
  508. continue;
  509. }
  510. // Complete any other objects that we can.
  511. //
  512. final Iterator<ObjectId> pending = swapFetchQueue();
  513. while (pending.hasNext()) {
  514. final ObjectId p = pending.next();
  515. if (pack.index.hasObject(p)) {
  516. pending.remove();
  517. process(p);
  518. } else {
  519. workQueue.add(p);
  520. }
  521. }
  522. return true;
  523. }
  524. return false;
  525. }
  526. private Iterator<ObjectId> swapFetchQueue() {
  527. final Iterator<ObjectId> r = workQueue.iterator();
  528. workQueue = new LinkedList<ObjectId>();
  529. return r;
  530. }
  531. private boolean downloadLooseObject(final AnyObjectId id,
  532. final String looseName, final WalkRemoteObjectDatabase remote)
  533. throws TransportException {
  534. try {
  535. final byte[] compressed = remote.open(looseName).toArray();
  536. verifyAndInsertLooseObject(id, compressed);
  537. return true;
  538. } catch (FileNotFoundException e) {
  539. // Not available in a loose format from this alternate?
  540. // Try another strategy to get the object.
  541. //
  542. recordError(id, e);
  543. return false;
  544. } catch (IOException e) {
  545. throw new TransportException(MessageFormat.format(JGitText.get().cannotDownload, id.name()), e);
  546. }
  547. }
  548. private void verifyAndInsertLooseObject(final AnyObjectId id,
  549. final byte[] compressed) throws IOException {
  550. final ObjectLoader uol;
  551. try {
  552. uol = UnpackedObject.parse(compressed, id);
  553. } catch (CorruptObjectException parsingError) {
  554. // Some HTTP servers send back a "200 OK" status with an HTML
  555. // page that explains the requested file could not be found.
  556. // These servers are most certainly misconfigured, but many
  557. // of them exist in the world, and many of those are hosting
  558. // Git repositories.
  559. //
  560. // Since an HTML page is unlikely to hash to one of our loose
  561. // objects we treat this condition as a FileNotFoundException
  562. // and attempt to recover by getting the object from another
  563. // source.
  564. //
  565. final FileNotFoundException e;
  566. e = new FileNotFoundException(id.name());
  567. e.initCause(parsingError);
  568. throw e;
  569. }
  570. final int type = uol.getType();
  571. final byte[] raw = uol.getCachedBytes();
  572. if (objCheck != null) {
  573. try {
  574. objCheck.check(id, type, raw);
  575. } catch (CorruptObjectException e) {
  576. throw new TransportException(MessageFormat.format(
  577. JGitText.get().transportExceptionInvalid,
  578. Constants.typeString(type), id.name(), e.getMessage()));
  579. }
  580. }
  581. ObjectId act = inserter.insert(type, raw);
  582. if (!AnyObjectId.equals(id, act)) {
  583. throw new TransportException(MessageFormat.format(
  584. JGitText.get().incorrectHashFor, id.name(), act.name(),
  585. Constants.typeString(type),
  586. Integer.valueOf(compressed.length)));
  587. }
  588. }
  589. private Collection<WalkRemoteObjectDatabase> expandOneAlternate(
  590. final AnyObjectId id, final ProgressMonitor pm) {
  591. while (!noAlternatesYet.isEmpty()) {
  592. final WalkRemoteObjectDatabase wrr = noAlternatesYet.removeFirst();
  593. try {
  594. pm.beginTask(JGitText.get().listingAlternates, ProgressMonitor.UNKNOWN);
  595. Collection<WalkRemoteObjectDatabase> altList = wrr
  596. .getAlternates();
  597. if (altList != null && !altList.isEmpty())
  598. return altList;
  599. } catch (IOException e) {
  600. // Try another repository.
  601. //
  602. recordError(id, e);
  603. } finally {
  604. pm.endTask();
  605. }
  606. }
  607. return null;
  608. }
  609. private void markLocalRefsComplete(final Set<ObjectId> have) throws TransportException {
  610. Map<String, Ref> refs;
  611. try {
  612. refs = local.getRefDatabase().getRefs(ALL);
  613. } catch (IOException e) {
  614. throw new TransportException(e.getMessage(), e);
  615. }
  616. for (final Ref r : refs.values()) {
  617. try {
  618. markLocalObjComplete(revWalk.parseAny(r.getObjectId()));
  619. } catch (IOException readError) {
  620. throw new TransportException(MessageFormat.format(JGitText.get().localRefIsMissingObjects, r.getName()), readError);
  621. }
  622. }
  623. for (final ObjectId id : have) {
  624. try {
  625. markLocalObjComplete(revWalk.parseAny(id));
  626. } catch (IOException readError) {
  627. throw new TransportException(MessageFormat.format(JGitText.get().transportExceptionMissingAssumed, id.name()), readError);
  628. }
  629. }
  630. }
  631. private void markLocalObjComplete(RevObject obj) throws IOException {
  632. while (obj.getType() == Constants.OBJ_TAG) {
  633. obj.add(COMPLETE);
  634. obj = ((RevTag) obj).getObject();
  635. revWalk.parseHeaders(obj);
  636. }
  637. switch (obj.getType()) {
  638. case Constants.OBJ_BLOB:
  639. obj.add(COMPLETE);
  640. break;
  641. case Constants.OBJ_COMMIT:
  642. pushLocalCommit((RevCommit) obj);
  643. break;
  644. case Constants.OBJ_TREE:
  645. markTreeComplete((RevTree) obj);
  646. break;
  647. }
  648. }
  649. private void markLocalCommitsComplete(final int until)
  650. throws TransportException {
  651. try {
  652. for (;;) {
  653. final RevCommit c = localCommitQueue.peek();
  654. if (c == null || c.getCommitTime() < until)
  655. return;
  656. localCommitQueue.next();
  657. markTreeComplete(c.getTree());
  658. for (final RevCommit p : c.getParents())
  659. pushLocalCommit(p);
  660. }
  661. } catch (IOException err) {
  662. throw new TransportException(JGitText.get().localObjectsIncomplete, err);
  663. }
  664. }
  665. private void pushLocalCommit(final RevCommit p)
  666. throws MissingObjectException, IOException {
  667. if (p.has(LOCALLY_SEEN))
  668. return;
  669. revWalk.parseHeaders(p);
  670. p.add(LOCALLY_SEEN);
  671. p.add(COMPLETE);
  672. p.carry(COMPLETE);
  673. localCommitQueue.add(p);
  674. }
  675. private void markTreeComplete(final RevTree tree) throws IOException {
  676. if (tree.has(COMPLETE))
  677. return;
  678. tree.add(COMPLETE);
  679. treeWalk.reset(tree);
  680. while (treeWalk.next()) {
  681. final FileMode mode = treeWalk.getFileMode(0);
  682. final int sType = mode.getObjectType();
  683. switch (sType) {
  684. case Constants.OBJ_BLOB:
  685. treeWalk.getObjectId(idBuffer, 0);
  686. revWalk.lookupAny(idBuffer, sType).add(COMPLETE);
  687. continue;
  688. case Constants.OBJ_TREE: {
  689. treeWalk.getObjectId(idBuffer, 0);
  690. final RevObject o = revWalk.lookupAny(idBuffer, sType);
  691. if (!o.has(COMPLETE)) {
  692. o.add(COMPLETE);
  693. treeWalk.enterSubtree();
  694. }
  695. continue;
  696. }
  697. default:
  698. if (FileMode.GITLINK.equals(mode))
  699. continue;
  700. treeWalk.getObjectId(idBuffer, 0);
  701. throw new CorruptObjectException(MessageFormat.format(JGitText.get().corruptObjectInvalidMode3
  702. , mode, idBuffer.name(), treeWalk.getPathString(), tree.name()));
  703. }
  704. }
  705. }
  706. private void recordError(final AnyObjectId id, final Throwable what) {
  707. final ObjectId objId = id.copy();
  708. List<Throwable> errors = fetchErrors.get(objId);
  709. if (errors == null) {
  710. errors = new ArrayList<Throwable>(2);
  711. fetchErrors.put(objId, errors);
  712. }
  713. errors.add(what);
  714. }
  715. private class RemotePack {
  716. final WalkRemoteObjectDatabase connection;
  717. final String packName;
  718. final String idxName;
  719. File tmpIdx;
  720. PackIndex index;
  721. RemotePack(final WalkRemoteObjectDatabase c, final String pn) {
  722. connection = c;
  723. packName = pn;
  724. idxName = packName.substring(0, packName.length() - 5) + ".idx"; //$NON-NLS-1$
  725. String tn = idxName;
  726. if (tn.startsWith("pack-")) //$NON-NLS-1$
  727. tn = tn.substring(5);
  728. if (tn.endsWith(".idx")) //$NON-NLS-1$
  729. tn = tn.substring(0, tn.length() - 4);
  730. if (local.getObjectDatabase() instanceof ObjectDirectory) {
  731. tmpIdx = new File(((ObjectDirectory) local.getObjectDatabase())
  732. .getDirectory(),
  733. "walk-" + tn + ".walkidx"); //$NON-NLS-1$ //$NON-NLS-2$
  734. }
  735. }
  736. void openIndex(final ProgressMonitor pm) throws IOException {
  737. if (index != null)
  738. return;
  739. if (tmpIdx == null)
  740. tmpIdx = File.createTempFile("jgit-walk-", ".idx"); //$NON-NLS-1$ //$NON-NLS-2$
  741. else if (tmpIdx.isFile()) {
  742. try {
  743. index = PackIndex.open(tmpIdx);
  744. return;
  745. } catch (FileNotFoundException err) {
  746. // Fall through and get the file.
  747. }
  748. }
  749. final WalkRemoteObjectDatabase.FileStream s;
  750. s = connection.open("pack/" + idxName); //$NON-NLS-1$
  751. pm.beginTask("Get " + idxName.substring(0, 12) + "..idx", //$NON-NLS-1$ //$NON-NLS-2$
  752. s.length < 0 ? ProgressMonitor.UNKNOWN
  753. : (int) (s.length / 1024));
  754. try {
  755. final FileOutputStream fos = new FileOutputStream(tmpIdx);
  756. try {
  757. final byte[] buf = new byte[2048];
  758. int cnt;
  759. while (!pm.isCancelled() && (cnt = s.in.read(buf)) >= 0) {
  760. fos.write(buf, 0, cnt);
  761. pm.update(cnt / 1024);
  762. }
  763. } finally {
  764. fos.close();
  765. }
  766. } catch (IOException err) {
  767. FileUtils.delete(tmpIdx);
  768. throw err;
  769. } finally {
  770. s.in.close();
  771. }
  772. pm.endTask();
  773. if (pm.isCancelled()) {
  774. FileUtils.delete(tmpIdx);
  775. return;
  776. }
  777. try {
  778. index = PackIndex.open(tmpIdx);
  779. } catch (IOException e) {
  780. FileUtils.delete(tmpIdx);
  781. throw e;
  782. }
  783. }
  784. void downloadPack(final ProgressMonitor monitor) throws IOException {
  785. String name = "pack/" + packName; //$NON-NLS-1$
  786. WalkRemoteObjectDatabase.FileStream s = connection.open(name);
  787. PackParser parser = inserter.newPackParser(s.in);
  788. parser.setAllowThin(false);
  789. parser.setObjectChecker(objCheck);
  790. parser.setLockMessage(lockMessage);
  791. PackLock lock = parser.parse(monitor);
  792. if (lock != null)
  793. packLocks.add(lock);
  794. }
  795. }
  796. }