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

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