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.

FetchProcess.java 16KB

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 년 전
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 년 전
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 년 전
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 년 전
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. /*
  2. * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.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 static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
  46. import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
  47. import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_NONFASTFORWARD;
  48. import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE_NONFASTFORWARD;
  49. import java.io.File;
  50. import java.io.IOException;
  51. import java.io.OutputStreamWriter;
  52. import java.io.Writer;
  53. import java.text.MessageFormat;
  54. import java.util.ArrayList;
  55. import java.util.Collection;
  56. import java.util.Collections;
  57. import java.util.HashMap;
  58. import java.util.HashSet;
  59. import java.util.Iterator;
  60. import java.util.Map;
  61. import java.util.Set;
  62. import java.util.concurrent.TimeUnit;
  63. import org.eclipse.jgit.errors.MissingObjectException;
  64. import org.eclipse.jgit.errors.NotSupportedException;
  65. import org.eclipse.jgit.errors.TransportException;
  66. import org.eclipse.jgit.internal.JGitText;
  67. import org.eclipse.jgit.lib.BatchRefUpdate;
  68. import org.eclipse.jgit.lib.BatchingProgressMonitor;
  69. import org.eclipse.jgit.lib.Constants;
  70. import org.eclipse.jgit.lib.ObjectId;
  71. import org.eclipse.jgit.lib.ProgressMonitor;
  72. import org.eclipse.jgit.lib.Ref;
  73. import org.eclipse.jgit.lib.RefDatabase;
  74. import org.eclipse.jgit.revwalk.ObjectWalk;
  75. import org.eclipse.jgit.revwalk.RevWalk;
  76. import org.eclipse.jgit.storage.file.LockFile;
  77. import org.eclipse.jgit.storage.file.PackLock;
  78. class FetchProcess {
  79. /** Transport we will fetch over. */
  80. private final Transport transport;
  81. /** List of things we want to fetch from the remote repository. */
  82. private final Collection<RefSpec> toFetch;
  83. /** Set of refs we will actually wind up asking to obtain. */
  84. private final HashMap<ObjectId, Ref> askFor = new HashMap<ObjectId, Ref>();
  85. /** Objects we know we have locally. */
  86. private final HashSet<ObjectId> have = new HashSet<ObjectId>();
  87. /** Updates to local tracking branches (if any). */
  88. private final ArrayList<TrackingRefUpdate> localUpdates = new ArrayList<TrackingRefUpdate>();
  89. /** Records to be recorded into FETCH_HEAD. */
  90. private final ArrayList<FetchHeadRecord> fetchHeadUpdates = new ArrayList<FetchHeadRecord>();
  91. private final ArrayList<PackLock> packLocks = new ArrayList<PackLock>();
  92. private FetchConnection conn;
  93. private Map<String, Ref> localRefs;
  94. FetchProcess(final Transport t, final Collection<RefSpec> f) {
  95. transport = t;
  96. toFetch = f;
  97. }
  98. void execute(final ProgressMonitor monitor, final FetchResult result)
  99. throws NotSupportedException, TransportException {
  100. askFor.clear();
  101. localUpdates.clear();
  102. fetchHeadUpdates.clear();
  103. packLocks.clear();
  104. localRefs = null;
  105. try {
  106. executeImp(monitor, result);
  107. } finally {
  108. try {
  109. for (final PackLock lock : packLocks)
  110. lock.unlock();
  111. } catch (IOException e) {
  112. throw new TransportException(e.getMessage(), e);
  113. }
  114. }
  115. }
  116. private void executeImp(final ProgressMonitor monitor,
  117. final FetchResult result) throws NotSupportedException,
  118. TransportException {
  119. conn = transport.openFetch();
  120. try {
  121. result.setAdvertisedRefs(transport.getURI(), conn.getRefsMap());
  122. final Set<Ref> matched = new HashSet<Ref>();
  123. for (final RefSpec spec : toFetch) {
  124. if (spec.getSource() == null)
  125. throw new TransportException(MessageFormat.format(
  126. JGitText.get().sourceRefNotSpecifiedForRefspec, spec));
  127. if (spec.isWildcard())
  128. expandWildcard(spec, matched);
  129. else
  130. expandSingle(spec, matched);
  131. }
  132. Collection<Ref> additionalTags = Collections.<Ref> emptyList();
  133. final TagOpt tagopt = transport.getTagOpt();
  134. if (tagopt == TagOpt.AUTO_FOLLOW)
  135. additionalTags = expandAutoFollowTags();
  136. else if (tagopt == TagOpt.FETCH_TAGS)
  137. expandFetchTags();
  138. final boolean includedTags;
  139. if (!askFor.isEmpty() && !askForIsComplete()) {
  140. fetchObjects(monitor);
  141. includedTags = conn.didFetchIncludeTags();
  142. // Connection was used for object transfer. If we
  143. // do another fetch we must open a new connection.
  144. //
  145. closeConnection(result);
  146. } else {
  147. includedTags = false;
  148. }
  149. if (tagopt == TagOpt.AUTO_FOLLOW && !additionalTags.isEmpty()) {
  150. // There are more tags that we want to follow, but
  151. // not all were asked for on the initial request.
  152. //
  153. have.addAll(askFor.keySet());
  154. askFor.clear();
  155. for (final Ref r : additionalTags) {
  156. ObjectId id = r.getPeeledObjectId();
  157. if (id == null)
  158. id = r.getObjectId();
  159. if (transport.local.hasObject(id))
  160. wantTag(r);
  161. }
  162. if (!askFor.isEmpty() && (!includedTags || !askForIsComplete())) {
  163. reopenConnection();
  164. if (!askFor.isEmpty())
  165. fetchObjects(monitor);
  166. }
  167. }
  168. } finally {
  169. closeConnection(result);
  170. }
  171. BatchRefUpdate batch = transport.local.getRefDatabase()
  172. .newBatchUpdate()
  173. .setAllowNonFastForwards(true)
  174. .setRefLogMessage("fetch", true);
  175. final RevWalk walk = new RevWalk(transport.local);
  176. try {
  177. if (monitor instanceof BatchingProgressMonitor) {
  178. ((BatchingProgressMonitor) monitor).setDelayStart(
  179. 250, TimeUnit.MILLISECONDS);
  180. }
  181. if (transport.isRemoveDeletedRefs())
  182. deleteStaleTrackingRefs(result, batch);
  183. for (TrackingRefUpdate u : localUpdates) {
  184. result.add(u);
  185. batch.addCommand(u.asReceiveCommand());
  186. }
  187. for (ReceiveCommand cmd : batch.getCommands()) {
  188. cmd.updateType(walk);
  189. if (cmd.getType() == UPDATE_NONFASTFORWARD
  190. && cmd instanceof TrackingRefUpdate.Command
  191. && !((TrackingRefUpdate.Command) cmd).canForceUpdate())
  192. cmd.setResult(REJECTED_NONFASTFORWARD);
  193. }
  194. if (transport.isDryRun()) {
  195. for (ReceiveCommand cmd : batch.getCommands()) {
  196. if (cmd.getResult() == NOT_ATTEMPTED)
  197. cmd.setResult(OK);
  198. }
  199. } else
  200. batch.execute(walk, monitor);
  201. } catch (IOException err) {
  202. throw new TransportException(MessageFormat.format(
  203. JGitText.get().failureUpdatingTrackingRef,
  204. getFirstFailedRefName(batch), err.getMessage()), err);
  205. } finally {
  206. walk.release();
  207. }
  208. if (!fetchHeadUpdates.isEmpty()) {
  209. try {
  210. updateFETCH_HEAD(result);
  211. } catch (IOException err) {
  212. throw new TransportException(MessageFormat.format(
  213. JGitText.get().failureUpdatingFETCH_HEAD, err.getMessage()), err);
  214. }
  215. }
  216. }
  217. private void fetchObjects(final ProgressMonitor monitor)
  218. throws TransportException {
  219. try {
  220. conn.setPackLockMessage("jgit fetch " + transport.uri);
  221. conn.fetch(monitor, askFor.values(), have);
  222. } finally {
  223. packLocks.addAll(conn.getPackLocks());
  224. }
  225. if (transport.isCheckFetchedObjects()
  226. && !conn.didFetchTestConnectivity() && !askForIsComplete())
  227. throw new TransportException(transport.getURI(),
  228. JGitText.get().peerDidNotSupplyACompleteObjectGraph);
  229. }
  230. private void closeConnection(final FetchResult result) {
  231. if (conn != null) {
  232. conn.close();
  233. result.addMessages(conn.getMessages());
  234. conn = null;
  235. }
  236. }
  237. private void reopenConnection() throws NotSupportedException,
  238. TransportException {
  239. if (conn != null)
  240. return;
  241. conn = transport.openFetch();
  242. // Since we opened a new connection we cannot be certain
  243. // that the system we connected to has the same exact set
  244. // of objects available (think round-robin DNS and mirrors
  245. // that aren't updated at the same time).
  246. //
  247. // We rebuild our askFor list using only the refs that the
  248. // new connection has offered to us.
  249. //
  250. final HashMap<ObjectId, Ref> avail = new HashMap<ObjectId, Ref>();
  251. for (final Ref r : conn.getRefs())
  252. avail.put(r.getObjectId(), r);
  253. final Collection<Ref> wants = new ArrayList<Ref>(askFor.values());
  254. askFor.clear();
  255. for (final Ref want : wants) {
  256. final Ref newRef = avail.get(want.getObjectId());
  257. if (newRef != null) {
  258. askFor.put(newRef.getObjectId(), newRef);
  259. } else {
  260. removeFetchHeadRecord(want.getObjectId());
  261. removeTrackingRefUpdate(want.getObjectId());
  262. }
  263. }
  264. }
  265. private void removeTrackingRefUpdate(final ObjectId want) {
  266. final Iterator<TrackingRefUpdate> i = localUpdates.iterator();
  267. while (i.hasNext()) {
  268. final TrackingRefUpdate u = i.next();
  269. if (u.getNewObjectId().equals(want))
  270. i.remove();
  271. }
  272. }
  273. private void removeFetchHeadRecord(final ObjectId want) {
  274. final Iterator<FetchHeadRecord> i = fetchHeadUpdates.iterator();
  275. while (i.hasNext()) {
  276. final FetchHeadRecord fh = i.next();
  277. if (fh.newValue.equals(want))
  278. i.remove();
  279. }
  280. }
  281. private void updateFETCH_HEAD(final FetchResult result) throws IOException {
  282. File meta = transport.local.getDirectory();
  283. if (meta == null)
  284. return;
  285. final LockFile lock = new LockFile(new File(meta, "FETCH_HEAD"),
  286. transport.local.getFS());
  287. try {
  288. if (lock.lock()) {
  289. final Writer w = new OutputStreamWriter(lock.getOutputStream());
  290. try {
  291. for (final FetchHeadRecord h : fetchHeadUpdates) {
  292. h.write(w);
  293. result.add(h);
  294. }
  295. } finally {
  296. w.close();
  297. }
  298. lock.commit();
  299. }
  300. } finally {
  301. lock.unlock();
  302. }
  303. }
  304. private boolean askForIsComplete() throws TransportException {
  305. try {
  306. final ObjectWalk ow = new ObjectWalk(transport.local);
  307. try {
  308. for (final ObjectId want : askFor.keySet())
  309. ow.markStart(ow.parseAny(want));
  310. for (final Ref ref : localRefs().values())
  311. ow.markUninteresting(ow.parseAny(ref.getObjectId()));
  312. ow.checkConnectivity();
  313. } finally {
  314. ow.release();
  315. }
  316. return true;
  317. } catch (MissingObjectException e) {
  318. return false;
  319. } catch (IOException e) {
  320. throw new TransportException(JGitText.get().unableToCheckConnectivity, e);
  321. }
  322. }
  323. private void expandWildcard(final RefSpec spec, final Set<Ref> matched)
  324. throws TransportException {
  325. for (final Ref src : conn.getRefs()) {
  326. if (spec.matchSource(src) && matched.add(src))
  327. want(src, spec.expandFromSource(src));
  328. }
  329. }
  330. private void expandSingle(final RefSpec spec, final Set<Ref> matched)
  331. throws TransportException {
  332. final Ref src = conn.getRef(spec.getSource());
  333. if (src == null) {
  334. throw new TransportException(MessageFormat.format(JGitText.get().remoteDoesNotHaveSpec, spec.getSource()));
  335. }
  336. if (matched.add(src))
  337. want(src, spec);
  338. }
  339. private Collection<Ref> expandAutoFollowTags() throws TransportException {
  340. final Collection<Ref> additionalTags = new ArrayList<Ref>();
  341. final Map<String, Ref> haveRefs = localRefs();
  342. for (final Ref r : conn.getRefs()) {
  343. if (!isTag(r))
  344. continue;
  345. Ref local = haveRefs.get(r.getName());
  346. ObjectId obj = r.getObjectId();
  347. if (r.getPeeledObjectId() == null) {
  348. if (local != null && obj.equals(local.getObjectId()))
  349. continue;
  350. if (askFor.containsKey(obj) || transport.local.hasObject(obj))
  351. wantTag(r);
  352. else
  353. additionalTags.add(r);
  354. continue;
  355. }
  356. if (local != null) {
  357. if (!obj.equals(local.getObjectId()))
  358. wantTag(r);
  359. } else if (askFor.containsKey(r.getPeeledObjectId())
  360. || transport.local.hasObject(r.getPeeledObjectId()))
  361. wantTag(r);
  362. else
  363. additionalTags.add(r);
  364. }
  365. return additionalTags;
  366. }
  367. private void expandFetchTags() throws TransportException {
  368. final Map<String, Ref> haveRefs = localRefs();
  369. for (final Ref r : conn.getRefs()) {
  370. if (!isTag(r))
  371. continue;
  372. final Ref local = haveRefs.get(r.getName());
  373. if (local == null || !r.getObjectId().equals(local.getObjectId()))
  374. wantTag(r);
  375. }
  376. }
  377. private void wantTag(final Ref r) throws TransportException {
  378. want(r, new RefSpec().setSource(r.getName())
  379. .setDestination(r.getName()));
  380. }
  381. private void want(final Ref src, final RefSpec spec)
  382. throws TransportException {
  383. final ObjectId newId = src.getObjectId();
  384. if (spec.getDestination() != null) {
  385. final TrackingRefUpdate tru = createUpdate(spec, newId);
  386. if (newId.equals(tru.getOldObjectId()))
  387. return;
  388. localUpdates.add(tru);
  389. }
  390. askFor.put(newId, src);
  391. final FetchHeadRecord fhr = new FetchHeadRecord();
  392. fhr.newValue = newId;
  393. fhr.notForMerge = spec.getDestination() != null;
  394. fhr.sourceName = src.getName();
  395. fhr.sourceURI = transport.getURI();
  396. fetchHeadUpdates.add(fhr);
  397. }
  398. private TrackingRefUpdate createUpdate(RefSpec spec, ObjectId newId)
  399. throws TransportException {
  400. Ref ref = localRefs().get(spec.getDestination());
  401. ObjectId oldId = ref != null && ref.getObjectId() != null
  402. ? ref.getObjectId()
  403. : ObjectId.zeroId();
  404. return new TrackingRefUpdate(
  405. spec.isForceUpdate(),
  406. spec.getSource(),
  407. spec.getDestination(),
  408. oldId,
  409. newId);
  410. }
  411. private Map<String, Ref> localRefs() throws TransportException {
  412. if (localRefs == null) {
  413. try {
  414. localRefs = transport.local.getRefDatabase()
  415. .getRefs(RefDatabase.ALL);
  416. } catch (IOException err) {
  417. throw new TransportException(JGitText.get().cannotListRefs, err);
  418. }
  419. }
  420. return localRefs;
  421. }
  422. private void deleteStaleTrackingRefs(FetchResult result,
  423. BatchRefUpdate batch) throws IOException {
  424. for (final Ref ref : localRefs().values()) {
  425. final String refname = ref.getName();
  426. for (final RefSpec spec : toFetch) {
  427. if (spec.matchDestination(refname)) {
  428. final RefSpec s = spec.expandFromDestination(refname);
  429. if (result.getAdvertisedRef(s.getSource()) == null) {
  430. deleteTrackingRef(result, batch, s, ref);
  431. }
  432. }
  433. }
  434. }
  435. }
  436. private void deleteTrackingRef(final FetchResult result,
  437. final BatchRefUpdate batch, final RefSpec spec, final Ref localRef) {
  438. if (localRef.getObjectId() == null)
  439. return;
  440. TrackingRefUpdate update = new TrackingRefUpdate(
  441. true,
  442. spec.getSource(),
  443. localRef.getName(),
  444. localRef.getObjectId(),
  445. ObjectId.zeroId());
  446. result.add(update);
  447. batch.addCommand(update.asReceiveCommand());
  448. }
  449. private static boolean isTag(final Ref r) {
  450. return isTag(r.getName());
  451. }
  452. private static boolean isTag(final String name) {
  453. return name.startsWith(Constants.R_TAGS);
  454. }
  455. private static String getFirstFailedRefName(BatchRefUpdate batch) {
  456. for (ReceiveCommand cmd : batch.getCommands()) {
  457. if (cmd.getResult() != ReceiveCommand.Result.OK)
  458. return cmd.getRefName();
  459. }
  460. return "";
  461. }
  462. }