1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 package org.eclipse.jgit.transport;
46
47 import static org.eclipse.jgit.lib.RefDatabase.ALL;
48
49 import java.io.File;
50 import java.io.FileNotFoundException;
51 import java.io.FileOutputStream;
52 import java.io.IOException;
53 import java.text.MessageFormat;
54 import java.util.ArrayList;
55 import java.util.Collection;
56 import java.util.HashMap;
57 import java.util.HashSet;
58 import java.util.Iterator;
59 import java.util.LinkedList;
60 import java.util.List;
61 import java.util.Map;
62 import java.util.Set;
63
64 import org.eclipse.jgit.errors.CompoundException;
65 import org.eclipse.jgit.errors.CorruptObjectException;
66 import org.eclipse.jgit.errors.MissingObjectException;
67 import org.eclipse.jgit.errors.TransportException;
68 import org.eclipse.jgit.internal.JGitText;
69 import org.eclipse.jgit.internal.storage.file.ObjectDirectory;
70 import org.eclipse.jgit.internal.storage.file.PackIndex;
71 import org.eclipse.jgit.internal.storage.file.PackLock;
72 import org.eclipse.jgit.internal.storage.file.UnpackedObject;
73 import org.eclipse.jgit.lib.AnyObjectId;
74 import org.eclipse.jgit.lib.Constants;
75 import org.eclipse.jgit.lib.FileMode;
76 import org.eclipse.jgit.lib.MutableObjectId;
77 import org.eclipse.jgit.lib.ObjectChecker;
78 import org.eclipse.jgit.lib.ObjectId;
79 import org.eclipse.jgit.lib.ObjectInserter;
80 import org.eclipse.jgit.lib.ObjectLoader;
81 import org.eclipse.jgit.lib.ObjectReader;
82 import org.eclipse.jgit.lib.ProgressMonitor;
83 import org.eclipse.jgit.lib.Ref;
84 import org.eclipse.jgit.lib.Repository;
85 import org.eclipse.jgit.revwalk.DateRevQueue;
86 import org.eclipse.jgit.revwalk.RevCommit;
87 import org.eclipse.jgit.revwalk.RevFlag;
88 import org.eclipse.jgit.revwalk.RevObject;
89 import org.eclipse.jgit.revwalk.RevTag;
90 import org.eclipse.jgit.revwalk.RevTree;
91 import org.eclipse.jgit.revwalk.RevWalk;
92 import org.eclipse.jgit.treewalk.TreeWalk;
93 import org.eclipse.jgit.util.FileUtils;
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116 class WalkFetchConnection extends BaseFetchConnection {
117
118 final Repository local;
119
120
121 final ObjectChecker objCheck;
122
123
124
125
126
127
128
129
130 private final List<WalkRemoteObjectDatabase> remotes;
131
132
133 private int lastRemoteIdx;
134
135 private final RevWalk revWalk;
136
137 private final TreeWalk treeWalk;
138
139
140 private final RevFlag COMPLETE;
141
142
143 private final RevFlag IN_WORK_QUEUE;
144
145
146 private final RevFlag LOCALLY_SEEN;
147
148
149 private final DateRevQueue localCommitQueue;
150
151
152 private LinkedList<ObjectId> workQueue;
153
154
155 private final LinkedList<WalkRemoteObjectDatabase> noPacksYet;
156
157
158 private final LinkedList<WalkRemoteObjectDatabase> noAlternatesYet;
159
160
161 private final LinkedList<RemotePack> unfetchedPacks;
162
163
164
165
166
167
168
169
170 private final Set<String> packsConsidered;
171
172 private final MutableObjectId idBuffer = new MutableObjectId();
173
174
175
176
177
178
179
180
181 private final HashMap<ObjectId, List<Throwable>> fetchErrors;
182
183 String lockMessage;
184
185 final List<PackLock> packLocks;
186
187
188 final ObjectInserter inserter;
189
190
191 private final ObjectReader reader;
192
193 WalkFetchConnection(final WalkTransport t, final WalkRemoteObjectDatabase w) {
194 Transport wt = (Transport)t;
195 local = wt.local;
196 objCheck = wt.getObjectChecker();
197 inserter = local.newObjectInserter();
198 reader = local.newObjectReader();
199
200 remotes = new ArrayList<WalkRemoteObjectDatabase>();
201 remotes.add(w);
202
203 unfetchedPacks = new LinkedList<RemotePack>();
204 packsConsidered = new HashSet<String>();
205
206 noPacksYet = new LinkedList<WalkRemoteObjectDatabase>();
207 noPacksYet.add(w);
208
209 noAlternatesYet = new LinkedList<WalkRemoteObjectDatabase>();
210 noAlternatesYet.add(w);
211
212 fetchErrors = new HashMap<ObjectId, List<Throwable>>();
213 packLocks = new ArrayList<PackLock>(4);
214
215 revWalk = new RevWalk(reader);
216 revWalk.setRetainBody(false);
217 treeWalk = new TreeWalk(reader);
218 COMPLETE = revWalk.newFlag("COMPLETE");
219 IN_WORK_QUEUE = revWalk.newFlag("IN_WORK_QUEUE");
220 LOCALLY_SEEN = revWalk.newFlag("LOCALLY_SEEN");
221
222 localCommitQueue = new DateRevQueue();
223 workQueue = new LinkedList<ObjectId>();
224 }
225
226 public boolean didFetchTestConnectivity() {
227 return true;
228 }
229
230 @Override
231 protected void doFetch(final ProgressMonitor monitor,
232 final Collection<Ref> want, final Set<ObjectId> have)
233 throws TransportException {
234 markLocalRefsComplete(have);
235 queueWants(want);
236
237 while (!monitor.isCancelled() && !workQueue.isEmpty()) {
238 final ObjectId id = workQueue.removeFirst();
239 if (!(id instanceof RevObject) || !((RevObject) id).has(COMPLETE))
240 downloadObject(monitor, id);
241 process(id);
242 }
243 }
244
245 public Collection<PackLock> getPackLocks() {
246 return packLocks;
247 }
248
249 public void setPackLockMessage(final String message) {
250 lockMessage = message;
251 }
252
253 @Override
254 public void close() {
255 inserter.close();
256 reader.close();
257 for (final RemotePack p : unfetchedPacks) {
258 if (p.tmpIdx != null)
259 p.tmpIdx.delete();
260 }
261 for (final WalkRemoteObjectDatabase r : remotes)
262 r.close();
263 }
264
265 private void queueWants(final Collection<Ref> want)
266 throws TransportException {
267 final HashSet<ObjectId> inWorkQueue = new HashSet<ObjectId>();
268 for (final Ref r : want) {
269 final ObjectId id = r.getObjectId();
270 if (id == null) {
271 throw new NullPointerException(MessageFormat.format(
272 JGitText.get().transportProvidedRefWithNoObjectId, r.getName()));
273 }
274 try {
275 final RevObject obj = revWalk.parseAny(id);
276 if (obj.has(COMPLETE))
277 continue;
278 if (inWorkQueue.add(id)) {
279 obj.add(IN_WORK_QUEUE);
280 workQueue.add(obj);
281 }
282 } catch (MissingObjectException e) {
283 if (inWorkQueue.add(id))
284 workQueue.add(id);
285 } catch (IOException e) {
286 throw new TransportException(MessageFormat.format(JGitText.get().cannotRead, id.name()), e);
287 }
288 }
289 }
290
291 private void process(final ObjectId id) throws TransportException {
292 final RevObject obj;
293 try {
294 if (id instanceof RevObject) {
295 obj = (RevObject) id;
296 if (obj.has(COMPLETE))
297 return;
298 revWalk.parseHeaders(obj);
299 } else {
300 obj = revWalk.parseAny(id);
301 if (obj.has(COMPLETE))
302 return;
303 }
304 } catch (IOException e) {
305 throw new TransportException(MessageFormat.format(JGitText.get().cannotRead, id.name()), e);
306 }
307
308 switch (obj.getType()) {
309 case Constants.OBJ_BLOB:
310 processBlob(obj);
311 break;
312 case Constants.OBJ_TREE:
313 processTree(obj);
314 break;
315 case Constants.OBJ_COMMIT:
316 processCommit(obj);
317 break;
318 case Constants.OBJ_TAG:
319 processTag(obj);
320 break;
321 default:
322 throw new TransportException(MessageFormat.format(JGitText.get().unknownObjectType, id.name()));
323 }
324
325
326
327
328 fetchErrors.remove(id);
329 }
330
331 private void processBlob(final RevObject obj) throws TransportException {
332 try {
333 if (reader.has(obj, Constants.OBJ_BLOB))
334 obj.add(COMPLETE);
335 else
336 throw new TransportException(MessageFormat.format(JGitText
337 .get().cannotReadBlob, obj.name()),
338 new MissingObjectException(obj, Constants.TYPE_BLOB));
339 } catch (IOException error) {
340 throw new TransportException(MessageFormat.format(
341 JGitText.get().cannotReadBlob, obj.name()), error);
342 }
343 }
344
345 private void processTree(final RevObject obj) throws TransportException {
346 try {
347 treeWalk.reset(obj);
348 while (treeWalk.next()) {
349 final FileMode mode = treeWalk.getFileMode(0);
350 final int sType = mode.getObjectType();
351
352 switch (sType) {
353 case Constants.OBJ_BLOB:
354 case Constants.OBJ_TREE:
355 treeWalk.getObjectId(idBuffer, 0);
356 needs(revWalk.lookupAny(idBuffer, sType));
357 continue;
358
359 default:
360 if (FileMode.GITLINK.equals(mode))
361 continue;
362 treeWalk.getObjectId(idBuffer, 0);
363 throw new CorruptObjectException(MessageFormat.format(JGitText.get().invalidModeFor
364 , mode, idBuffer.name(), treeWalk.getPathString(), obj.getId().name()));
365 }
366 }
367 } catch (IOException ioe) {
368 throw new TransportException(MessageFormat.format(JGitText.get().cannotReadTree, obj.name()), ioe);
369 }
370 obj.add(COMPLETE);
371 }
372
373 private void processCommit(final RevObject obj) throws TransportException {
374 final RevCommit commit = (RevCommit) obj;
375 markLocalCommitsComplete(commit.getCommitTime());
376 needs(commit.getTree());
377 for (final RevCommit p : commit.getParents())
378 needs(p);
379 obj.add(COMPLETE);
380 }
381
382 private void processTag(final RevObject obj) {
383 final RevTag tag = (RevTag) obj;
384 needs(tag.getObject());
385 obj.add(COMPLETE);
386 }
387
388 private void needs(final RevObject obj) {
389 if (obj.has(COMPLETE))
390 return;
391 if (!obj.has(IN_WORK_QUEUE)) {
392 obj.add(IN_WORK_QUEUE);
393 workQueue.add(obj);
394 }
395 }
396
397 private void downloadObject(final ProgressMonitor pm, final AnyObjectId id)
398 throws TransportException {
399 if (alreadyHave(id))
400 return;
401
402 for (;;) {
403
404
405
406
407 if (downloadPackedObject(pm, id))
408 return;
409
410
411
412
413 final String idStr = id.name();
414 final String subdir = idStr.substring(0, 2);
415 final String file = idStr.substring(2);
416 final String looseName = subdir + "/" + file;
417
418 for (int i = lastRemoteIdx; i < remotes.size(); i++) {
419 if (downloadLooseObject(id, looseName, remotes.get(i))) {
420 lastRemoteIdx = i;
421 return;
422 }
423 }
424 for (int i = 0; i < lastRemoteIdx; i++) {
425 if (downloadLooseObject(id, looseName, remotes.get(i))) {
426 lastRemoteIdx = i;
427 return;
428 }
429 }
430
431
432
433 while (!noPacksYet.isEmpty()) {
434 final WalkRemoteObjectDatabase wrr = noPacksYet.removeFirst();
435 final Collection<String> packNameList;
436 try {
437 pm.beginTask(JGitText.get().listingPacks,
438 ProgressMonitor.UNKNOWN);
439 packNameList = wrr.getPackNames();
440 } catch (IOException e) {
441
442
443 recordError(id, e);
444 continue;
445 } finally {
446 pm.endTask();
447 }
448
449 if (packNameList == null || packNameList.isEmpty())
450 continue;
451 for (final String packName : packNameList) {
452 if (packsConsidered.add(packName))
453 unfetchedPacks.add(new RemotePack(wrr, packName));
454 }
455 if (downloadPackedObject(pm, id))
456 return;
457 }
458
459
460
461 Collection<WalkRemoteObjectDatabase> al = expandOneAlternate(id, pm);
462 if (al != null && !al.isEmpty()) {
463 for (final WalkRemoteObjectDatabase alt : al) {
464 remotes.add(alt);
465 noPacksYet.add(alt);
466 noAlternatesYet.add(alt);
467 }
468 continue;
469 }
470
471
472
473 List<Throwable> failures = fetchErrors.get(id);
474 final TransportException te;
475
476 te = new TransportException(MessageFormat.format(JGitText.get().cannotGet, id.name()));
477 if (failures != null && !failures.isEmpty()) {
478 if (failures.size() == 1)
479 te.initCause(failures.get(0));
480 else
481 te.initCause(new CompoundException(failures));
482 }
483 throw te;
484 }
485 }
486
487 private boolean alreadyHave(final AnyObjectId id) throws TransportException {
488 try {
489 return reader.has(id);
490 } catch (IOException error) {
491 throw new TransportException(MessageFormat.format(
492 JGitText.get().cannotReadObject, id.name()), error);
493 }
494 }
495
496 private boolean downloadPackedObject(final ProgressMonitor monitor,
497 final AnyObjectId id) throws TransportException {
498
499
500
501 final Iterator<RemotePack> packItr = unfetchedPacks.iterator();
502 while (packItr.hasNext() && !monitor.isCancelled()) {
503 final RemotePack pack = packItr.next();
504 try {
505 pack.openIndex(monitor);
506 } catch (IOException err) {
507
508
509
510
511
512 recordError(id, err);
513 packItr.remove();
514 continue;
515 }
516
517 if (monitor.isCancelled()) {
518
519
520
521
522 return false;
523 }
524
525 if (!pack.index.hasObject(id)) {
526
527
528 continue;
529 }
530
531
532
533
534
535 try {
536 pack.downloadPack(monitor);
537 } catch (IOException err) {
538
539
540
541
542
543 recordError(id, err);
544 continue;
545 } finally {
546
547
548
549
550
551
552 try {
553 if (pack.tmpIdx != null)
554 FileUtils.delete(pack.tmpIdx);
555 } catch (IOException e) {
556 throw new TransportException(e.getMessage(), e);
557 }
558 packItr.remove();
559 }
560
561 if (!alreadyHave(id)) {
562
563
564
565
566 recordError(id, new FileNotFoundException(MessageFormat.format(
567 JGitText.get().objectNotFoundIn, id.name(), pack.packName)));
568 continue;
569 }
570
571
572
573 final Iterator<ObjectId> pending = swapFetchQueue();
574 while (pending.hasNext()) {
575 final ObjectId p = pending.next();
576 if (pack.index.hasObject(p)) {
577 pending.remove();
578 process(p);
579 } else {
580 workQueue.add(p);
581 }
582 }
583 return true;
584
585 }
586 return false;
587 }
588
589 private Iterator<ObjectId> swapFetchQueue() {
590 final Iterator<ObjectId> r = workQueue.iterator();
591 workQueue = new LinkedList<ObjectId>();
592 return r;
593 }
594
595 private boolean downloadLooseObject(final AnyObjectId id,
596 final String looseName, final WalkRemoteObjectDatabase remote)
597 throws TransportException {
598 try {
599 final byte[] compressed = remote.open(looseName).toArray();
600 verifyAndInsertLooseObject(id, compressed);
601 return true;
602 } catch (FileNotFoundException e) {
603
604
605
606 recordError(id, e);
607 return false;
608 } catch (IOException e) {
609 throw new TransportException(MessageFormat.format(JGitText.get().cannotDownload, id.name()), e);
610 }
611 }
612
613 private void verifyAndInsertLooseObject(final AnyObjectId id,
614 final byte[] compressed) throws IOException {
615 final ObjectLoader uol;
616 try {
617 uol = UnpackedObject.parse(compressed, id);
618 } catch (CorruptObjectException parsingError) {
619
620
621
622
623
624
625
626
627
628
629
630 final FileNotFoundException e;
631 e = new FileNotFoundException(id.name());
632 e.initCause(parsingError);
633 throw e;
634 }
635
636 final int type = uol.getType();
637 final byte[] raw = uol.getCachedBytes();
638 if (objCheck != null) {
639 try {
640 objCheck.check(id, type, raw);
641 } catch (CorruptObjectException e) {
642 throw new TransportException(MessageFormat.format(
643 JGitText.get().transportExceptionInvalid,
644 Constants.typeString(type), id.name(), e.getMessage()));
645 }
646 }
647
648 ObjectId act = inserter.insert(type, raw);
649 if (!AnyObjectId.equals(id, act)) {
650 throw new TransportException(MessageFormat.format(
651 JGitText.get().incorrectHashFor, id.name(), act.name(),
652 Constants.typeString(type),
653 Integer.valueOf(compressed.length)));
654 }
655 inserter.flush();
656 }
657
658 private Collection<WalkRemoteObjectDatabase> expandOneAlternate(
659 final AnyObjectId id, final ProgressMonitor pm) {
660 while (!noAlternatesYet.isEmpty()) {
661 final WalkRemoteObjectDatabase wrr = noAlternatesYet.removeFirst();
662 try {
663 pm.beginTask(JGitText.get().listingAlternates, ProgressMonitor.UNKNOWN);
664 Collection<WalkRemoteObjectDatabase> altList = wrr
665 .getAlternates();
666 if (altList != null && !altList.isEmpty())
667 return altList;
668 } catch (IOException e) {
669
670
671 recordError(id, e);
672 } finally {
673 pm.endTask();
674 }
675 }
676 return null;
677 }
678
679 private void markLocalRefsComplete(final Set<ObjectId> have) throws TransportException {
680 Map<String, Ref> refs;
681 try {
682 refs = local.getRefDatabase().getRefs(ALL);
683 } catch (IOException e) {
684 throw new TransportException(e.getMessage(), e);
685 }
686 for (final Ref r : refs.values()) {
687 try {
688 markLocalObjComplete(revWalk.parseAny(r.getObjectId()));
689 } catch (IOException readError) {
690 throw new TransportException(MessageFormat.format(JGitText.get().localRefIsMissingObjects, r.getName()), readError);
691 }
692 }
693 for (final ObjectId id : have) {
694 try {
695 markLocalObjComplete(revWalk.parseAny(id));
696 } catch (IOException readError) {
697 throw new TransportException(MessageFormat.format(JGitText.get().transportExceptionMissingAssumed, id.name()), readError);
698 }
699 }
700 }
701
702 private void markLocalObjComplete(RevObject obj) throws IOException {
703 while (obj.getType() == Constants.OBJ_TAG) {
704 obj.add(COMPLETE);
705 obj = ((RevTag) obj).getObject();
706 revWalk.parseHeaders(obj);
707 }
708
709 switch (obj.getType()) {
710 case Constants.OBJ_BLOB:
711 obj.add(COMPLETE);
712 break;
713 case Constants.OBJ_COMMIT:
714 pushLocalCommit((RevCommit) obj);
715 break;
716 case Constants.OBJ_TREE:
717 markTreeComplete((RevTree) obj);
718 break;
719 }
720 }
721
722 private void markLocalCommitsComplete(final int until)
723 throws TransportException {
724 try {
725 for (;;) {
726 final RevCommit c = localCommitQueue.peek();
727 if (c == null || c.getCommitTime() < until)
728 return;
729 localCommitQueue.next();
730
731 markTreeComplete(c.getTree());
732 for (final RevCommit p : c.getParents())
733 pushLocalCommit(p);
734 }
735 } catch (IOException err) {
736 throw new TransportException(JGitText.get().localObjectsIncomplete, err);
737 }
738 }
739
740 private void pushLocalCommit(final RevCommit p)
741 throws MissingObjectException, IOException {
742 if (p.has(LOCALLY_SEEN))
743 return;
744 revWalk.parseHeaders(p);
745 p.add(LOCALLY_SEEN);
746 p.add(COMPLETE);
747 p.carry(COMPLETE);
748 localCommitQueue.add(p);
749 }
750
751 private void markTreeComplete(final RevTree tree) throws IOException {
752 if (tree.has(COMPLETE))
753 return;
754 tree.add(COMPLETE);
755 treeWalk.reset(tree);
756 while (treeWalk.next()) {
757 final FileMode mode = treeWalk.getFileMode(0);
758 final int sType = mode.getObjectType();
759
760 switch (sType) {
761 case Constants.OBJ_BLOB:
762 treeWalk.getObjectId(idBuffer, 0);
763 revWalk.lookupAny(idBuffer, sType).add(COMPLETE);
764 continue;
765
766 case Constants.OBJ_TREE: {
767 treeWalk.getObjectId(idBuffer, 0);
768 final RevObject o = revWalk.lookupAny(idBuffer, sType);
769 if (!o.has(COMPLETE)) {
770 o.add(COMPLETE);
771 treeWalk.enterSubtree();
772 }
773 continue;
774 }
775 default:
776 if (FileMode.GITLINK.equals(mode))
777 continue;
778 treeWalk.getObjectId(idBuffer, 0);
779 throw new CorruptObjectException(MessageFormat.format(JGitText.get().corruptObjectInvalidMode3
780 , mode, idBuffer.name(), treeWalk.getPathString(), tree.name()));
781 }
782 }
783 }
784
785 private void recordError(final AnyObjectId id, final Throwable what) {
786 final ObjectId objId = id.copy();
787 List<Throwable> errors = fetchErrors.get(objId);
788 if (errors == null) {
789 errors = new ArrayList<Throwable>(2);
790 fetchErrors.put(objId, errors);
791 }
792 errors.add(what);
793 }
794
795 private class RemotePack {
796 final WalkRemoteObjectDatabase connection;
797
798 final String packName;
799
800 final String idxName;
801
802 File tmpIdx;
803
804 PackIndex index;
805
806 RemotePack(final WalkRemoteObjectDatabase c, final String pn) {
807 connection = c;
808 packName = pn;
809 idxName = packName.substring(0, packName.length() - 5) + ".idx";
810
811 String tn = idxName;
812 if (tn.startsWith("pack-"))
813 tn = tn.substring(5);
814 if (tn.endsWith(".idx"))
815 tn = tn.substring(0, tn.length() - 4);
816
817 if (local.getObjectDatabase() instanceof ObjectDirectory) {
818 tmpIdx = new File(((ObjectDirectory) local.getObjectDatabase())
819 .getDirectory(),
820 "walk-" + tn + ".walkidx");
821 }
822 }
823
824 void openIndex(final ProgressMonitor pm) throws IOException {
825 if (index != null)
826 return;
827 if (tmpIdx == null)
828 tmpIdx = File.createTempFile("jgit-walk-", ".idx");
829 else if (tmpIdx.isFile()) {
830 try {
831 index = PackIndex.open(tmpIdx);
832 return;
833 } catch (FileNotFoundException err) {
834
835 }
836 }
837
838 final WalkRemoteObjectDatabase.FileStream s;
839 s = connection.open("pack/" + idxName);
840 pm.beginTask("Get " + idxName.substring(0, 12) + "..idx",
841 s.length < 0 ? ProgressMonitor.UNKNOWN
842 : (int) (s.length / 1024));
843 try {
844 final FileOutputStream fos = new FileOutputStream(tmpIdx);
845 try {
846 final byte[] buf = new byte[2048];
847 int cnt;
848 while (!pm.isCancelled() && (cnt = s.in.read(buf)) >= 0) {
849 fos.write(buf, 0, cnt);
850 pm.update(cnt / 1024);
851 }
852 } finally {
853 fos.close();
854 }
855 } catch (IOException err) {
856 FileUtils.delete(tmpIdx);
857 throw err;
858 } finally {
859 s.in.close();
860 }
861 pm.endTask();
862
863 if (pm.isCancelled()) {
864 FileUtils.delete(tmpIdx);
865 return;
866 }
867
868 try {
869 index = PackIndex.open(tmpIdx);
870 } catch (IOException e) {
871 FileUtils.delete(tmpIdx);
872 throw e;
873 }
874 }
875
876 void downloadPack(final ProgressMonitor monitor) throws IOException {
877 String name = "pack/" + packName;
878 WalkRemoteObjectDatabase.FileStream s = connection.open(name);
879 PackParser parser = inserter.newPackParser(s.in);
880 parser.setAllowThin(false);
881 parser.setObjectChecker(objCheck);
882 parser.setLockMessage(lockMessage);
883 PackLock lock = parser.parse(monitor);
884 if (lock != null)
885 packLocks.add(lock);
886 inserter.flush();
887 }
888 }
889 }