Check object connectivity during fetch if fsck is enabled
[egit.git] / org.spearce.jgit / src / org / spearce / jgit / transport / TransportBundle.java
blob7d38b02670d40fd171b8b0098cdfce95c485a49a
1 /*
2 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
4 * Copyright (C) 2008, Google Inc.
6 * All rights reserved.
8 * Redistribution and use in source and binary forms, with or
9 * without modification, are permitted provided that the following
10 * conditions are met:
12 * - Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following
17 * disclaimer in the documentation and/or other materials provided
18 * with the distribution.
20 * - Neither the name of the Git Development Community nor the
21 * names of its contributors may be used to endorse or promote
22 * products derived from this software without specific prior
23 * written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
26 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
27 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
30 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
32 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
33 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
34 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
37 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 package org.spearce.jgit.transport;
42 import java.io.BufferedInputStream;
43 import java.io.IOException;
44 import java.io.InputStream;
45 import java.util.ArrayList;
46 import java.util.Collection;
47 import java.util.HashSet;
48 import java.util.LinkedHashMap;
49 import java.util.List;
50 import java.util.Set;
52 import org.spearce.jgit.errors.MissingBundlePrerequisiteException;
53 import org.spearce.jgit.errors.MissingObjectException;
54 import org.spearce.jgit.errors.NotSupportedException;
55 import org.spearce.jgit.errors.PackProtocolException;
56 import org.spearce.jgit.errors.TransportException;
57 import org.spearce.jgit.lib.Constants;
58 import org.spearce.jgit.lib.ObjectId;
59 import org.spearce.jgit.lib.ProgressMonitor;
60 import org.spearce.jgit.lib.Ref;
61 import org.spearce.jgit.lib.Repository;
62 import org.spearce.jgit.revwalk.RevCommit;
63 import org.spearce.jgit.revwalk.RevFlag;
64 import org.spearce.jgit.revwalk.RevObject;
65 import org.spearce.jgit.revwalk.RevWalk;
66 import org.spearce.jgit.util.RawParseUtils;
68 /**
69 * Supports fetching from a git bundle (sneaker-net object transport).
70 * <p>
71 * Push support for a bundle is complex, as one does not have a peer to
72 * communicate with to decide what the peer already knows. So push is not
73 * supported by the bundle transport.
75 abstract class TransportBundle extends PackTransport {
76 static final String V2_BUNDLE_SIGNATURE = "# v2 git bundle";
78 TransportBundle(final Repository local, final URIish uri) {
79 super(local, uri);
82 @Override
83 public PushConnection openPush() throws NotSupportedException {
84 throw new NotSupportedException(
85 "Push is not supported for bundle transport");
88 @Override
89 public void close() {
90 // Resources must be established per-connection.
93 class BundleFetchConnection extends BaseFetchConnection {
94 InputStream bin;
96 final Set<ObjectId> prereqs = new HashSet<ObjectId>();
98 BundleFetchConnection(final InputStream src) throws TransportException {
99 bin = new BufferedInputStream(src, IndexPack.BUFFER_SIZE);
100 try {
101 switch (readSignature()) {
102 case 2:
103 readBundleV2();
104 break;
105 default:
106 throw new TransportException(uri, "not a bundle");
108 } catch (TransportException err) {
109 close();
110 throw err;
111 } catch (IOException err) {
112 close();
113 throw new TransportException(uri, err.getMessage(), err);
114 } catch (RuntimeException err) {
115 close();
116 throw new TransportException(uri, err.getMessage(), err);
120 private int readSignature() throws IOException {
121 final String rev = readLine(new byte[1024]);
122 if (V2_BUNDLE_SIGNATURE.equals(rev))
123 return 2;
124 throw new TransportException(uri, "not a bundle");
127 private void readBundleV2() throws IOException {
128 final byte[] hdrbuf = new byte[1024];
129 final LinkedHashMap<String, Ref> avail = new LinkedHashMap<String, Ref>();
130 for (;;) {
131 String line = readLine(hdrbuf);
132 if (line.length() == 0)
133 break;
135 if (line.charAt(0) == '-') {
136 prereqs.add(ObjectId.fromString(line.substring(1, 41)));
137 continue;
140 final String name = line.substring(41, line.length());
141 final ObjectId id = ObjectId.fromString(line.substring(0, 40));
142 final Ref prior = avail.put(name, new Ref(Ref.Storage.NETWORK,
143 name, id));
144 if (prior != null)
145 throw duplicateAdvertisement(name);
147 available(avail);
150 private PackProtocolException duplicateAdvertisement(final String name) {
151 return new PackProtocolException(uri,
152 "duplicate advertisements of " + name);
155 private String readLine(final byte[] hdrbuf) throws IOException {
156 bin.mark(hdrbuf.length);
157 final int cnt = bin.read(hdrbuf);
158 int lf = 0;
159 while (lf < cnt && hdrbuf[lf] != '\n')
160 lf++;
161 bin.reset();
162 bin.skip(lf);
163 if (lf < cnt && hdrbuf[lf] == '\n')
164 bin.skip(1);
165 return RawParseUtils.decode(Constants.CHARSET, hdrbuf, 0, lf);
168 public boolean didFetchTestConnectivity() {
169 return false;
172 @Override
173 protected void doFetch(final ProgressMonitor monitor,
174 final Collection<Ref> want) throws TransportException {
175 verifyPrerequisites();
176 try {
177 final IndexPack ip = newIndexPack();
178 ip.index(monitor);
179 ip.renameAndOpenPack();
180 } catch (IOException err) {
181 close();
182 throw new TransportException(uri, err.getMessage(), err);
183 } catch (RuntimeException err) {
184 close();
185 throw new TransportException(uri, err.getMessage(), err);
189 private IndexPack newIndexPack() throws IOException {
190 final IndexPack ip = IndexPack.create(local, bin);
191 ip.setFixThin(true);
192 ip.setObjectChecking(TransportBundle.this.isCheckFetchedObjects());
193 return ip;
196 private void verifyPrerequisites() throws TransportException {
197 if (prereqs.isEmpty())
198 return;
200 final RevWalk rw = new RevWalk(local);
201 final RevFlag PREREQ = rw.newFlag("PREREQ");
202 final RevFlag SEEN = rw.newFlag("SEEN");
204 final List<ObjectId> missing = new ArrayList<ObjectId>();
205 final List<RevObject> commits = new ArrayList<RevObject>();
206 for (final ObjectId p : prereqs) {
207 try {
208 final RevCommit c = rw.parseCommit(p);
209 if (!c.has(PREREQ)) {
210 c.add(PREREQ);
211 commits.add(c);
213 } catch (MissingObjectException notFound) {
214 missing.add(p);
215 } catch (IOException err) {
216 throw new TransportException(uri, "Cannot read commit "
217 + p.name(), err);
220 if (!missing.isEmpty())
221 throw new MissingBundlePrerequisiteException(uri, missing);
223 for (final Ref r : local.getAllRefs().values()) {
224 try {
225 rw.markStart(rw.parseCommit(r.getObjectId()));
226 } catch (IOException readError) {
227 // If we cannot read the value of the ref skip it.
231 int remaining = commits.size();
232 try {
233 RevCommit c;
234 while ((c = rw.next()) != null) {
235 if (c.has(PREREQ)) {
236 c.add(SEEN);
237 if (--remaining == 0)
238 break;
241 } catch (IOException err) {
242 throw new TransportException(uri, "Cannot read object", err);
245 if (remaining > 0) {
246 for (final RevObject o : commits) {
247 if (!o.has(SEEN))
248 missing.add(o);
250 throw new MissingBundlePrerequisiteException(uri, missing);
254 @Override
255 public void close() {
256 if (bin != null) {
257 try {
258 bin.close();
259 } catch (IOException ie) {
260 // Ignore close failures.
261 } finally {
262 bin = null;