2 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
7 * Redistribution and use in source and binary forms, with or
8 * without modification, are permitted provided that the following
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
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
;
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
;
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
;
81 * Generic fetch support for dumb transport protocols.
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
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.
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
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}.
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
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.
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
) {
172 objCheck
= wt
.isCheckFetchedObjects() ?
new ObjectChecker() : null;
174 remotes
= new ArrayList
<WalkRemoteObjectDatabase
>();
177 unfetchedPacks
= new LinkedList
<RemotePack
>();
178 packsConsidered
= new HashSet
<String
>();
180 noPacksYet
= new LinkedList
<WalkRemoteObjectDatabase
>();
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() {
203 protected void doFetch(final ProgressMonitor monitor
,
204 final Collection
<Ref
> want
, final Set
<ObjectId
> have
)
205 throws TransportException
{
206 markLocalRefsComplete(have
);
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
);
218 public void close() {
219 for (final RemotePack p
: unfetchedPacks
)
221 for (final WalkRemoteObjectDatabase r
: remotes
)
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();
231 final RevObject obj
= revWalk
.parseAny(id
);
232 if (obj
.has(COMPLETE
))
234 if (inWorkQueue
.add(id
)) {
235 obj
.add(IN_WORK_QUEUE
);
238 } catch (MissingObjectException e
) {
239 if (inWorkQueue
.add(id
))
241 } catch (IOException e
) {
242 throw new TransportException("Cannot read " + id
.name(), e
);
247 private void process(final ObjectId id
) throws TransportException
{
250 if (id
instanceof RevObject
) {
251 obj
= (RevObject
) id
;
252 if (obj
.has(COMPLETE
))
256 obj
= revWalk
.parseAny(id
);
257 if (obj
.has(COMPLETE
))
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.
270 switch (obj
.getType()) {
271 case Constants
.OBJ_BLOB
:
274 case Constants
.OBJ_TREE
:
277 case Constants
.OBJ_COMMIT
:
280 case Constants
.OBJ_TAG
:
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
));
300 private void processTree(final RevObject obj
) throws TransportException
{
303 while (treeWalk
.next()) {
304 final FileMode mode
= treeWalk
.getFileMode(0);
305 final int sType
= mode
.getObjectType();
308 case Constants
.OBJ_BLOB
:
309 case Constants
.OBJ_TREE
:
310 treeWalk
.getObjectId(idBuffer
, 0);
311 needs(revWalk
.lookupAny(idBuffer
, sType
));
315 if (FileMode
.GITLINK
.equals(mode
))
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
);
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())
339 private void processTag(final RevObject obj
) {
340 final RevTag tag
= (RevTag
) obj
;
341 needs(tag
.getObject());
345 private void needs(final RevObject obj
) {
346 if (obj
.has(COMPLETE
))
348 if (!obj
.has(IN_WORK_QUEUE
)) {
349 obj
.add(IN_WORK_QUEUE
);
354 private void downloadObject(final ProgressMonitor pm
, final AnyObjectId id
)
355 throws TransportException
{
356 if (local
.hasObject(id
))
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
))
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
))) {
381 for (int i
= 0; i
< lastRemoteIdx
; i
++) {
382 if (downloadLooseObject(id
, looseName
, remotes
.get(i
))) {
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
;
394 pm
.beginTask("Listing packs", ProgressMonitor
.UNKNOWN
);
395 packNameList
= wrr
.getPackNames();
396 } catch (IOException e
) {
397 // Try another repository.
405 if (packNameList
== null || packNameList
.isEmpty())
407 for (final String packName
: packNameList
) {
408 if (packsConsidered
.add(packName
))
409 unfetchedPacks
.add(new RemotePack(wrr
, packName
));
411 if (downloadPackedObject(pm
, id
))
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
) {
422 noAlternatesYet
.add(alt
);
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));
437 te
.initCause(new CompoundException(failures
));
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();
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
);
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
472 if (!pack
.index
.hasObject(id
)) {
473 // Not in this pack? Try another.
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.
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
490 recordError(id
, err
);
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();
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
+ "."));
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
)) {
531 private Iterator
<ObjectId
> swapFetchQueue() {
532 final Iterator
<ObjectId
> r
= workQueue
.iterator();
533 workQueue
= new LinkedList
<ObjectId
>();
537 private boolean downloadLooseObject(final AnyObjectId id
,
538 final String looseName
, final WalkRemoteObjectDatabase remote
)
539 throws TransportException
{
541 final byte[] compressed
= remote
.open(looseName
).toArray();
542 verifyLooseObject(id
, compressed
);
543 saveLooseObject(id
, compressed
);
545 } catch (FileNotFoundException e
) {
546 // Not available in a loose format from this alternate?
547 // Try another strategy to get the object.
551 } catch (IOException e
) {
552 throw new TransportException("Cannot download " + id
.name(), e
);
556 private void verifyLooseObject(final AnyObjectId id
, final byte[] compressed
)
558 final UnpackedObjectLoader uol
;
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
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
573 final FileNotFoundException e
;
574 e
= new FileNotFoundException(id
.name());
575 e
.initCause(parsingError
);
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) {
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
{
608 tmp
= File
.createTempFile("noz", null, local
.getObjectsDirectory());
610 final FileOutputStream out
= new FileOutputStream(tmp
);
612 out
.write(compressed
);
617 } catch (IOException e
) {
622 final File o
= local
.toFile(id
);
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();
635 if (local
.hasObject(id
))
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();
645 pm
.beginTask("Listing alternates", ProgressMonitor
.UNKNOWN
);
646 Collection
<WalkRemoteObjectDatabase
> altList
= wrr
648 if (altList
!= null && !altList
.isEmpty())
650 } catch (IOException e
) {
651 // Try another repository.
661 private void markLocalRefsComplete(final Set
<ObjectId
> have
) throws TransportException
{
662 for (final Ref r
: local
.getAllRefs().values()) {
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
) {
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
) {
683 obj
= ((RevTag
) obj
).getObject();
687 switch (obj
.getType()) {
688 case Constants
.OBJ_BLOB
:
691 case Constants
.OBJ_COMMIT
:
692 pushLocalCommit((RevCommit
) obj
);
694 case Constants
.OBJ_TREE
:
695 markTreeComplete((RevTree
) obj
);
700 private void markLocalCommitsComplete(final int until
)
701 throws TransportException
{
704 final RevCommit c
= localCommitQueue
.peek();
705 if (c
== null || c
.getCommitTime() < until
)
707 localCommitQueue
.next();
709 markTreeComplete(c
.getTree());
710 for (final RevCommit p
: c
.getParents())
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
))
727 localCommitQueue
.add(p
);
730 private void markTreeComplete(final RevTree tree
) throws IOException
{
731 if (tree
.has(COMPLETE
))
734 treeWalk
.reset(tree
);
735 while (treeWalk
.next()) {
736 final FileMode mode
= treeWalk
.getFileMode(0);
737 final int sType
= mode
.getObjectType();
740 case Constants
.OBJ_BLOB
:
741 treeWalk
.getObjectId(idBuffer
, 0);
742 revWalk
.lookupAny(idBuffer
, sType
).add(COMPLETE
);
745 case Constants
.OBJ_TREE
: {
746 treeWalk
.getObjectId(idBuffer
, 0);
747 final RevObject o
= revWalk
.lookupAny(idBuffer
, sType
);
748 if (!o
.has(COMPLETE
)) {
750 treeWalk
.enterSubtree();
755 if (FileMode
.GITLINK
.equals(mode
))
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
);
775 private class RemotePack
{
776 final WalkRemoteObjectDatabase connection
;
778 final String packName
;
780 final String idxName
;
786 RemotePack(final WalkRemoteObjectDatabase c
, final String pn
) {
787 final File objdir
= local
.getObjectsDirectory();
790 idxName
= packName
.substring(0, packName
.length() - 5) + ".idx";
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
{
803 if (tmpIdx
.isFile()) {
805 index
= PackIndex
.open(tmpIdx
);
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));
818 final FileOutputStream fos
= new FileOutputStream(tmpIdx
);
820 final byte[] buf
= new byte[2048];
822 while (!pm
.isCancelled() && (cnt
= s
.in
.read(buf
)) >= 0) {
823 fos
.write(buf
, 0, cnt
);
824 pm
.update(cnt
/ 1024);
829 } catch (IOException err
) {
837 if (pm
.isCancelled()) {
843 index
= PackIndex
.open(tmpIdx
);
844 } catch (IOException e
) {
850 void downloadPack(final ProgressMonitor monitor
) throws IOException
{
851 final WalkRemoteObjectDatabase
.FileStream s
;
854 s
= connection
.open("pack/" + packName
);
855 ip
= IndexPack
.create(local
, s
.in
);
856 ip
.setFixThin(false);
857 ip
.setObjectChecker(objCheck
);
859 ip
.renameAndOpenPack();