You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

BasePackPushConnection.java 13KB

Capture non-progress side band #2 messages and put in result Any messages received on side band #2 that aren't scraped as a progress message into our ProgressMonitor are now forwarded to a buffer which is later included into the OperationResult object. Application callers can use this buffer to present the additional messages from the remote peer after the push or fetch operation has concluded. The smart push connections using the native send-pack/receive-pack protocol now request side-band-64k capability if it is available and forward any messages received through that channel onto this message buffer. This makes hook messages available over smart HTTP, or even over SSH. The SSH transport was modified to redirect the remote command's stderr stream into the message buffer, interleaved with any data received over side band #2. Due to buffering between these two different channels in the SSH channel mux itself the order of any writes between the two cannot be ensured, but it tries to stay close. The local fork transport was also modified to redirect the local receive-pack's stderr into the message buffer, rather than going to the invoking JVM's System.err. This gives applications a chance to log the local error messages, rather than needing to redirect their JVM's stderr before startup. To keep things simple, the application has to wait for the entire operation to complete before it can see the messages. This may be a downside if the user is trying to debug a remote hook that is blocking indefinitely, the user would need to abort the connection before they can inspect the message buffer in any sort of UI built on top of JGit. Change-Id: Ibc215f4569e63071da5b7e5c6674ce924ae39e11 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Capture non-progress side band #2 messages and put in result Any messages received on side band #2 that aren't scraped as a progress message into our ProgressMonitor are now forwarded to a buffer which is later included into the OperationResult object. Application callers can use this buffer to present the additional messages from the remote peer after the push or fetch operation has concluded. The smart push connections using the native send-pack/receive-pack protocol now request side-band-64k capability if it is available and forward any messages received through that channel onto this message buffer. This makes hook messages available over smart HTTP, or even over SSH. The SSH transport was modified to redirect the remote command's stderr stream into the message buffer, interleaved with any data received over side band #2. Due to buffering between these two different channels in the SSH channel mux itself the order of any writes between the two cannot be ensured, but it tries to stay close. The local fork transport was also modified to redirect the local receive-pack's stderr into the message buffer, rather than going to the invoking JVM's System.err. This gives applications a chance to log the local error messages, rather than needing to redirect their JVM's stderr before startup. To keep things simple, the application has to wait for the entire operation to complete before it can see the messages. This may be a downside if the user is trying to debug a remote hook that is blocking indefinitely, the user would need to abort the connection before they can inspect the message buffer in any sort of UI built on top of JGit. Change-Id: Ibc215f4569e63071da5b7e5c6674ce924ae39e11 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Capture non-progress side band #2 messages and put in result Any messages received on side band #2 that aren't scraped as a progress message into our ProgressMonitor are now forwarded to a buffer which is later included into the OperationResult object. Application callers can use this buffer to present the additional messages from the remote peer after the push or fetch operation has concluded. The smart push connections using the native send-pack/receive-pack protocol now request side-band-64k capability if it is available and forward any messages received through that channel onto this message buffer. This makes hook messages available over smart HTTP, or even over SSH. The SSH transport was modified to redirect the remote command's stderr stream into the message buffer, interleaved with any data received over side band #2. Due to buffering between these two different channels in the SSH channel mux itself the order of any writes between the two cannot be ensured, but it tries to stay close. The local fork transport was also modified to redirect the local receive-pack's stderr into the message buffer, rather than going to the invoking JVM's System.err. This gives applications a chance to log the local error messages, rather than needing to redirect their JVM's stderr before startup. To keep things simple, the application has to wait for the entire operation to complete before it can see the messages. This may be a downside if the user is trying to debug a remote hook that is blocking indefinitely, the user would need to abort the connection before they can inspect the message buffer in any sort of UI built on top of JGit. Change-Id: Ibc215f4569e63071da5b7e5c6674ce924ae39e11 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Capture non-progress side band #2 messages and put in result Any messages received on side band #2 that aren't scraped as a progress message into our ProgressMonitor are now forwarded to a buffer which is later included into the OperationResult object. Application callers can use this buffer to present the additional messages from the remote peer after the push or fetch operation has concluded. The smart push connections using the native send-pack/receive-pack protocol now request side-band-64k capability if it is available and forward any messages received through that channel onto this message buffer. This makes hook messages available over smart HTTP, or even over SSH. The SSH transport was modified to redirect the remote command's stderr stream into the message buffer, interleaved with any data received over side band #2. Due to buffering between these two different channels in the SSH channel mux itself the order of any writes between the two cannot be ensured, but it tries to stay close. The local fork transport was also modified to redirect the local receive-pack's stderr into the message buffer, rather than going to the invoking JVM's System.err. This gives applications a chance to log the local error messages, rather than needing to redirect their JVM's stderr before startup. To keep things simple, the application has to wait for the entire operation to complete before it can see the messages. This may be a downside if the user is trying to debug a remote hook that is blocking indefinitely, the user would need to abort the connection before they can inspect the message buffer in any sort of UI built on top of JGit. Change-Id: Ibc215f4569e63071da5b7e5c6674ce924ae39e11 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Capture non-progress side band #2 messages and put in result Any messages received on side band #2 that aren't scraped as a progress message into our ProgressMonitor are now forwarded to a buffer which is later included into the OperationResult object. Application callers can use this buffer to present the additional messages from the remote peer after the push or fetch operation has concluded. The smart push connections using the native send-pack/receive-pack protocol now request side-band-64k capability if it is available and forward any messages received through that channel onto this message buffer. This makes hook messages available over smart HTTP, or even over SSH. The SSH transport was modified to redirect the remote command's stderr stream into the message buffer, interleaved with any data received over side band #2. Due to buffering between these two different channels in the SSH channel mux itself the order of any writes between the two cannot be ensured, but it tries to stay close. The local fork transport was also modified to redirect the local receive-pack's stderr into the message buffer, rather than going to the invoking JVM's System.err. This gives applications a chance to log the local error messages, rather than needing to redirect their JVM's stderr before startup. To keep things simple, the application has to wait for the entire operation to complete before it can see the messages. This may be a downside if the user is trying to debug a remote hook that is blocking indefinitely, the user would need to abort the connection before they can inspect the message buffer in any sort of UI built on top of JGit. Change-Id: Ibc215f4569e63071da5b7e5c6674ce924ae39e11 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
Capture non-progress side band #2 messages and put in result Any messages received on side band #2 that aren't scraped as a progress message into our ProgressMonitor are now forwarded to a buffer which is later included into the OperationResult object. Application callers can use this buffer to present the additional messages from the remote peer after the push or fetch operation has concluded. The smart push connections using the native send-pack/receive-pack protocol now request side-band-64k capability if it is available and forward any messages received through that channel onto this message buffer. This makes hook messages available over smart HTTP, or even over SSH. The SSH transport was modified to redirect the remote command's stderr stream into the message buffer, interleaved with any data received over side band #2. Due to buffering between these two different channels in the SSH channel mux itself the order of any writes between the two cannot be ensured, but it tries to stay close. The local fork transport was also modified to redirect the local receive-pack's stderr into the message buffer, rather than going to the invoking JVM's System.err. This gives applications a chance to log the local error messages, rather than needing to redirect their JVM's stderr before startup. To keep things simple, the application has to wait for the entire operation to complete before it can see the messages. This may be a downside if the user is trying to debug a remote hook that is blocking indefinitely, the user would need to abort the connection before they can inspect the message buffer in any sort of UI built on top of JGit. Change-Id: Ibc215f4569e63071da5b7e5c6674ce924ae39e11 Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
14 years ago
PackWriter: Support reuse of entire packs The most expensive part of packing a repository for transport to another system is enumerating all of the objects in the repository. Once this gets to the size of the linux-2.6 repository (1.8 million objects), enumeration can take several CPU minutes and costs a lot of temporary working set memory. Teach PackWriter to efficiently reuse an existing "cached pack" by answering a clone request with a thin pack followed by a larger cached pack appended to the end. This requires the repository owner to first construct the cached pack by hand, and record the tip commits inside of $GIT_DIR/objects/info/cached-packs: cd $GIT_DIR root=$(git rev-parse master) tmp=objects/.tmp-$$ names=$(echo $root | git pack-objects --keep-true-parents --revs $tmp) for n in $names; do chmod a-w $tmp-$n.pack $tmp-$n.idx touch objects/pack/pack-$n.keep mv $tmp-$n.pack objects/pack/pack-$n.pack mv $tmp-$n.idx objects/pack/pack-$n.idx done (echo "+ $root"; for n in $names; do echo "P $n"; done; echo) >>objects/info/cached-packs git repack -a -d When a clone request needs to include $root, the corresponding cached pack will be copied as-is, rather than enumerating all of the objects that are reachable from $root. For a linux-2.6 kernel repository that should be about 376 MiB, the above process creates two packs of 368 MiB and 38 MiB[1]. This is a local disk usage increase of ~26 MiB, due to reduced delta compression between the large cached pack and the smaller recent activity pack. The overhead is similar to 1 full copy of the compressed project sources. With this cached pack in hand, JGit daemon completes a clone request in 1m17s less time, but a slightly larger data transfer (+2.39 MiB): Before: remote: Counting objects: 1861830, done remote: Finding sources: 100% (1861830/1861830) remote: Getting sizes: 100% (88243/88243) remote: Compressing objects: 100% (88184/88184) Receiving objects: 100% (1861830/1861830), 376.01 MiB | 19.01 MiB/s, done. remote: Total 1861830 (delta 4706), reused 1851053 (delta 1553844) Resolving deltas: 100% (1564621/1564621), done. real 3m19.005s After: remote: Counting objects: 1601, done remote: Counting objects: 1828460, done remote: Finding sources: 100% (50475/50475) remote: Getting sizes: 100% (18843/18843) remote: Compressing objects: 100% (7585/7585) remote: Total 1861830 (delta 2407), reused 1856197 (delta 37510) Receiving objects: 100% (1861830/1861830), 378.40 MiB | 31.31 MiB/s, done. Resolving deltas: 100% (1559477/1559477), done. real 2m2.938s Repository owners can periodically refresh their cached packs by repacking their repository, folding all newer objects into a larger cached pack. Since repacking is already considered to be a normal Git maintenance activity, this isn't a very big burden. [1] In this test $root was set back about two weeks. Change-Id: Ib87131d5c4b5e8c5cacb0f4fe16ff4ece554734b Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
13 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. /*
  2. * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
  3. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
  4. * and other copyright owners as documented in the project's IP log.
  5. *
  6. * This program and the accompanying materials are made available
  7. * under the terms of the Eclipse Distribution License v1.0 which
  8. * accompanies this distribution, is reproduced below, and is
  9. * available at http://www.eclipse.org/org/documents/edl-v10.php
  10. *
  11. * All rights reserved.
  12. *
  13. * Redistribution and use in source and binary forms, with or
  14. * without modification, are permitted provided that the following
  15. * conditions are met:
  16. *
  17. * - Redistributions of source code must retain the above copyright
  18. * notice, this list of conditions and the following disclaimer.
  19. *
  20. * - Redistributions in binary form must reproduce the above
  21. * copyright notice, this list of conditions and the following
  22. * disclaimer in the documentation and/or other materials provided
  23. * with the distribution.
  24. *
  25. * - Neither the name of the Eclipse Foundation, Inc. nor the
  26. * names of its contributors may be used to endorse or promote
  27. * products derived from this software without specific prior
  28. * written permission.
  29. *
  30. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  31. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  32. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  33. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  34. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  35. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  36. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  37. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  38. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  39. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  40. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  41. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  42. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  43. */
  44. package org.eclipse.jgit.transport;
  45. import java.io.IOException;
  46. import java.io.OutputStream;
  47. import java.text.MessageFormat;
  48. import java.util.Collection;
  49. import java.util.HashSet;
  50. import java.util.Map;
  51. import java.util.Set;
  52. import org.eclipse.jgit.errors.NoRemoteRepositoryException;
  53. import org.eclipse.jgit.errors.NotSupportedException;
  54. import org.eclipse.jgit.errors.PackProtocolException;
  55. import org.eclipse.jgit.errors.TransportException;
  56. import org.eclipse.jgit.internal.JGitText;
  57. import org.eclipse.jgit.internal.storage.pack.PackWriter;
  58. import org.eclipse.jgit.lib.ObjectId;
  59. import org.eclipse.jgit.lib.ProgressMonitor;
  60. import org.eclipse.jgit.lib.Ref;
  61. import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
  62. /**
  63. * Push implementation using the native Git pack transfer service.
  64. * <p>
  65. * This is the canonical implementation for transferring objects to the remote
  66. * repository from the local repository by talking to the 'git-receive-pack'
  67. * service. Objects are packed on the local side into a pack file and then sent
  68. * to the remote repository.
  69. * <p>
  70. * This connection requires only a bi-directional pipe or socket, and thus is
  71. * easily wrapped up into a local process pipe, anonymous TCP socket, or a
  72. * command executed through an SSH tunnel.
  73. * <p>
  74. * This implementation honors {@link Transport#isPushThin()} option.
  75. * <p>
  76. * Concrete implementations should just call
  77. * {@link #init(java.io.InputStream, java.io.OutputStream)} and
  78. * {@link #readAdvertisedRefs()} methods in constructor or before any use. They
  79. * should also handle resources releasing in {@link #close()} method if needed.
  80. */
  81. public abstract class BasePackPushConnection extends BasePackConnection implements
  82. PushConnection {
  83. /**
  84. * The client expects a status report after the server processes the pack.
  85. * @since 2.0
  86. */
  87. public static final String CAPABILITY_REPORT_STATUS = GitProtocolConstants.CAPABILITY_REPORT_STATUS;
  88. /**
  89. * The server supports deleting refs.
  90. * @since 2.0
  91. */
  92. public static final String CAPABILITY_DELETE_REFS = GitProtocolConstants.CAPABILITY_DELETE_REFS;
  93. /**
  94. * The server supports packs with OFS deltas.
  95. * @since 2.0
  96. */
  97. public static final String CAPABILITY_OFS_DELTA = GitProtocolConstants.CAPABILITY_OFS_DELTA;
  98. /**
  99. * The client supports using the 64K side-band for progress messages.
  100. * @since 2.0
  101. */
  102. public static final String CAPABILITY_SIDE_BAND_64K = GitProtocolConstants.CAPABILITY_SIDE_BAND_64K;
  103. private final boolean thinPack;
  104. private boolean capableDeleteRefs;
  105. private boolean capableReport;
  106. private boolean capableSideBand;
  107. private boolean capableOfsDelta;
  108. private boolean sentCommand;
  109. private boolean writePack;
  110. /** Time in milliseconds spent transferring the pack data. */
  111. private long packTransferTime;
  112. /**
  113. * Create a new connection to push using the native git transport.
  114. *
  115. * @param packTransport
  116. * the transport.
  117. */
  118. public BasePackPushConnection(final PackTransport packTransport) {
  119. super(packTransport);
  120. thinPack = transport.isPushThin();
  121. }
  122. public void push(final ProgressMonitor monitor,
  123. final Map<String, RemoteRefUpdate> refUpdates)
  124. throws TransportException {
  125. push(monitor, refUpdates, null);
  126. }
  127. /**
  128. * @since 3.0
  129. */
  130. public void push(final ProgressMonitor monitor,
  131. final Map<String, RemoteRefUpdate> refUpdates, OutputStream outputStream)
  132. throws TransportException {
  133. markStartedOperation();
  134. doPush(monitor, refUpdates, outputStream);
  135. }
  136. @Override
  137. protected TransportException noRepository() {
  138. // Sadly we cannot tell the "invalid URI" case from "push not allowed".
  139. // Opening a fetch connection can help us tell the difference, as any
  140. // useful repository is going to support fetch if it also would allow
  141. // push. So if fetch throws NoRemoteRepositoryException we know the
  142. // URI is wrong. Otherwise we can correctly state push isn't allowed
  143. // as the fetch connection opened successfully.
  144. //
  145. try {
  146. transport.openFetch().close();
  147. } catch (NotSupportedException e) {
  148. // Fall through.
  149. } catch (NoRemoteRepositoryException e) {
  150. // Fetch concluded the repository doesn't exist.
  151. //
  152. return e;
  153. } catch (TransportException e) {
  154. // Fall through.
  155. }
  156. return new TransportException(uri, JGitText.get().pushNotPermitted);
  157. }
  158. /**
  159. * Push one or more objects and update the remote repository.
  160. *
  161. * @param monitor
  162. * progress monitor to receive status updates.
  163. * @param refUpdates
  164. * update commands to be applied to the remote repository.
  165. * @param outputStream
  166. * output stream to write sideband messages to
  167. * @throws TransportException
  168. * if any exception occurs.
  169. * @since 3.0
  170. */
  171. protected void doPush(final ProgressMonitor monitor,
  172. final Map<String, RemoteRefUpdate> refUpdates,
  173. OutputStream outputStream) throws TransportException {
  174. try {
  175. writeCommands(refUpdates.values(), monitor, outputStream);
  176. if (writePack)
  177. writePack(refUpdates, monitor);
  178. if (sentCommand) {
  179. if (capableReport)
  180. readStatusReport(refUpdates);
  181. if (capableSideBand) {
  182. // Ensure the data channel is at EOF, so we know we have
  183. // read all side-band data from all channels and have a
  184. // complete copy of the messages (if any) buffered from
  185. // the other data channels.
  186. //
  187. int b = in.read();
  188. if (0 <= b)
  189. throw new TransportException(uri, MessageFormat.format(
  190. JGitText.get().expectedEOFReceived,
  191. Character.valueOf((char) b)));
  192. }
  193. }
  194. } catch (TransportException e) {
  195. throw e;
  196. } catch (Exception e) {
  197. throw new TransportException(uri, e.getMessage(), e);
  198. } finally {
  199. close();
  200. }
  201. }
  202. private void writeCommands(final Collection<RemoteRefUpdate> refUpdates,
  203. final ProgressMonitor monitor, OutputStream outputStream) throws IOException {
  204. final String capabilities = enableCapabilities(monitor, outputStream);
  205. for (final RemoteRefUpdate rru : refUpdates) {
  206. if (!capableDeleteRefs && rru.isDelete()) {
  207. rru.setStatus(Status.REJECTED_NODELETE);
  208. continue;
  209. }
  210. final StringBuilder sb = new StringBuilder();
  211. final Ref advertisedRef = getRef(rru.getRemoteName());
  212. final ObjectId oldId = (advertisedRef == null ? ObjectId.zeroId()
  213. : advertisedRef.getObjectId());
  214. sb.append(oldId.name());
  215. sb.append(' ');
  216. sb.append(rru.getNewObjectId().name());
  217. sb.append(' ');
  218. sb.append(rru.getRemoteName());
  219. if (!sentCommand) {
  220. sentCommand = true;
  221. sb.append(capabilities);
  222. }
  223. pckOut.writeString(sb.toString());
  224. rru.setStatus(Status.AWAITING_REPORT);
  225. if (!rru.isDelete())
  226. writePack = true;
  227. }
  228. if (monitor.isCancelled())
  229. throw new TransportException(uri, JGitText.get().pushCancelled);
  230. pckOut.end();
  231. outNeedsEnd = false;
  232. }
  233. private String enableCapabilities(final ProgressMonitor monitor,
  234. OutputStream outputStream) {
  235. final StringBuilder line = new StringBuilder();
  236. capableReport = wantCapability(line, CAPABILITY_REPORT_STATUS);
  237. capableDeleteRefs = wantCapability(line, CAPABILITY_DELETE_REFS);
  238. capableOfsDelta = wantCapability(line, CAPABILITY_OFS_DELTA);
  239. capableSideBand = wantCapability(line, CAPABILITY_SIDE_BAND_64K);
  240. if (capableSideBand) {
  241. in = new SideBandInputStream(in, monitor, getMessageWriter(),
  242. outputStream);
  243. pckIn = new PacketLineIn(in);
  244. }
  245. addUserAgentCapability(line);
  246. if (line.length() > 0)
  247. line.setCharAt(0, '\0');
  248. return line.toString();
  249. }
  250. private void writePack(final Map<String, RemoteRefUpdate> refUpdates,
  251. final ProgressMonitor monitor) throws IOException {
  252. Set<ObjectId> remoteObjects = new HashSet<ObjectId>();
  253. Set<ObjectId> newObjects = new HashSet<ObjectId>();
  254. try (final PackWriter writer = new PackWriter(transport.getPackConfig(),
  255. local.newObjectReader())) {
  256. for (final Ref r : getRefs()) {
  257. // only add objects that we actually have
  258. ObjectId oid = r.getObjectId();
  259. if (local.hasObject(oid))
  260. remoteObjects.add(oid);
  261. }
  262. remoteObjects.addAll(additionalHaves);
  263. for (final RemoteRefUpdate r : refUpdates.values()) {
  264. if (!ObjectId.zeroId().equals(r.getNewObjectId()))
  265. newObjects.add(r.getNewObjectId());
  266. }
  267. writer.setIndexDisabled(true);
  268. writer.setUseCachedPacks(true);
  269. writer.setUseBitmaps(true);
  270. writer.setThin(thinPack);
  271. writer.setReuseValidatingObjects(false);
  272. writer.setDeltaBaseAsOffset(capableOfsDelta);
  273. writer.preparePack(monitor, newObjects, remoteObjects);
  274. writer.writePack(monitor, monitor, out);
  275. packTransferTime = writer.getStatistics().getTimeWriting();
  276. }
  277. }
  278. private void readStatusReport(final Map<String, RemoteRefUpdate> refUpdates)
  279. throws IOException {
  280. final String unpackLine = readStringLongTimeout();
  281. if (!unpackLine.startsWith("unpack ")) //$NON-NLS-1$
  282. throw new PackProtocolException(uri, MessageFormat.format(JGitText.get().unexpectedReportLine, unpackLine));
  283. final String unpackStatus = unpackLine.substring("unpack ".length()); //$NON-NLS-1$
  284. if (!unpackStatus.equals("ok")) //$NON-NLS-1$
  285. throw new TransportException(uri, MessageFormat.format(
  286. JGitText.get().errorOccurredDuringUnpackingOnTheRemoteEnd, unpackStatus));
  287. String refLine;
  288. while ((refLine = pckIn.readString()) != PacketLineIn.END) {
  289. boolean ok = false;
  290. int refNameEnd = -1;
  291. if (refLine.startsWith("ok ")) { //$NON-NLS-1$
  292. ok = true;
  293. refNameEnd = refLine.length();
  294. } else if (refLine.startsWith("ng ")) { //$NON-NLS-1$
  295. ok = false;
  296. refNameEnd = refLine.indexOf(" ", 3); //$NON-NLS-1$
  297. }
  298. if (refNameEnd == -1)
  299. throw new PackProtocolException(MessageFormat.format(JGitText.get().unexpectedReportLine2
  300. , uri, refLine));
  301. final String refName = refLine.substring(3, refNameEnd);
  302. final String message = (ok ? null : refLine
  303. .substring(refNameEnd + 1));
  304. final RemoteRefUpdate rru = refUpdates.get(refName);
  305. if (rru == null)
  306. throw new PackProtocolException(MessageFormat.format(JGitText.get().unexpectedRefReport, uri, refName));
  307. if (ok) {
  308. rru.setStatus(Status.OK);
  309. } else {
  310. rru.setStatus(Status.REJECTED_OTHER_REASON);
  311. rru.setMessage(message);
  312. }
  313. }
  314. for (final RemoteRefUpdate rru : refUpdates.values()) {
  315. if (rru.getStatus() == Status.AWAITING_REPORT)
  316. throw new PackProtocolException(MessageFormat.format(
  317. JGitText.get().expectedReportForRefNotReceived , uri, rru.getRemoteName()));
  318. }
  319. }
  320. private String readStringLongTimeout() throws IOException {
  321. if (timeoutIn == null)
  322. return pckIn.readString();
  323. // The remote side may need a lot of time to choke down the pack
  324. // we just sent them. There may be many deltas that need to be
  325. // resolved by the remote. Its hard to say how long the other
  326. // end is going to be silent. Taking 10x the configured timeout
  327. // or the time spent transferring the pack, whichever is larger,
  328. // gives the other side some reasonable window to process the data,
  329. // but this is just a wild guess.
  330. //
  331. final int oldTimeout = timeoutIn.getTimeout();
  332. final int sendTime = (int) Math.min(packTransferTime, 28800000L);
  333. try {
  334. timeoutIn.setTimeout(10 * Math.max(sendTime, oldTimeout));
  335. return pckIn.readString();
  336. } finally {
  337. timeoutIn.setTimeout(oldTimeout);
  338. }
  339. }
  340. }