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
.util
.ArrayList
;
46 import java
.util
.Collection
;
47 import java
.util
.HashMap
;
48 import java
.util
.HashSet
;
49 import java
.util
.Iterator
;
50 import java
.util
.LinkedList
;
51 import java
.util
.List
;
54 import org
.spearce
.jgit
.errors
.CompoundException
;
55 import org
.spearce
.jgit
.errors
.CorruptObjectException
;
56 import org
.spearce
.jgit
.errors
.MissingObjectException
;
57 import org
.spearce
.jgit
.errors
.ObjectWritingException
;
58 import org
.spearce
.jgit
.errors
.TransportException
;
59 import org
.spearce
.jgit
.lib
.AnyObjectId
;
60 import org
.spearce
.jgit
.lib
.Constants
;
61 import org
.spearce
.jgit
.lib
.FileMode
;
62 import org
.spearce
.jgit
.lib
.ObjectChecker
;
63 import org
.spearce
.jgit
.lib
.ObjectId
;
64 import org
.spearce
.jgit
.lib
.PackIndex
;
65 import org
.spearce
.jgit
.lib
.ProgressMonitor
;
66 import org
.spearce
.jgit
.lib
.Ref
;
67 import org
.spearce
.jgit
.lib
.Repository
;
68 import org
.spearce
.jgit
.lib
.UnpackedObjectLoader
;
69 import org
.spearce
.jgit
.revwalk
.DateRevQueue
;
70 import org
.spearce
.jgit
.revwalk
.RevCommit
;
71 import org
.spearce
.jgit
.revwalk
.RevFlag
;
72 import org
.spearce
.jgit
.revwalk
.RevObject
;
73 import org
.spearce
.jgit
.revwalk
.RevTag
;
74 import org
.spearce
.jgit
.revwalk
.RevTree
;
75 import org
.spearce
.jgit
.revwalk
.RevWalk
;
76 import org
.spearce
.jgit
.treewalk
.TreeWalk
;
79 * Generic fetch support for dumb transport protocols.
81 * Since there are no Git-specific smarts on the remote side of the connection
82 * the client side must determine which objects it needs to copy in order to
83 * completely fetch the requested refs and their history. The generic walk
84 * support in this class parses each individual object (once it has been copied
85 * to the local repository) and examines the list of objects that must also be
86 * copied to create a complete history. Objects which are already available
87 * locally are retained (and not copied), saving bandwidth for incremental
88 * fetches. Pack files are copied from the remote repository only as a last
89 * resort, as the entire pack must be copied locally in order to access any
92 * This fetch connection does not actually perform the object data transfer.
93 * Instead it delegates the transfer to a {@link WalkRemoteObjectDatabase},
94 * which knows how to read individual files from the remote repository and
95 * supply the data as a standard Java InputStream.
97 * @see WalkRemoteObjectDatabase
99 class WalkFetchConnection
extends BaseFetchConnection
{
100 /** The repository this transport fetches into, or pushes out of. */
101 private final Repository local
;
103 /** If not null the validator for received objects. */
104 private final ObjectChecker objCheck
;
107 * List of all remote repositories we may need to get objects out of.
109 * The first repository in the list is the one we were asked to fetch from;
110 * the remaining repositories point to the alternate locations we can fetch
113 private final List
<WalkRemoteObjectDatabase
> remotes
;
115 /** Most recently used item in {@link #remotes}. */
116 private int lastRemoteIdx
;
118 private final RevWalk revWalk
;
120 private final TreeWalk treeWalk
;
122 /** Objects whose direct dependents we know we have (or will have). */
123 private final RevFlag COMPLETE
;
125 /** Objects that have already entered {@link #workQueue}. */
126 private final RevFlag IN_WORK_QUEUE
;
128 /** Commits that have already entered {@link #localCommitQueue}. */
129 private final RevFlag LOCALLY_SEEN
;
131 /** Commits already reachable from all local refs. */
132 private final DateRevQueue localCommitQueue
;
134 /** Objects we need to copy from the remote repository. */
135 private LinkedList
<ObjectId
> workQueue
;
137 /** Databases we have not yet obtained the list of packs from. */
138 private final LinkedList
<WalkRemoteObjectDatabase
> noPacksYet
;
140 /** Databases we have not yet obtained the alternates from. */
141 private final LinkedList
<WalkRemoteObjectDatabase
> noAlternatesYet
;
143 /** Packs we have discovered, but have not yet fetched locally. */
144 private final LinkedList
<RemotePack
> unfetchedPacks
;
147 * Packs whose indexes we have looked at in {@link #unfetchedPacks}.
149 * We try to avoid getting duplicate copies of the same pack through
150 * multiple alternates by only looking at packs whose names are not yet in
153 private final Set
<String
> packsConsidered
;
156 * Errors received while trying to obtain an object.
158 * If the fetch winds up failing because we cannot locate a specific object
159 * then we need to report all errors related to that object back to the
160 * caller as there may be cascading failures.
162 private final HashMap
<ObjectId
, List
<Throwable
>> fetchErrors
;
164 WalkFetchConnection(final WalkTransport wt
, final WalkRemoteObjectDatabase w
) {
166 objCheck
= wt
.isCheckFetchedObjects() ?
new ObjectChecker() : null;
168 remotes
= new ArrayList
<WalkRemoteObjectDatabase
>();
171 unfetchedPacks
= new LinkedList
<RemotePack
>();
172 packsConsidered
= new HashSet
<String
>();
174 noPacksYet
= new LinkedList
<WalkRemoteObjectDatabase
>();
177 noAlternatesYet
= new LinkedList
<WalkRemoteObjectDatabase
>();
178 noAlternatesYet
.add(w
);
180 fetchErrors
= new HashMap
<ObjectId
, List
<Throwable
>>();
182 revWalk
= new RevWalk(local
);
183 treeWalk
= new TreeWalk(local
);
184 COMPLETE
= revWalk
.newFlag("COMPLETE");
185 IN_WORK_QUEUE
= revWalk
.newFlag("IN_WORK_QUEUE");
186 LOCALLY_SEEN
= revWalk
.newFlag("LOCALLY_SEEN");
188 localCommitQueue
= new DateRevQueue();
189 workQueue
= new LinkedList
<ObjectId
>();
192 public boolean didFetchTestConnectivity() {
197 protected void doFetch(final ProgressMonitor monitor
,
198 final Collection
<Ref
> want
, final Set
<ObjectId
> have
)
199 throws TransportException
{
200 markLocalRefsComplete(have
);
203 while (!monitor
.isCancelled() && !workQueue
.isEmpty()) {
204 final ObjectId id
= workQueue
.removeFirst();
205 if (!(id
instanceof RevObject
) || !((RevObject
) id
).has(COMPLETE
))
206 downloadObject(monitor
, id
);
212 public void close() {
213 for (final RemotePack p
: unfetchedPacks
)
215 for (final WalkRemoteObjectDatabase r
: remotes
)
219 private void queueWants(final Collection
<Ref
> want
)
220 throws TransportException
{
221 final HashSet
<ObjectId
> inWorkQueue
= new HashSet
<ObjectId
>();
222 for (final Ref r
: want
) {
223 final ObjectId id
= r
.getObjectId();
225 final RevObject obj
= revWalk
.parseAny(id
);
226 if (obj
.has(COMPLETE
))
228 if (inWorkQueue
.add(id
)) {
229 obj
.add(IN_WORK_QUEUE
);
232 } catch (MissingObjectException e
) {
233 if (inWorkQueue
.add(id
))
235 } catch (IOException e
) {
236 throw new TransportException("Cannot read " + id
.name(), e
);
241 private void process(final ObjectId id
) throws TransportException
{
244 if (id
instanceof RevObject
) {
245 obj
= (RevObject
) id
;
246 if (obj
.has(COMPLETE
))
250 obj
= revWalk
.parseAny(id
);
251 if (obj
.has(COMPLETE
))
254 } catch (IOException e
) {
255 throw new TransportException("Cannot read " + id
.name(), e
);
258 // We only care about traversal; disposing an object throws its
259 // message buffer (if any) away but retains the links so we can
260 // continue to navigate, but use less memory long-term.
264 switch (obj
.getType()) {
265 case Constants
.OBJ_BLOB
:
268 case Constants
.OBJ_TREE
:
271 case Constants
.OBJ_COMMIT
:
274 case Constants
.OBJ_TAG
:
278 throw new TransportException("Unknown object type " + id
.name());
281 // If we had any prior errors fetching this object they are
282 // now resolved, as the object was parsed successfully.
284 fetchErrors
.remove(id
.copy());
287 private void processBlob(final RevObject obj
) throws TransportException
{
288 if (!local
.hasObject(obj
))
289 throw new TransportException("Cannot read blob " + obj
.name(),
290 new MissingObjectException(obj
, Constants
.TYPE_BLOB
));
294 private void processTree(final RevObject obj
) throws TransportException
{
296 treeWalk
.reset(new ObjectId
[] { obj
});
297 while (treeWalk
.next()) {
298 final FileMode mode
= treeWalk
.getFileMode(0);
299 final int sType
= mode
.getObjectType();
302 case Constants
.OBJ_BLOB
:
303 case Constants
.OBJ_TREE
: {
304 final ObjectId sId
= treeWalk
.getObjectId(0);
305 needs(revWalk
.lookupAny(sId
, sType
));
309 if (FileMode
.GITLINK
.equals(mode
))
311 throw new CorruptObjectException("Invalid mode " + mode
312 + " for " + treeWalk
.getObjectId(0).name() + " "
313 + treeWalk
.getPathString() + " in "
314 + obj
.getId().name() + ".");
317 } catch (IOException ioe
) {
318 throw new TransportException("Cannot read tree " + obj
.name(), ioe
);
323 private void processCommit(final RevObject obj
) throws TransportException
{
324 final RevCommit commit
= (RevCommit
) obj
;
325 markLocalCommitsComplete(commit
.getCommitTime());
326 needs(commit
.getTree());
327 for (final RevCommit p
: commit
.getParents())
332 private void processTag(final RevObject obj
) {
333 final RevTag tag
= (RevTag
) obj
;
334 needs(tag
.getObject());
338 private void needs(final RevObject obj
) {
339 if (obj
.has(COMPLETE
))
341 if (!obj
.has(IN_WORK_QUEUE
)) {
342 obj
.add(IN_WORK_QUEUE
);
347 private void downloadObject(final ProgressMonitor pm
, final AnyObjectId id
)
348 throws TransportException
{
349 if (local
.hasObject(id
))
353 // Try a pack file we know about, but don't have yet. Odds are
354 // that if it has this object, it has others related to it so
355 // getting the pack is a good bet.
357 if (downloadPackedObject(pm
, id
))
360 // Search for a loose object over all alternates, starting
361 // from the one we last successfully located an object through.
363 final String idStr
= id
.name();
364 final String subdir
= idStr
.substring(0, 2);
365 final String file
= idStr
.substring(2);
366 final String looseName
= subdir
+ "/" + file
;
368 for (int i
= lastRemoteIdx
; i
< remotes
.size(); i
++) {
369 if (downloadLooseObject(id
, looseName
, remotes
.get(i
))) {
374 for (int i
= 0; i
< lastRemoteIdx
; i
++) {
375 if (downloadLooseObject(id
, looseName
, remotes
.get(i
))) {
381 // Try to obtain more pack information and search those.
383 while (!noPacksYet
.isEmpty()) {
384 final WalkRemoteObjectDatabase wrr
= noPacksYet
.removeFirst();
385 final Collection
<String
> packNameList
;
387 pm
.beginTask("Listing packs", ProgressMonitor
.UNKNOWN
);
388 packNameList
= wrr
.getPackNames();
389 } catch (IOException e
) {
390 // Try another repository.
398 if (packNameList
== null || packNameList
.isEmpty())
400 for (final String packName
: packNameList
) {
401 if (packsConsidered
.add(packName
))
402 unfetchedPacks
.add(new RemotePack(wrr
, packName
));
404 if (downloadPackedObject(pm
, id
))
408 // Try to expand the first alternate we haven't expanded yet.
410 Collection
<WalkRemoteObjectDatabase
> al
= expandOneAlternate(id
, pm
);
411 if (al
!= null && !al
.isEmpty()) {
412 for (final WalkRemoteObjectDatabase alt
: al
) {
415 noAlternatesYet
.add(alt
);
420 // We could not obtain the object. There may be reasons why.
422 List
<Throwable
> failures
= fetchErrors
.get(id
.copy());
423 final TransportException te
;
425 te
= new TransportException("Cannot get " + id
.name() + ".");
426 if (failures
!= null && !failures
.isEmpty()) {
427 if (failures
.size() == 1)
428 te
.initCause(failures
.get(0));
430 te
.initCause(new CompoundException(failures
));
436 private boolean downloadPackedObject(final ProgressMonitor monitor
,
437 final AnyObjectId id
) throws TransportException
{
438 // Search for the object in a remote pack whose index we have,
439 // but whose pack we do not yet have.
441 final Iterator
<RemotePack
> packItr
= unfetchedPacks
.iterator();
442 while (packItr
.hasNext() && !monitor
.isCancelled()) {
443 final RemotePack pack
= packItr
.next();
445 pack
.openIndex(monitor
);
446 } catch (IOException err
) {
447 // If the index won't open its either not found or
448 // its a format we don't recognize. In either case
449 // we may still be able to obtain the object from
450 // another source, so don't consider it a failure.
452 recordError(id
, err
);
457 if (monitor
.isCancelled()) {
458 // If we were cancelled while the index was opening
459 // the open may have aborted. We can't search an
465 if (!pack
.index
.hasObject(id
)) {
466 // Not in this pack? Try another.
471 // It should be in the associated pack. Download that
472 // and attach it to the local repository so we can use
473 // all of the contained objects.
476 pack
.downloadPack(monitor
);
477 } catch (IOException err
) {
478 // If the pack failed to download, index correctly,
479 // or open in the local repository we may still be
480 // able to obtain this object from another pack or
483 recordError(id
, err
);
486 // If the pack was good its in the local repository
487 // and Repository.hasObject(id) will succeed in the
488 // future, so we do not need this data anymore. If
489 // it failed the index and pack are unusable and we
490 // shouldn't consult them again.
492 pack
.tmpIdx
.delete();
496 if (!local
.hasObject(id
)) {
497 // What the hell? This pack claimed to have
498 // the object, but after indexing we didn't
499 // actually find it in the pack.
501 recordError(id
, new FileNotFoundException("Object " + id
.name()
502 + " not found in " + pack
.packName
+ "."));
506 // Complete any other objects that we can.
508 final Iterator
<ObjectId
> pending
= swapFetchQueue();
509 while (pending
.hasNext()) {
510 final ObjectId p
= pending
.next();
511 if (pack
.index
.hasObject(p
)) {
524 private Iterator
<ObjectId
> swapFetchQueue() {
525 final Iterator
<ObjectId
> r
= workQueue
.iterator();
526 workQueue
= new LinkedList
<ObjectId
>();
530 private boolean downloadLooseObject(final AnyObjectId id
,
531 final String looseName
, final WalkRemoteObjectDatabase remote
)
532 throws TransportException
{
534 final byte[] compressed
= remote
.open(looseName
).toArray();
535 verifyLooseObject(id
, compressed
);
536 saveLooseObject(id
, compressed
);
538 } catch (FileNotFoundException e
) {
539 // Not available in a loose format from this alternate?
540 // Try another strategy to get the object.
544 } catch (IOException e
) {
545 throw new TransportException("Cannot download " + id
.name(), e
);
549 private void verifyLooseObject(final AnyObjectId id
, final byte[] compressed
)
551 final UnpackedObjectLoader uol
;
553 uol
= new UnpackedObjectLoader(compressed
);
554 } catch (CorruptObjectException parsingError
) {
555 // Some HTTP servers send back a "200 OK" status with an HTML
556 // page that explains the requested file could not be found.
557 // These servers are most certainly misconfigured, but many
558 // of them exist in the world, and many of those are hosting
561 // Since an HTML page is unlikely to hash to one of our loose
562 // objects we treat this condition as a FileNotFoundException
563 // and attempt to recover by getting the object from another
566 final FileNotFoundException e
;
567 e
= new FileNotFoundException(id
.name());
568 e
.initCause(parsingError
);
572 if (!AnyObjectId
.equals(id
, uol
.getId())) {
573 throw new TransportException("Incorrect hash for " + id
.name()
574 + "; computed " + uol
.getId().name() + " as a "
575 + Constants
.encodedTypeString(uol
.getType()) + " from "
576 + compressed
.length
+ " bytes.");
578 if (objCheck
!= null) {
580 objCheck
.check(uol
.getType(), uol
.getCachedBytes());
581 } catch (CorruptObjectException e
) {
582 throw new TransportException("Invalid "
583 + Constants
.encodedTypeString(uol
.getType()) + " "
584 + id
.name() + ":" + e
.getMessage());
589 private void saveLooseObject(final AnyObjectId id
, final byte[] compressed
)
590 throws IOException
, ObjectWritingException
{
593 tmp
= File
.createTempFile("noz", null, local
.getObjectsDirectory());
595 final FileOutputStream out
= new FileOutputStream(tmp
);
597 out
.write(compressed
);
602 } catch (IOException e
) {
607 final File o
= local
.toFile(id
);
611 // Maybe the directory doesn't exist yet as the object
612 // directories are always lazily created. Note that we
613 // try the rename first as the directory likely does exist.
615 o
.getParentFile().mkdir();
620 if (local
.hasObject(id
))
622 throw new ObjectWritingException("Unable to store " + id
.name() + ".");
625 private Collection
<WalkRemoteObjectDatabase
> expandOneAlternate(
626 final AnyObjectId id
, final ProgressMonitor pm
) {
627 while (!noAlternatesYet
.isEmpty()) {
628 final WalkRemoteObjectDatabase wrr
= noAlternatesYet
.removeFirst();
630 pm
.beginTask("Listing alternates", ProgressMonitor
.UNKNOWN
);
631 Collection
<WalkRemoteObjectDatabase
> altList
= wrr
633 if (altList
!= null && !altList
.isEmpty())
635 } catch (IOException e
) {
636 // Try another repository.
646 private void markLocalRefsComplete(final Set
<ObjectId
> have
) throws TransportException
{
647 for (final Ref r
: local
.getAllRefs().values()) {
649 markLocalObjComplete(revWalk
.parseAny(r
.getObjectId()));
650 } catch (IOException readError
) {
651 throw new TransportException("Local ref " + r
.getName()
652 + " is missing object(s).", readError
);
655 for (final ObjectId id
: have
) {
657 markLocalObjComplete(revWalk
.parseAny(id
));
658 } catch (IOException readError
) {
659 throw new TransportException("Missing assumed "+id
.name(), readError
);
664 private void markLocalObjComplete(RevObject obj
) throws IOException
{
665 while (obj
.getType() == Constants
.OBJ_TAG
) {
668 obj
= ((RevTag
) obj
).getObject();
672 switch (obj
.getType()) {
673 case Constants
.OBJ_BLOB
:
676 case Constants
.OBJ_COMMIT
:
677 pushLocalCommit((RevCommit
) obj
);
679 case Constants
.OBJ_TREE
:
680 markTreeComplete((RevTree
) obj
);
685 private void markLocalCommitsComplete(final int until
)
686 throws TransportException
{
689 final RevCommit c
= localCommitQueue
.peek();
690 if (c
== null || c
.getCommitTime() < until
)
692 localCommitQueue
.next();
694 markTreeComplete(c
.getTree());
695 for (final RevCommit p
: c
.getParents())
698 } catch (IOException err
) {
699 throw new TransportException("Local objects incomplete.", err
);
703 private void pushLocalCommit(final RevCommit p
)
704 throws MissingObjectException
, IOException
{
705 if (p
.has(LOCALLY_SEEN
))
712 localCommitQueue
.add(p
);
715 private void markTreeComplete(final RevTree tree
) throws IOException
{
716 if (tree
.has(COMPLETE
))
719 treeWalk
.reset(new ObjectId
[] { tree
});
720 while (treeWalk
.next()) {
721 final FileMode mode
= treeWalk
.getFileMode(0);
722 final int sType
= mode
.getObjectType();
725 case Constants
.OBJ_BLOB
: {
726 final ObjectId sid
= treeWalk
.getObjectId(0);
727 revWalk
.lookupAny(sid
, sType
).add(COMPLETE
);
730 case Constants
.OBJ_TREE
: {
731 final ObjectId sid
= treeWalk
.getObjectId(0);
732 final RevObject o
= revWalk
.lookupAny(sid
, sType
);
733 if (!o
.has(COMPLETE
)) {
735 treeWalk
.enterSubtree();
740 if (FileMode
.GITLINK
.equals(mode
))
742 throw new CorruptObjectException("Invalid mode " + mode
743 + " for " + treeWalk
.getObjectId(0).name() + " "
744 + treeWalk
.getPathString() + " in " + tree
.name() + ".");
749 private void recordError(final AnyObjectId id
, final Throwable what
) {
750 final ObjectId objId
= id
.copy();
751 List
<Throwable
> errors
= fetchErrors
.get(objId
);
752 if (errors
== null) {
753 errors
= new ArrayList
<Throwable
>(2);
754 fetchErrors
.put(objId
, errors
);
759 private class RemotePack
{
760 final WalkRemoteObjectDatabase connection
;
762 final String packName
;
764 final String idxName
;
770 RemotePack(final WalkRemoteObjectDatabase c
, final String pn
) {
771 final File objdir
= local
.getObjectsDirectory();
774 idxName
= packName
.substring(0, packName
.length() - 5) + ".idx";
777 if (tn
.startsWith("pack-"))
778 tn
= tn
.substring(5);
779 if (tn
.endsWith(".idx"))
780 tn
= tn
.substring(0, tn
.length() - 4);
781 tmpIdx
= new File(objdir
, "walk-" + tn
+ ".walkidx");
784 void openIndex(final ProgressMonitor pm
) throws IOException
{
787 if (tmpIdx
.isFile()) {
789 index
= PackIndex
.open(tmpIdx
);
791 } catch (FileNotFoundException err
) {
792 // Fall through and get the file.
796 final WalkRemoteObjectDatabase
.FileStream s
;
797 s
= connection
.open("pack/" + idxName
);
798 pm
.beginTask("Get " + idxName
.substring(0, 12) + "..idx",
799 s
.length
< 0 ? ProgressMonitor
.UNKNOWN
800 : (int) (s
.length
/ 1024));
802 final FileOutputStream fos
= new FileOutputStream(tmpIdx
);
804 final byte[] buf
= new byte[2048];
806 while (!pm
.isCancelled() && (cnt
= s
.in
.read(buf
)) >= 0) {
807 fos
.write(buf
, 0, cnt
);
808 pm
.update(cnt
/ 1024);
813 } catch (IOException err
) {
821 if (pm
.isCancelled()) {
827 index
= PackIndex
.open(tmpIdx
);
828 } catch (IOException e
) {
834 void downloadPack(final ProgressMonitor monitor
) throws IOException
{
835 final WalkRemoteObjectDatabase
.FileStream s
;
838 s
= connection
.open("pack/" + packName
);
839 ip
= IndexPack
.create(local
, s
.in
);
840 ip
.setFixThin(false);
841 ip
.setObjectChecker(objCheck
);
843 ip
.renameAndOpenPack();