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 12KB

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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  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.text.MessageFormat;
  47. import java.util.ArrayList;
  48. import java.util.Collection;
  49. import java.util.List;
  50. import java.util.Map;
  51. import org.eclipse.jgit.JGitText;
  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.lib.ObjectId;
  57. import org.eclipse.jgit.lib.ProgressMonitor;
  58. import org.eclipse.jgit.lib.Ref;
  59. import org.eclipse.jgit.storage.pack.PackWriter;
  60. import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
  61. /**
  62. * Push implementation using the native Git pack transfer service.
  63. * <p>
  64. * This is the canonical implementation for transferring objects to the remote
  65. * repository from the local repository by talking to the 'git-receive-pack'
  66. * service. Objects are packed on the local side into a pack file and then sent
  67. * to the remote repository.
  68. * <p>
  69. * This connection requires only a bi-directional pipe or socket, and thus is
  70. * easily wrapped up into a local process pipe, anonymous TCP socket, or a
  71. * command executed through an SSH tunnel.
  72. * <p>
  73. * This implementation honors {@link Transport#isPushThin()} option.
  74. * <p>
  75. * Concrete implementations should just call
  76. * {@link #init(java.io.InputStream, java.io.OutputStream)} and
  77. * {@link #readAdvertisedRefs()} methods in constructor or before any use. They
  78. * should also handle resources releasing in {@link #close()} method if needed.
  79. */
  80. public abstract class BasePackPushConnection extends BasePackConnection implements
  81. PushConnection {
  82. static final String CAPABILITY_REPORT_STATUS = "report-status";
  83. static final String CAPABILITY_DELETE_REFS = "delete-refs";
  84. static final String CAPABILITY_OFS_DELTA = "ofs-delta";
  85. static final String CAPABILITY_SIDE_BAND_64K = "side-band-64k";
  86. private final boolean thinPack;
  87. private boolean capableDeleteRefs;
  88. private boolean capableReport;
  89. private boolean capableSideBand;
  90. private boolean capableOfsDelta;
  91. private boolean sentCommand;
  92. private boolean writePack;
  93. /** Time in milliseconds spent transferring the pack data. */
  94. private long packTransferTime;
  95. /**
  96. * Create a new connection to push using the native git transport.
  97. *
  98. * @param packTransport
  99. * the transport.
  100. */
  101. public BasePackPushConnection(final PackTransport packTransport) {
  102. super(packTransport);
  103. thinPack = transport.isPushThin();
  104. }
  105. public void push(final ProgressMonitor monitor,
  106. final Map<String, RemoteRefUpdate> refUpdates)
  107. throws TransportException {
  108. markStartedOperation();
  109. doPush(monitor, refUpdates);
  110. }
  111. @Override
  112. protected TransportException noRepository() {
  113. // Sadly we cannot tell the "invalid URI" case from "push not allowed".
  114. // Opening a fetch connection can help us tell the difference, as any
  115. // useful repository is going to support fetch if it also would allow
  116. // push. So if fetch throws NoRemoteRepositoryException we know the
  117. // URI is wrong. Otherwise we can correctly state push isn't allowed
  118. // as the fetch connection opened successfully.
  119. //
  120. try {
  121. transport.openFetch().close();
  122. } catch (NotSupportedException e) {
  123. // Fall through.
  124. } catch (NoRemoteRepositoryException e) {
  125. // Fetch concluded the repository doesn't exist.
  126. //
  127. return e;
  128. } catch (TransportException e) {
  129. // Fall through.
  130. }
  131. return new TransportException(uri, JGitText.get().pushNotPermitted);
  132. }
  133. /**
  134. * Push one or more objects and update the remote repository.
  135. *
  136. * @param monitor
  137. * progress monitor to receive status updates.
  138. * @param refUpdates
  139. * update commands to be applied to the remote repository.
  140. * @throws TransportException
  141. * if any exception occurs.
  142. */
  143. protected void doPush(final ProgressMonitor monitor,
  144. final Map<String, RemoteRefUpdate> refUpdates)
  145. throws TransportException {
  146. try {
  147. writeCommands(refUpdates.values(), monitor);
  148. if (writePack)
  149. writePack(refUpdates, monitor);
  150. if (sentCommand) {
  151. if (capableReport)
  152. readStatusReport(refUpdates);
  153. if (capableSideBand) {
  154. // Ensure the data channel is at EOF, so we know we have
  155. // read all side-band data from all channels and have a
  156. // complete copy of the messages (if any) buffered from
  157. // the other data channels.
  158. //
  159. int b = in.read();
  160. if (0 <= b)
  161. throw new TransportException(uri, MessageFormat.format(JGitText.get().expectedEOFReceived, (char) b));
  162. }
  163. }
  164. } catch (TransportException e) {
  165. throw e;
  166. } catch (Exception e) {
  167. throw new TransportException(uri, e.getMessage(), e);
  168. } finally {
  169. close();
  170. }
  171. }
  172. private void writeCommands(final Collection<RemoteRefUpdate> refUpdates,
  173. final ProgressMonitor monitor) throws IOException {
  174. final String capabilities = enableCapabilities(monitor);
  175. for (final RemoteRefUpdate rru : refUpdates) {
  176. if (!capableDeleteRefs && rru.isDelete()) {
  177. rru.setStatus(Status.REJECTED_NODELETE);
  178. continue;
  179. }
  180. final StringBuilder sb = new StringBuilder();
  181. final Ref advertisedRef = getRef(rru.getRemoteName());
  182. final ObjectId oldId = (advertisedRef == null ? ObjectId.zeroId()
  183. : advertisedRef.getObjectId());
  184. sb.append(oldId.name());
  185. sb.append(' ');
  186. sb.append(rru.getNewObjectId().name());
  187. sb.append(' ');
  188. sb.append(rru.getRemoteName());
  189. if (!sentCommand) {
  190. sentCommand = true;
  191. sb.append(capabilities);
  192. }
  193. pckOut.writeString(sb.toString());
  194. rru.setStatus(Status.AWAITING_REPORT);
  195. if (!rru.isDelete())
  196. writePack = true;
  197. }
  198. if (monitor.isCancelled())
  199. throw new TransportException(uri, JGitText.get().pushCancelled);
  200. pckOut.end();
  201. outNeedsEnd = false;
  202. }
  203. private String enableCapabilities(final ProgressMonitor monitor) {
  204. final StringBuilder line = new StringBuilder();
  205. capableReport = wantCapability(line, CAPABILITY_REPORT_STATUS);
  206. capableDeleteRefs = wantCapability(line, CAPABILITY_DELETE_REFS);
  207. capableOfsDelta = wantCapability(line, CAPABILITY_OFS_DELTA);
  208. capableSideBand = wantCapability(line, CAPABILITY_SIDE_BAND_64K);
  209. if (capableSideBand) {
  210. in = new SideBandInputStream(in, monitor, getMessageWriter());
  211. pckIn = new PacketLineIn(in);
  212. }
  213. if (line.length() > 0)
  214. line.setCharAt(0, '\0');
  215. return line.toString();
  216. }
  217. private void writePack(final Map<String, RemoteRefUpdate> refUpdates,
  218. final ProgressMonitor monitor) throws IOException {
  219. List<ObjectId> remoteObjects = new ArrayList<ObjectId>(getRefs().size());
  220. List<ObjectId> newObjects = new ArrayList<ObjectId>(refUpdates.size());
  221. final long start;
  222. final PackWriter writer = new PackWriter(transport.getPackConfig(),
  223. local.newObjectReader());
  224. try {
  225. for (final Ref r : getRefs())
  226. remoteObjects.add(r.getObjectId());
  227. remoteObjects.addAll(additionalHaves);
  228. for (final RemoteRefUpdate r : refUpdates.values()) {
  229. if (!ObjectId.zeroId().equals(r.getNewObjectId()))
  230. newObjects.add(r.getNewObjectId());
  231. }
  232. writer.setUseCachedPacks(true);
  233. writer.setThin(thinPack);
  234. writer.setDeltaBaseAsOffset(capableOfsDelta);
  235. writer.preparePack(monitor, newObjects, remoteObjects);
  236. start = System.currentTimeMillis();
  237. writer.writePack(monitor, monitor, out);
  238. } finally {
  239. writer.release();
  240. }
  241. out.flush();
  242. packTransferTime = System.currentTimeMillis() - start;
  243. }
  244. private void readStatusReport(final Map<String, RemoteRefUpdate> refUpdates)
  245. throws IOException {
  246. final String unpackLine = readStringLongTimeout();
  247. if (!unpackLine.startsWith("unpack "))
  248. throw new PackProtocolException(uri, MessageFormat.format(JGitText.get().unexpectedReportLine, unpackLine));
  249. final String unpackStatus = unpackLine.substring("unpack ".length());
  250. if (!unpackStatus.equals("ok"))
  251. throw new TransportException(uri, MessageFormat.format(
  252. JGitText.get().errorOccurredDuringUnpackingOnTheRemoteEnd, unpackStatus));
  253. String refLine;
  254. while ((refLine = pckIn.readString()) != PacketLineIn.END) {
  255. boolean ok = false;
  256. int refNameEnd = -1;
  257. if (refLine.startsWith("ok ")) {
  258. ok = true;
  259. refNameEnd = refLine.length();
  260. } else if (refLine.startsWith("ng ")) {
  261. ok = false;
  262. refNameEnd = refLine.indexOf(" ", 3);
  263. }
  264. if (refNameEnd == -1)
  265. throw new PackProtocolException(MessageFormat.format(JGitText.get().unexpectedReportLine2
  266. , uri, refLine));
  267. final String refName = refLine.substring(3, refNameEnd);
  268. final String message = (ok ? null : refLine
  269. .substring(refNameEnd + 1));
  270. final RemoteRefUpdate rru = refUpdates.get(refName);
  271. if (rru == null)
  272. throw new PackProtocolException(MessageFormat.format(JGitText.get().unexpectedRefReport, uri, refName));
  273. if (ok) {
  274. rru.setStatus(Status.OK);
  275. } else {
  276. rru.setStatus(Status.REJECTED_OTHER_REASON);
  277. rru.setMessage(message);
  278. }
  279. }
  280. for (final RemoteRefUpdate rru : refUpdates.values()) {
  281. if (rru.getStatus() == Status.AWAITING_REPORT)
  282. throw new PackProtocolException(MessageFormat.format(
  283. JGitText.get().expectedReportForRefNotReceived , uri, rru.getRemoteName()));
  284. }
  285. }
  286. private String readStringLongTimeout() throws IOException {
  287. if (timeoutIn == null)
  288. return pckIn.readString();
  289. // The remote side may need a lot of time to choke down the pack
  290. // we just sent them. There may be many deltas that need to be
  291. // resolved by the remote. Its hard to say how long the other
  292. // end is going to be silent. Taking 10x the configured timeout
  293. // or the time spent transferring the pack, whichever is larger,
  294. // gives the other side some reasonable window to process the data,
  295. // but this is just a wild guess.
  296. //
  297. final int oldTimeout = timeoutIn.getTimeout();
  298. final int sendTime = (int) Math.min(packTransferTime, 28800000L);
  299. try {
  300. timeoutIn.setTimeout(10 * Math.max(sendTime, oldTimeout));
  301. return pckIn.readString();
  302. } finally {
  303. timeoutIn.setTimeout(oldTimeout);
  304. }
  305. }
  306. }