Remove getId from ObjectLoader API as its unnecessary overhead
[egit/qmx.git] / org.spearce.jgit / src / org / spearce / jgit / transport / WalkFetchConnection.java
blob93b5bd2d8b28e451dd3c66c7e5760b1244d4e8b7
1 /*
2 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or
8 * without modification, are permitted provided that the following
9 * conditions are met:
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
19 * - Neither the name of the Git Development Community nor the
20 * names of its contributors may be used to endorse or promote
21 * products derived from this software without specific prior
22 * written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
25 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
26 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 package org.spearce.jgit.transport;
41 import java.io.File;
42 import java.io.FileNotFoundException;
43 import java.io.FileOutputStream;
44 import java.io.IOException;
45 import java.security.MessageDigest;
46 import java.util.ArrayList;
47 import java.util.Collection;
48 import java.util.HashMap;
49 import java.util.HashSet;
50 import java.util.Iterator;
51 import java.util.LinkedList;
52 import java.util.List;
53 import java.util.Set;
55 import org.spearce.jgit.errors.CompoundException;
56 import org.spearce.jgit.errors.CorruptObjectException;
57 import org.spearce.jgit.errors.MissingObjectException;
58 import org.spearce.jgit.errors.ObjectWritingException;
59 import org.spearce.jgit.errors.TransportException;
60 import org.spearce.jgit.lib.AnyObjectId;
61 import org.spearce.jgit.lib.Constants;
62 import org.spearce.jgit.lib.FileMode;
63 import org.spearce.jgit.lib.MutableObjectId;
64 import org.spearce.jgit.lib.ObjectChecker;
65 import org.spearce.jgit.lib.ObjectId;
66 import org.spearce.jgit.lib.PackIndex;
67 import org.spearce.jgit.lib.ProgressMonitor;
68 import org.spearce.jgit.lib.Ref;
69 import org.spearce.jgit.lib.Repository;
70 import org.spearce.jgit.lib.UnpackedObjectLoader;
71 import org.spearce.jgit.revwalk.DateRevQueue;
72 import org.spearce.jgit.revwalk.RevCommit;
73 import org.spearce.jgit.revwalk.RevFlag;
74 import org.spearce.jgit.revwalk.RevObject;
75 import org.spearce.jgit.revwalk.RevTag;
76 import org.spearce.jgit.revwalk.RevTree;
77 import org.spearce.jgit.revwalk.RevWalk;
78 import org.spearce.jgit.treewalk.TreeWalk;
80 /**
81 * Generic fetch support for dumb transport protocols.
82 * <p>
83 * Since there are no Git-specific smarts on the remote side of the connection
84 * the client side must determine which objects it needs to copy in order to
85 * completely fetch the requested refs and their history. The generic walk
86 * support in this class parses each individual object (once it has been copied
87 * to the local repository) and examines the list of objects that must also be
88 * copied to create a complete history. Objects which are already available
89 * locally are retained (and not copied), saving bandwidth for incremental
90 * fetches. Pack files are copied from the remote repository only as a last
91 * resort, as the entire pack must be copied locally in order to access any
92 * single object.
93 * <p>
94 * This fetch connection does not actually perform the object data transfer.
95 * Instead it delegates the transfer to a {@link WalkRemoteObjectDatabase},
96 * which knows how to read individual files from the remote repository and
97 * supply the data as a standard Java InputStream.
99 * @see WalkRemoteObjectDatabase
101 class WalkFetchConnection extends BaseFetchConnection {
102 /** The repository this transport fetches into, or pushes out of. */
103 private final Repository local;
105 /** If not null the validator for received objects. */
106 private final ObjectChecker objCheck;
109 * List of all remote repositories we may need to get objects out of.
110 * <p>
111 * The first repository in the list is the one we were asked to fetch from;
112 * the remaining repositories point to the alternate locations we can fetch
113 * objects through.
115 private final List<WalkRemoteObjectDatabase> remotes;
117 /** Most recently used item in {@link #remotes}. */
118 private int lastRemoteIdx;
120 private final RevWalk revWalk;
122 private final TreeWalk treeWalk;
124 /** Objects whose direct dependents we know we have (or will have). */
125 private final RevFlag COMPLETE;
127 /** Objects that have already entered {@link #workQueue}. */
128 private final RevFlag IN_WORK_QUEUE;
130 /** Commits that have already entered {@link #localCommitQueue}. */
131 private final RevFlag LOCALLY_SEEN;
133 /** Commits already reachable from all local refs. */
134 private final DateRevQueue localCommitQueue;
136 /** Objects we need to copy from the remote repository. */
137 private LinkedList<ObjectId> workQueue;
139 /** Databases we have not yet obtained the list of packs from. */
140 private final LinkedList<WalkRemoteObjectDatabase> noPacksYet;
142 /** Databases we have not yet obtained the alternates from. */
143 private final LinkedList<WalkRemoteObjectDatabase> noAlternatesYet;
145 /** Packs we have discovered, but have not yet fetched locally. */
146 private final LinkedList<RemotePack> unfetchedPacks;
149 * Packs whose indexes we have looked at in {@link #unfetchedPacks}.
150 * <p>
151 * We try to avoid getting duplicate copies of the same pack through
152 * multiple alternates by only looking at packs whose names are not yet in
153 * this collection.
155 private final Set<String> packsConsidered;
157 private final MutableObjectId idBuffer = new MutableObjectId();
159 private final MessageDigest objectDigest = Constants.newMessageDigest();
162 * Errors received while trying to obtain an object.
163 * <p>
164 * If the fetch winds up failing because we cannot locate a specific object
165 * then we need to report all errors related to that object back to the
166 * caller as there may be cascading failures.
168 private final HashMap<ObjectId, List<Throwable>> fetchErrors;
170 WalkFetchConnection(final WalkTransport wt, final WalkRemoteObjectDatabase w) {
171 local = wt.local;
172 objCheck = wt.isCheckFetchedObjects() ? new ObjectChecker() : null;
174 remotes = new ArrayList<WalkRemoteObjectDatabase>();
175 remotes.add(w);
177 unfetchedPacks = new LinkedList<RemotePack>();
178 packsConsidered = new HashSet<String>();
180 noPacksYet = new LinkedList<WalkRemoteObjectDatabase>();
181 noPacksYet.add(w);
183 noAlternatesYet = new LinkedList<WalkRemoteObjectDatabase>();
184 noAlternatesYet.add(w);
186 fetchErrors = new HashMap<ObjectId, List<Throwable>>();
188 revWalk = new RevWalk(local);
189 treeWalk = new TreeWalk(local);
190 COMPLETE = revWalk.newFlag("COMPLETE");
191 IN_WORK_QUEUE = revWalk.newFlag("IN_WORK_QUEUE");
192 LOCALLY_SEEN = revWalk.newFlag("LOCALLY_SEEN");
194 localCommitQueue = new DateRevQueue();
195 workQueue = new LinkedList<ObjectId>();
198 public boolean didFetchTestConnectivity() {
199 return true;
202 @Override
203 protected void doFetch(final ProgressMonitor monitor,
204 final Collection<Ref> want, final Set<ObjectId> have)
205 throws TransportException {
206 markLocalRefsComplete(have);
207 queueWants(want);
209 while (!monitor.isCancelled() && !workQueue.isEmpty()) {
210 final ObjectId id = workQueue.removeFirst();
211 if (!(id instanceof RevObject) || !((RevObject) id).has(COMPLETE))
212 downloadObject(monitor, id);
213 process(id);
217 @Override
218 public void close() {
219 for (final RemotePack p : unfetchedPacks)
220 p.tmpIdx.delete();
221 for (final WalkRemoteObjectDatabase r : remotes)
222 r.close();
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);
238 } catch (MissingObjectException e) {
239 if (inWorkQueue.add(id))
240 workQueue.add(id);
241 } catch (IOException e) {
242 throw new TransportException("Cannot read " + id.name(), e);
247 private void process(final ObjectId id) throws TransportException {
248 final RevObject obj;
249 try {
250 if (id instanceof RevObject) {
251 obj = (RevObject) id;
252 if (obj.has(COMPLETE))
253 return;
254 revWalk.parse(obj);
255 } else {
256 obj = revWalk.parseAny(id);
257 if (obj.has(COMPLETE))
258 return;
260 } catch (IOException e) {
261 throw new TransportException("Cannot read " + id.name(), e);
264 // We only care about traversal; disposing an object throws its
265 // message buffer (if any) away but retains the links so we can
266 // continue to navigate, but use less memory long-term.
268 obj.dispose();
270 switch (obj.getType()) {
271 case Constants.OBJ_BLOB:
272 processBlob(obj);
273 break;
274 case Constants.OBJ_TREE:
275 processTree(obj);
276 break;
277 case Constants.OBJ_COMMIT:
278 processCommit(obj);
279 break;
280 case Constants.OBJ_TAG:
281 processTag(obj);
282 break;
283 default:
284 throw new TransportException("Unknown object type " + id.name());
287 // If we had any prior errors fetching this object they are
288 // now resolved, as the object was parsed successfully.
290 fetchErrors.remove(id.copy());
293 private void processBlob(final RevObject obj) throws TransportException {
294 if (!local.hasObject(obj))
295 throw new TransportException("Cannot read blob " + obj.name(),
296 new MissingObjectException(obj, Constants.TYPE_BLOB));
297 obj.add(COMPLETE);
300 private void processTree(final RevObject obj) throws TransportException {
301 try {
302 treeWalk.reset(obj);
303 while (treeWalk.next()) {
304 final FileMode mode = treeWalk.getFileMode(0);
305 final int sType = mode.getObjectType();
307 switch (sType) {
308 case Constants.OBJ_BLOB:
309 case Constants.OBJ_TREE:
310 treeWalk.getObjectId(idBuffer, 0);
311 needs(revWalk.lookupAny(idBuffer, sType));
312 continue;
314 default:
315 if (FileMode.GITLINK.equals(mode))
316 continue;
317 treeWalk.getObjectId(idBuffer, 0);
318 throw new CorruptObjectException("Invalid mode " + mode
319 + " for " + idBuffer.name() + " "
320 + treeWalk.getPathString() + " in "
321 + obj.getId().name() + ".");
324 } catch (IOException ioe) {
325 throw new TransportException("Cannot read tree " + obj.name(), ioe);
327 obj.add(COMPLETE);
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);
339 private void processTag(final RevObject obj) {
340 final RevTag tag = (RevTag) obj;
341 needs(tag.getObject());
342 obj.add(COMPLETE);
345 private void needs(final RevObject obj) {
346 if (obj.has(COMPLETE))
347 return;
348 if (!obj.has(IN_WORK_QUEUE)) {
349 obj.add(IN_WORK_QUEUE);
350 workQueue.add(obj);
354 private void downloadObject(final ProgressMonitor pm, final AnyObjectId id)
355 throws TransportException {
356 if (local.hasObject(id))
357 return;
359 for (;;) {
360 // Try a pack file we know about, but don't have yet. Odds are
361 // that if it has this object, it has others related to it so
362 // getting the pack is a good bet.
364 if (downloadPackedObject(pm, id))
365 return;
367 // Search for a loose object over all alternates, starting
368 // from the one we last successfully located an object through.
370 final String idStr = id.name();
371 final String subdir = idStr.substring(0, 2);
372 final String file = idStr.substring(2);
373 final String looseName = subdir + "/" + file;
375 for (int i = lastRemoteIdx; i < remotes.size(); i++) {
376 if (downloadLooseObject(id, looseName, remotes.get(i))) {
377 lastRemoteIdx = i;
378 return;
381 for (int i = 0; i < lastRemoteIdx; i++) {
382 if (downloadLooseObject(id, looseName, remotes.get(i))) {
383 lastRemoteIdx = i;
384 return;
388 // Try to obtain more pack information and search those.
390 while (!noPacksYet.isEmpty()) {
391 final WalkRemoteObjectDatabase wrr = noPacksYet.removeFirst();
392 final Collection<String> packNameList;
393 try {
394 pm.beginTask("Listing packs", ProgressMonitor.UNKNOWN);
395 packNameList = wrr.getPackNames();
396 } catch (IOException e) {
397 // Try another repository.
399 recordError(id, e);
400 continue;
401 } finally {
402 pm.endTask();
405 if (packNameList == null || packNameList.isEmpty())
406 continue;
407 for (final String packName : packNameList) {
408 if (packsConsidered.add(packName))
409 unfetchedPacks.add(new RemotePack(wrr, packName));
411 if (downloadPackedObject(pm, id))
412 return;
415 // Try to expand the first alternate we haven't expanded yet.
417 Collection<WalkRemoteObjectDatabase> al = expandOneAlternate(id, pm);
418 if (al != null && !al.isEmpty()) {
419 for (final WalkRemoteObjectDatabase alt : al) {
420 remotes.add(alt);
421 noPacksYet.add(alt);
422 noAlternatesYet.add(alt);
424 continue;
427 // We could not obtain the object. There may be reasons why.
429 List<Throwable> failures = fetchErrors.get(id.copy());
430 final TransportException te;
432 te = new TransportException("Cannot get " + id.name() + ".");
433 if (failures != null && !failures.isEmpty()) {
434 if (failures.size() == 1)
435 te.initCause(failures.get(0));
436 else
437 te.initCause(new CompoundException(failures));
439 throw te;
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.
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.
459 recordError(id, err);
460 packItr.remove();
461 continue;
464 if (monitor.isCancelled()) {
465 // If we were cancelled while the index was opening
466 // the open may have aborted. We can't search an
467 // unopen index.
469 return false;
472 if (!pack.index.hasObject(id)) {
473 // Not in this pack? Try another.
475 continue;
478 // It should be in the associated pack. Download that
479 // and attach it to the local repository so we can use
480 // all of the contained objects.
482 try {
483 pack.downloadPack(monitor);
484 } catch (IOException err) {
485 // If the pack failed to download, index correctly,
486 // or open in the local repository we may still be
487 // able to obtain this object from another pack or
488 // an alternate.
490 recordError(id, err);
491 continue;
492 } finally {
493 // If the pack was good its in the local repository
494 // and Repository.hasObject(id) will succeed in the
495 // future, so we do not need this data anymore. If
496 // it failed the index and pack are unusable and we
497 // shouldn't consult them again.
499 pack.tmpIdx.delete();
500 packItr.remove();
503 if (!local.hasObject(id)) {
504 // What the hell? This pack claimed to have
505 // the object, but after indexing we didn't
506 // actually find it in the pack.
508 recordError(id, new FileNotFoundException("Object " + id.name()
509 + " not found in " + pack.packName + "."));
510 continue;
513 // Complete any other objects that we can.
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);
525 return true;
528 return false;
531 private Iterator<ObjectId> swapFetchQueue() {
532 final Iterator<ObjectId> r = workQueue.iterator();
533 workQueue = new LinkedList<ObjectId>();
534 return r;
537 private boolean downloadLooseObject(final AnyObjectId id,
538 final String looseName, final WalkRemoteObjectDatabase remote)
539 throws TransportException {
540 try {
541 final byte[] compressed = remote.open(looseName).toArray();
542 verifyLooseObject(id, compressed);
543 saveLooseObject(id, compressed);
544 return true;
545 } catch (FileNotFoundException e) {
546 // Not available in a loose format from this alternate?
547 // Try another strategy to get the object.
549 recordError(id, e);
550 return false;
551 } catch (IOException e) {
552 throw new TransportException("Cannot download " + id.name(), e);
556 private void verifyLooseObject(final AnyObjectId id, final byte[] compressed)
557 throws IOException {
558 final UnpackedObjectLoader uol;
559 try {
560 uol = new UnpackedObjectLoader(compressed);
561 } catch (CorruptObjectException parsingError) {
562 // Some HTTP servers send back a "200 OK" status with an HTML
563 // page that explains the requested file could not be found.
564 // These servers are most certainly misconfigured, but many
565 // of them exist in the world, and many of those are hosting
566 // Git repositories.
568 // Since an HTML page is unlikely to hash to one of our loose
569 // objects we treat this condition as a FileNotFoundException
570 // and attempt to recover by getting the object from another
571 // source.
573 final FileNotFoundException e;
574 e = new FileNotFoundException(id.name());
575 e.initCause(parsingError);
576 throw e;
579 objectDigest.reset();
580 objectDigest.update(Constants.encodedTypeString(uol.getType()));
581 objectDigest.update((byte) ' ');
582 objectDigest.update(Constants.encodeASCII(uol.getSize()));
583 objectDigest.update((byte) 0);
584 objectDigest.update(uol.getCachedBytes());
585 idBuffer.fromRaw(objectDigest.digest(), 0);
587 if (!AnyObjectId.equals(id, idBuffer)) {
588 throw new TransportException("Incorrect hash for " + id.name()
589 + "; computed " + idBuffer.name() + " as a "
590 + Constants.encodedTypeString(uol.getType()) + " from "
591 + compressed.length + " bytes.");
593 if (objCheck != null) {
594 try {
595 objCheck.check(uol.getType(), uol.getCachedBytes());
596 } catch (CorruptObjectException e) {
597 throw new TransportException("Invalid "
598 + Constants.encodedTypeString(uol.getType()) + " "
599 + id.name() + ":" + e.getMessage());
604 private void saveLooseObject(final AnyObjectId id, final byte[] compressed)
605 throws IOException, ObjectWritingException {
606 final File tmp;
608 tmp = File.createTempFile("noz", null, local.getObjectsDirectory());
609 try {
610 final FileOutputStream out = new FileOutputStream(tmp);
611 try {
612 out.write(compressed);
613 } finally {
614 out.close();
616 tmp.setReadOnly();
617 } catch (IOException e) {
618 tmp.delete();
619 throw e;
622 final File o = local.toFile(id);
623 if (tmp.renameTo(o))
624 return;
626 // Maybe the directory doesn't exist yet as the object
627 // directories are always lazily created. Note that we
628 // try the rename first as the directory likely does exist.
630 o.getParentFile().mkdir();
631 if (tmp.renameTo(o))
632 return;
634 tmp.delete();
635 if (local.hasObject(id))
636 return;
637 throw new ObjectWritingException("Unable to store " + id.name() + ".");
640 private Collection<WalkRemoteObjectDatabase> expandOneAlternate(
641 final AnyObjectId id, final ProgressMonitor pm) {
642 while (!noAlternatesYet.isEmpty()) {
643 final WalkRemoteObjectDatabase wrr = noAlternatesYet.removeFirst();
644 try {
645 pm.beginTask("Listing alternates", ProgressMonitor.UNKNOWN);
646 Collection<WalkRemoteObjectDatabase> altList = wrr
647 .getAlternates();
648 if (altList != null && !altList.isEmpty())
649 return altList;
650 } catch (IOException e) {
651 // Try another repository.
653 recordError(id, e);
654 } finally {
655 pm.endTask();
658 return null;
661 private void markLocalRefsComplete(final Set<ObjectId> have) throws TransportException {
662 for (final Ref r : local.getAllRefs().values()) {
663 try {
664 markLocalObjComplete(revWalk.parseAny(r.getObjectId()));
665 } catch (IOException readError) {
666 throw new TransportException("Local ref " + r.getName()
667 + " is missing object(s).", readError);
670 for (final ObjectId id : have) {
671 try {
672 markLocalObjComplete(revWalk.parseAny(id));
673 } catch (IOException readError) {
674 throw new TransportException("Missing assumed "+id.name(), readError);
679 private void markLocalObjComplete(RevObject obj) throws IOException {
680 while (obj.getType() == Constants.OBJ_TAG) {
681 obj.add(COMPLETE);
682 obj.dispose();
683 obj = ((RevTag) obj).getObject();
684 revWalk.parse(obj);
687 switch (obj.getType()) {
688 case Constants.OBJ_BLOB:
689 obj.add(COMPLETE);
690 break;
691 case Constants.OBJ_COMMIT:
692 pushLocalCommit((RevCommit) obj);
693 break;
694 case Constants.OBJ_TREE:
695 markTreeComplete((RevTree) obj);
696 break;
700 private void markLocalCommitsComplete(final int until)
701 throws TransportException {
702 try {
703 for (;;) {
704 final RevCommit c = localCommitQueue.peek();
705 if (c == null || c.getCommitTime() < until)
706 return;
707 localCommitQueue.next();
709 markTreeComplete(c.getTree());
710 for (final RevCommit p : c.getParents())
711 pushLocalCommit(p);
713 } catch (IOException err) {
714 throw new TransportException("Local objects incomplete.", err);
718 private void pushLocalCommit(final RevCommit p)
719 throws MissingObjectException, IOException {
720 if (p.has(LOCALLY_SEEN))
721 return;
722 revWalk.parse(p);
723 p.add(LOCALLY_SEEN);
724 p.add(COMPLETE);
725 p.carry(COMPLETE);
726 p.dispose();
727 localCommitQueue.add(p);
730 private void markTreeComplete(final RevTree tree) throws IOException {
731 if (tree.has(COMPLETE))
732 return;
733 tree.add(COMPLETE);
734 treeWalk.reset(tree);
735 while (treeWalk.next()) {
736 final FileMode mode = treeWalk.getFileMode(0);
737 final int sType = mode.getObjectType();
739 switch (sType) {
740 case Constants.OBJ_BLOB:
741 treeWalk.getObjectId(idBuffer, 0);
742 revWalk.lookupAny(idBuffer, sType).add(COMPLETE);
743 continue;
745 case Constants.OBJ_TREE: {
746 treeWalk.getObjectId(idBuffer, 0);
747 final RevObject o = revWalk.lookupAny(idBuffer, sType);
748 if (!o.has(COMPLETE)) {
749 o.add(COMPLETE);
750 treeWalk.enterSubtree();
752 continue;
754 default:
755 if (FileMode.GITLINK.equals(mode))
756 continue;
757 treeWalk.getObjectId(idBuffer, 0);
758 throw new CorruptObjectException("Invalid mode " + mode
759 + " for " + idBuffer.name() + " "
760 + treeWalk.getPathString() + " in " + tree.name() + ".");
765 private void recordError(final AnyObjectId id, final Throwable what) {
766 final ObjectId objId = id.copy();
767 List<Throwable> errors = fetchErrors.get(objId);
768 if (errors == null) {
769 errors = new ArrayList<Throwable>(2);
770 fetchErrors.put(objId, errors);
772 errors.add(what);
775 private class RemotePack {
776 final WalkRemoteObjectDatabase connection;
778 final String packName;
780 final String idxName;
782 final File tmpIdx;
784 PackIndex index;
786 RemotePack(final WalkRemoteObjectDatabase c, final String pn) {
787 final File objdir = local.getObjectsDirectory();
788 connection = c;
789 packName = pn;
790 idxName = packName.substring(0, packName.length() - 5) + ".idx";
792 String tn = idxName;
793 if (tn.startsWith("pack-"))
794 tn = tn.substring(5);
795 if (tn.endsWith(".idx"))
796 tn = tn.substring(0, tn.length() - 4);
797 tmpIdx = new File(objdir, "walk-" + tn + ".walkidx");
800 void openIndex(final ProgressMonitor pm) throws IOException {
801 if (index != null)
802 return;
803 if (tmpIdx.isFile()) {
804 try {
805 index = PackIndex.open(tmpIdx);
806 return;
807 } catch (FileNotFoundException err) {
808 // Fall through and get the file.
812 final WalkRemoteObjectDatabase.FileStream s;
813 s = connection.open("pack/" + idxName);
814 pm.beginTask("Get " + idxName.substring(0, 12) + "..idx",
815 s.length < 0 ? ProgressMonitor.UNKNOWN
816 : (int) (s.length / 1024));
817 try {
818 final FileOutputStream fos = new FileOutputStream(tmpIdx);
819 try {
820 final byte[] buf = new byte[2048];
821 int cnt;
822 while (!pm.isCancelled() && (cnt = s.in.read(buf)) >= 0) {
823 fos.write(buf, 0, cnt);
824 pm.update(cnt / 1024);
826 } finally {
827 fos.close();
829 } catch (IOException err) {
830 tmpIdx.delete();
831 throw err;
832 } finally {
833 s.in.close();
835 pm.endTask();
837 if (pm.isCancelled()) {
838 tmpIdx.delete();
839 return;
842 try {
843 index = PackIndex.open(tmpIdx);
844 } catch (IOException e) {
845 tmpIdx.delete();
846 throw e;
850 void downloadPack(final ProgressMonitor monitor) throws IOException {
851 final WalkRemoteObjectDatabase.FileStream s;
852 final IndexPack ip;
854 s = connection.open("pack/" + packName);
855 ip = IndexPack.create(local, s.in);
856 ip.setFixThin(false);
857 ip.setObjectChecker(objCheck);
858 ip.index(monitor);
859 ip.renameAndOpenPack();