1 /*
2 * Copyright (C) 2008-2009, Google Inc.
3 * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
4 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
5 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
6 * and other copyright owners as documented in the project's IP log.
7 *
8 * This program and the accompanying materials are made available
9 * under the terms of the Eclipse Distribution License v1.0 which
10 * accompanies this distribution, is reproduced below, and is
11 * available at http://www.eclipse.org/org/documents/edl-v10.php
12 *
13 * All rights reserved.
14 *
15 * Redistribution and use in source and binary forms, with or
16 * without modification, are permitted provided that the following
17 * conditions are met:
18 *
19 * - Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 *
22 * - Redistributions in binary form must reproduce the above
23 * copyright notice, this list of conditions and the following
24 * disclaimer in the documentation and/or other materials provided
25 * with the distribution.
26 *
27 * - Neither the name of the Eclipse Foundation, Inc. nor the
28 * names of its contributors may be used to endorse or promote
29 * products derived from this software without specific prior
30 * written permission.
31 *
32 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
33 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
34 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
35 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
37 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
38 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
39 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
40 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
41 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
42 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
43 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
44 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45 */
46
47 package org.eclipse.jgit.transport;
48
49 import static org.eclipse.jgit.lib.RefDatabase.ALL;
50
51 import java.io.BufferedReader;
52 import java.io.IOException;
53 import java.io.InputStream;
54 import java.io.InputStreamReader;
55 import java.io.OutputStream;
56 import java.io.PrintStream;
57 import java.lang.ref.WeakReference;
58 import java.lang.reflect.Field;
59 import java.lang.reflect.Modifier;
60 import java.net.URISyntaxException;
61 import java.net.URL;
62 import java.text.MessageFormat;
63 import java.util.ArrayList;
64 import java.util.Collection;
65 import java.util.Collections;
66 import java.util.Enumeration;
67 import java.util.HashSet;
68 import java.util.LinkedList;
69 import java.util.List;
70 import java.util.Map;
71 import java.util.Vector;
72 import java.util.concurrent.CopyOnWriteArrayList;
73
74 import org.eclipse.jgit.api.errors.AbortedByHookException;
75 import org.eclipse.jgit.errors.NotSupportedException;
76 import org.eclipse.jgit.errors.TransportException;
77 import org.eclipse.jgit.hooks.Hooks;
78 import org.eclipse.jgit.hooks.PrePushHook;
79 import org.eclipse.jgit.internal.JGitText;
80 import org.eclipse.jgit.lib.Constants;
81 import org.eclipse.jgit.lib.NullProgressMonitor;
82 import org.eclipse.jgit.lib.ObjectChecker;
83 import org.eclipse.jgit.lib.ProgressMonitor;
84 import org.eclipse.jgit.lib.Ref;
85 import org.eclipse.jgit.lib.Repository;
86 import org.eclipse.jgit.storage.pack.PackConfig;
87
88 /**
89 * Connects two Git repositories together and copies objects between them.
90 * <p>
91 * A transport can be used for either fetching (copying objects into the
92 * caller's repository from the remote repository) or pushing (copying objects
93 * into the remote repository from the caller's repository). Each transport
94 * implementation is responsible for the details associated with establishing
95 * the network connection(s) necessary for the copy, as well as actually
96 * shuffling data back and forth.
97 * <p>
98 * Transport instances and the connections they create are not thread-safe.
99 * Callers must ensure a transport is accessed by only one thread at a time.
100 */
101 public abstract class Transport implements AutoCloseable {
102 /** Type of operation a Transport is being opened for. */
103 public enum Operation {
104 /** Transport is to fetch objects locally. */
105 FETCH,
106 /** Transport is to push objects remotely. */
107 PUSH;
108 }
109
110 private static final List<WeakReference<TransportProtocol>> protocols =
111 new CopyOnWriteArrayList<WeakReference<TransportProtocol>>();
112
113 static {
114 // Registration goes backwards in order of priority.
115 register(TransportLocal.PROTO_LOCAL);
116 register(TransportBundleFile.PROTO_BUNDLE);
117 register(TransportAmazonS3.PROTO_S3);
118 register(TransportGitAnon.PROTO_GIT);
119 register(TransportSftp.PROTO_SFTP);
120 register(TransportHttp.PROTO_FTP);
121 register(TransportHttp.PROTO_HTTP);
122 register(TransportGitSsh.PROTO_SSH);
123
124 registerByService();
125 }
126
127 private static void registerByService() {
128 ClassLoader ldr = Thread.currentThread().getContextClassLoader();
129 if (ldr == null)
130 ldr = Transport.class.getClassLoader();
131 Enumeration<URL> catalogs = catalogs(ldr);
132 while (catalogs.hasMoreElements())
133 scan(ldr, catalogs.nextElement());
134 }
135
136 private static Enumeration<URL> catalogs(ClassLoader ldr) {
137 try {
138 String prefix = "META-INF/services/"; //$NON-NLS-1$
139 String name = prefix + Transport.class.getName();
140 return ldr.getResources(name);
141 } catch (IOException err) {
142 return new Vector<URL>().elements();
143 }
144 }
145
146 private static void scan(ClassLoader ldr, URL url) {
147 BufferedReader br;
148 try {
149 InputStream urlIn = url.openStream();
150 br = new BufferedReader(new InputStreamReader(urlIn, "UTF-8")); //$NON-NLS-1$
151 } catch (IOException err) {
152 // If we cannot read from the service list, go to the next.
153 //
154 return;
155 }
156
157 try {
158 String line;
159 while ((line = br.readLine()) != null) {
160 line = line.trim();
161 if (line.length() == 0)
162 continue;
163 int comment = line.indexOf('#');
164 if (comment == 0)
165 continue;
166 if (comment != -1)
167 line = line.substring(0, comment).trim();
168 load(ldr, line);
169 }
170 } catch (IOException err) {
171 // If we failed during a read, ignore the error.
172 //
173 } finally {
174 try {
175 br.close();
176 } catch (IOException e) {
177 // Ignore the close error; we are only reading.
178 }
179 }
180 }
181
182 private static void load(ClassLoader ldr, String cn) {
183 Class<?> clazz;
184 try {
185 clazz = Class.forName(cn, false, ldr);
186 } catch (ClassNotFoundException notBuiltin) {
187 // Doesn't exist, even though the service entry is present.
188 //
189 return;
190 }
191
192 for (Field f : clazz.getDeclaredFields()) {
193 if ((f.getModifiers() & Modifier.STATIC) == Modifier.STATIC
194 && TransportProtocol.class.isAssignableFrom(f.getType())) {
195 TransportProtocol proto;
196 try {
197 proto = (TransportProtocol) f.get(null);
198 } catch (IllegalArgumentException e) {
199 // If we cannot access the field, don't.
200 continue;
201 } catch (IllegalAccessException e) {
202 // If we cannot access the field, don't.
203 continue;
204 }
205 if (proto != null)
206 register(proto);
207 }
208 }
209 }
210
211 /**
212 * Register a TransportProtocol instance for use during open.
213 * <p>
214 * Protocol definitions are held by WeakReference, allowing them to be
215 * garbage collected when the calling application drops all strongly held
216 * references to the TransportProtocol. Therefore applications should use a
217 * singleton pattern as described in {@link TransportProtocol}'s class
218 * documentation to ensure their protocol does not get disabled by garbage
219 * collection earlier than expected.
220 * <p>
221 * The new protocol is registered in front of all earlier protocols, giving
222 * it higher priority than the built-in protocol definitions.
223 *
224 * @param proto
225 * the protocol definition. Must not be null.
226 */
227 public static void register(TransportProtocol proto) {
228 protocols.add(0, new WeakReference<TransportProtocol>(proto));
229 }
230
231 /**
232 * Unregister a TransportProtocol instance.
233 * <p>
234 * Unregistering a protocol usually isn't necessary, as protocols are held
235 * by weak references and will automatically clear when they are garbage
236 * collected by the JVM. Matching is handled by reference equality, so the
237 * exact reference given to {@link #register(TransportProtocol)} must be
238 * used.
239 *
240 * @param proto
241 * the exact object previously given to register.
242 */
243 public static void unregister(TransportProtocol proto) {
244 for (WeakReference<TransportProtocol> ref : protocols) {
245 TransportProtocol refProto = ref.get();
246 if (refProto == null || refProto == proto)
247 protocols.remove(ref);
248 }
249 }
250
251 /**
252 * Obtain a copy of the registered protocols.
253 *
254 * @return an immutable copy of the currently registered protocols.
255 */
256 public static List<TransportProtocol> getTransportProtocols() {
257 int cnt = protocols.size();
258 List<TransportProtocol> res = new ArrayList<TransportProtocol>(cnt);
259 for (WeakReference<TransportProtocol> ref : protocols) {
260 TransportProtocol proto = ref.get();
261 if (proto != null)
262 res.add(proto);
263 else
264 protocols.remove(ref);
265 }
266 return Collections.unmodifiableList(res);
267 }
268
269 /**
270 * Open a new transport instance to connect two repositories.
271 * <p>
272 * This method assumes {@link Operation#FETCH}.
273 *
274 * @param local
275 * existing local repository.
276 * @param remote
277 * location of the remote repository - may be URI or remote
278 * configuration name.
279 * @return the new transport instance. Never null. In case of multiple URIs
280 * in remote configuration, only the first is chosen.
281 * @throws URISyntaxException
282 * the location is not a remote defined in the configuration
283 * file and is not a well-formed URL.
284 * @throws NotSupportedException
285 * the protocol specified is not supported.
286 * @throws TransportException
287 * the transport cannot open this URI.
288 */
289 public static Transport open(final Repository local, final String remote)
290 throws NotSupportedException, URISyntaxException,
291 TransportException {
292 return open(local, remote, Operation.FETCH);
293 }
294
295 /**
296 * Open a new transport instance to connect two repositories.
297 *
298 * @param local
299 * existing local repository.
300 * @param remote
301 * location of the remote repository - may be URI or remote
302 * configuration name.
303 * @param op
304 * planned use of the returned Transport; the URI may differ
305 * based on the type of connection desired.
306 * @return the new transport instance. Never null. In case of multiple URIs
307 * in remote configuration, only the first is chosen.
308 * @throws URISyntaxException
309 * the location is not a remote defined in the configuration
310 * file and is not a well-formed URL.
311 * @throws NotSupportedException
312 * the protocol specified is not supported.
313 * @throws TransportException
314 * the transport cannot open this URI.
315 */
316 public static Transport open(final Repository local, final String remote,
317 final Operation op) throws NotSupportedException,
318 URISyntaxException, TransportException {
319 if (local != null) {
320 final RemoteConfig cfg = new RemoteConfig(local.getConfig(), remote);
321 if (doesNotExist(cfg))
322 return open(local, new URIish(remote), null);
323 return open(local, cfg, op);
324 } else
325 return open(new URIish(remote));
326
327 }
328
329 /**
330 * Open new transport instances to connect two repositories.
331 * <p>
332 * This method assumes {@link Operation#FETCH}.
333 *
334 * @param local
335 * existing local repository.
336 * @param remote
337 * location of the remote repository - may be URI or remote
338 * configuration name.
339 * @return the list of new transport instances for every URI in remote
340 * configuration.
341 * @throws URISyntaxException
342 * the location is not a remote defined in the configuration
343 * file and is not a well-formed URL.
344 * @throws NotSupportedException
345 * the protocol specified is not supported.
346 * @throws TransportException
347 * the transport cannot open this URI.
348 */
349 public static List<Transport> openAll(final Repository local,
350 final String remote) throws NotSupportedException,
351 URISyntaxException, TransportException {
352 return openAll(local, remote, Operation.FETCH);
353 }
354
355 /**
356 * Open new transport instances to connect two repositories.
357 *
358 * @param local
359 * existing local repository.
360 * @param remote
361 * location of the remote repository - may be URI or remote
362 * configuration name.
363 * @param op
364 * planned use of the returned Transport; the URI may differ
365 * based on the type of connection desired.
366 * @return the list of new transport instances for every URI in remote
367 * configuration.
368 * @throws URISyntaxException
369 * the location is not a remote defined in the configuration
370 * file and is not a well-formed URL.
371 * @throws NotSupportedException
372 * the protocol specified is not supported.
373 * @throws TransportException
374 * the transport cannot open this URI.
375 */
376 public static List<Transport> openAll(final Repository local,
377 final String remote, final Operation op)
378 throws NotSupportedException, URISyntaxException,
379 TransportException {
380 final RemoteConfig cfg = new RemoteConfig(local.getConfig(), remote);
381 if (doesNotExist(cfg)) {
382 final ArrayList<Transport> transports = new ArrayList<Transport>(1);
383 transports.add(open(local, new URIish(remote), null));
384 return transports;
385 }
386 return openAll(local, cfg, op);
387 }
388
389 /**
390 * Open a new transport instance to connect two repositories.
391 * <p>
392 * This method assumes {@link Operation#FETCH}.
393 *
394 * @param local
395 * existing local repository.
396 * @param cfg
397 * configuration describing how to connect to the remote
398 * repository.
399 * @return the new transport instance. Never null. In case of multiple URIs
400 * in remote configuration, only the first is chosen.
401 * @throws NotSupportedException
402 * the protocol specified is not supported.
403 * @throws TransportException
404 * the transport cannot open this URI.
405 * @throws IllegalArgumentException
406 * if provided remote configuration doesn't have any URI
407 * associated.
408 */
409 public static Transport open(final Repository local, final RemoteConfig cfg)
410 throws NotSupportedException, TransportException {
411 return open(local, cfg, Operation.FETCH);
412 }
413
414 /**
415 * Open a new transport instance to connect two repositories.
416 *
417 * @param local
418 * existing local repository.
419 * @param cfg
420 * configuration describing how to connect to the remote
421 * repository.
422 * @param op
423 * planned use of the returned Transport; the URI may differ
424 * based on the type of connection desired.
425 * @return the new transport instance. Never null. In case of multiple URIs
426 * in remote configuration, only the first is chosen.
427 * @throws NotSupportedException
428 * the protocol specified is not supported.
429 * @throws TransportException
430 * the transport cannot open this URI.
431 * @throws IllegalArgumentException
432 * if provided remote configuration doesn't have any URI
433 * associated.
434 */
435 public static Transport open(final Repository local,
436 final RemoteConfig cfg, final Operation op)
437 throws NotSupportedException, TransportException {
438 final List<URIish> uris = getURIs(cfg, op);
439 if (uris.isEmpty())
440 throw new IllegalArgumentException(MessageFormat.format(
441 JGitText.get().remoteConfigHasNoURIAssociated, cfg.getName()));
442 final Transport tn = open(local, uris.get(0), cfg.getName());
443 tn.applyConfig(cfg);
444 return tn;
445 }
446
447 /**
448 * Open new transport instances to connect two repositories.
449 * <p>
450 * This method assumes {@link Operation#FETCH}.
451 *
452 * @param local
453 * existing local repository.
454 * @param cfg
455 * configuration describing how to connect to the remote
456 * repository.
457 * @return the list of new transport instances for every URI in remote
458 * configuration.
459 * @throws NotSupportedException
460 * the protocol specified is not supported.
461 * @throws TransportException
462 * the transport cannot open this URI.
463 */
464 public static List<Transport> openAll(final Repository local,
465 final RemoteConfig cfg) throws NotSupportedException,
466 TransportException {
467 return openAll(local, cfg, Operation.FETCH);
468 }
469
470 /**
471 * Open new transport instances to connect two repositories.
472 *
473 * @param local
474 * existing local repository.
475 * @param cfg
476 * configuration describing how to connect to the remote
477 * repository.
478 * @param op
479 * planned use of the returned Transport; the URI may differ
480 * based on the type of connection desired.
481 * @return the list of new transport instances for every URI in remote
482 * configuration.
483 * @throws NotSupportedException
484 * the protocol specified is not supported.
485 * @throws TransportException
486 * the transport cannot open this URI.
487 */
488 public static List<Transport> openAll(final Repository local,
489 final RemoteConfig cfg, final Operation op)
490 throws NotSupportedException, TransportException {
491 final List<URIish> uris = getURIs(cfg, op);
492 final List<Transport> transports = new ArrayList<Transport>(uris.size());
493 for (final URIish uri : uris) {
494 final Transport tn = open(local, uri, cfg.getName());
495 tn.applyConfig(cfg);
496 transports.add(tn);
497 }
498 return transports;
499 }
500
501 private static List<URIish> getURIs(final RemoteConfig cfg,
502 final Operation op) {
503 switch (op) {
504 case FETCH:
505 return cfg.getURIs();
506 case PUSH: {
507 List<URIish> uris = cfg.getPushURIs();
508 if (uris.isEmpty())
509 uris = cfg.getURIs();
510 return uris;
511 }
512 default:
513 throw new IllegalArgumentException(op.toString());
514 }
515 }
516
517 private static boolean doesNotExist(final RemoteConfig cfg) {
518 return cfg.getURIs().isEmpty() && cfg.getPushURIs().isEmpty();
519 }
520
521 /**
522 * Open a new transport instance to connect two repositories.
523 *
524 * @param local
525 * existing local repository.
526 * @param uri
527 * location of the remote repository.
528 * @return the new transport instance. Never null.
529 * @throws NotSupportedException
530 * the protocol specified is not supported.
531 * @throws TransportException
532 * the transport cannot open this URI.
533 */
534 public static Transport open(final Repository local, final URIish uri)
535 throws NotSupportedException, TransportException {
536 return open(local, uri, null);
537 }
538
539 /**
540 * Open a new transport instance to connect two repositories.
541 *
542 * @param local
543 * existing local repository.
544 * @param uri
545 * location of the remote repository.
546 * @param remoteName
547 * name of the remote, if the remote as configured in
548 * {@code local}; otherwise null.
549 * @return the new transport instance. Never null.
550 * @throws NotSupportedException
551 * the protocol specified is not supported.
552 * @throws TransportException
553 * the transport cannot open this URI.
554 */
555 public static Transport open(Repository local, URIish uri, String remoteName)
556 throws NotSupportedException, TransportException {
557 for (WeakReference<TransportProtocol> ref : protocols) {
558 TransportProtocol proto = ref.get();
559 if (proto == null) {
560 protocols.remove(ref);
561 continue;
562 }
563
564 if (proto.canHandle(uri, local, remoteName)) {
565 Transport tn = proto.open(uri, local, remoteName);
566 tn.prePush = Hooks.prePush(local, tn.hookOutRedirect);
567 tn.prePush.setRemoteLocation(uri.toString());
568 tn.prePush.setRemoteName(remoteName);
569 return tn;
570 }
571 }
572
573 throw new NotSupportedException(MessageFormat.format(JGitText.get().URINotSupported, uri));
574 }
575
576 /**
577 * Open a new transport with no local repository.
578 * <p>
579 * Note that the resulting transport instance can not be used for fetching
580 * or pushing, but only for reading remote refs.
581 *
582 * @param uri
583 * @return new Transport instance
584 * @throws NotSupportedException
585 * @throws TransportException
586 */
587 public static Transport open(URIish uri) throws NotSupportedException, TransportException {
588 for (WeakReference<TransportProtocol> ref : protocols) {
589 TransportProtocol proto = ref.get();
590 if (proto == null) {
591 protocols.remove(ref);
592 continue;
593 }
594
595 if (proto.canHandle(uri, null, null))
596 return proto.open(uri);
597 }
598
599 throw new NotSupportedException(MessageFormat.format(JGitText.get().URINotSupported, uri));
600 }
601
602 /**
603 * Convert push remote refs update specification from {@link RefSpec} form
604 * to {@link RemoteRefUpdate}. Conversion expands wildcards by matching
605 * source part to local refs. expectedOldObjectId in RemoteRefUpdate is
606 * always set as null. Tracking branch is configured if RefSpec destination
607 * matches source of any fetch ref spec for this transport remote
608 * configuration.
609 *
610 * @param db
611 * local database.
612 * @param specs
613 * collection of RefSpec to convert.
614 * @param fetchSpecs
615 * fetch specifications used for finding localtracking refs. May
616 * be null or empty collection.
617 * @return collection of set up {@link RemoteRefUpdate}.
618 * @throws IOException
619 * when problem occurred during conversion or specification set
620 * up: most probably, missing objects or refs.
621 */
622 public static Collection<RemoteRefUpdate> findRemoteRefUpdatesFor(
623 final Repository db, final Collection<RefSpec> specs,
624 Collection<RefSpec> fetchSpecs) throws IOException {
625 if (fetchSpecs == null)
626 fetchSpecs = Collections.emptyList();
627 final List<RemoteRefUpdate> result = new LinkedList<RemoteRefUpdate>();
628 final Collection<RefSpec> procRefs = expandPushWildcardsFor(db, specs);
629
630 for (final RefSpec spec : procRefs) {
631 String srcSpec = spec.getSource();
632 final Ref srcRef = db.getRef(srcSpec);
633 if (srcRef != null)
634 srcSpec = srcRef.getName();
635
636 String destSpec = spec.getDestination();
637 if (destSpec == null) {
638 // No destination (no-colon in ref-spec), DWIMery assumes src
639 //
640 destSpec = srcSpec;
641 }
642
643 if (srcRef != null && !destSpec.startsWith(Constants.R_REFS)) {
644 // Assume the same kind of ref at the destination, e.g.
645 // "refs/heads/foo:master", DWIMery assumes master is also
646 // under "refs/heads/".
647 //
648 final String n = srcRef.getName();
649 final int kindEnd = n.indexOf('/', Constants.R_REFS.length());
650 destSpec = n.substring(0, kindEnd + 1) + destSpec;
651 }
652
653 final boolean forceUpdate = spec.isForceUpdate();
654 final String localName = findTrackingRefName(destSpec, fetchSpecs);
655 final RemoteRefUpdate rru = new RemoteRefUpdate(db, srcSpec,
656 destSpec, forceUpdate, localName, null);
657 result.add(rru);
658 }
659 return result;
660 }
661
662 private static Collection<RefSpec> expandPushWildcardsFor(
663 final Repository db, final Collection<RefSpec> specs)
664 throws IOException {
665 final Map<String, Ref> localRefs = db.getRefDatabase().getRefs(ALL);
666 final Collection<RefSpec> procRefs = new HashSet<RefSpec>();
667
668 for (final RefSpec spec : specs) {
669 if (spec.isWildcard()) {
670 for (final Ref localRef : localRefs.values()) {
671 if (spec.matchSource(localRef))
672 procRefs.add(spec.expandFromSource(localRef));
673 }
674 } else {
675 procRefs.add(spec);
676 }
677 }
678 return procRefs;
679 }
680
681 private static String findTrackingRefName(final String remoteName,
682 final Collection<RefSpec> fetchSpecs) {
683 // try to find matching tracking refs
684 for (final RefSpec fetchSpec : fetchSpecs) {
685 if (fetchSpec.matchSource(remoteName)) {
686 if (fetchSpec.isWildcard())
687 return fetchSpec.expandFromSource(remoteName)
688 .getDestination();
689 else
690 return fetchSpec.getDestination();
691 }
692 }
693 return null;
694 }
695
696 /**
697 * Default setting for {@link #fetchThin} option.
698 */
699 public static final boolean DEFAULT_FETCH_THIN = true;
700
701 /**
702 * Default setting for {@link #pushThin} option.
703 */
704 public static final boolean DEFAULT_PUSH_THIN = false;
705
706 /**
707 * Specification for fetch or push operations, to fetch or push all tags.
708 * Acts as --tags.
709 */
710 public static final RefSpec REFSPEC_TAGS = new RefSpec(
711 "refs/tags/*:refs/tags/*"); //$NON-NLS-1$
712
713 /**
714 * Specification for push operation, to push all refs under refs/heads. Acts
715 * as --all.
716 */
717 public static final RefSpec REFSPEC_PUSH_ALL = new RefSpec(
718 "refs/heads/*:refs/heads/*"); //$NON-NLS-1$
719
720 /** The repository this transport fetches into, or pushes out of. */
721 protected final Repository local;
722
723 /** The URI used to create this transport. */
724 protected final URIish uri;
725
726 /** Name of the upload pack program, if it must be executed. */
727 private String optionUploadPack = RemoteConfig.DEFAULT_UPLOAD_PACK;
728
729 /** Specifications to apply during fetch. */
730 private List<RefSpec> fetch = Collections.emptyList();
731
732 /**
733 * How {@link #fetch(ProgressMonitor, Collection)} should handle tags.
734 * <p>
735 * We default to {@link TagOpt#NO_TAGS} so as to avoid fetching annotated
736 * tags during one-shot fetches used for later merges. This prevents
737 * dragging down tags from repositories that we do not have established
738 * tracking branches for. If we do not track the source repository, we most
739 * likely do not care about any tags it publishes.
740 */
741 private TagOpt tagopt = TagOpt.NO_TAGS;
742
743 /** Should fetch request thin-pack if remote repository can produce it. */
744 private boolean fetchThin = DEFAULT_FETCH_THIN;
745
746 /** Name of the receive pack program, if it must be executed. */
747 private String optionReceivePack = RemoteConfig.DEFAULT_RECEIVE_PACK;
748
749 /** Specifications to apply during push. */
750 private List<RefSpec> push = Collections.emptyList();
751
752 /** Should push produce thin-pack when sending objects to remote repository. */
753 private boolean pushThin = DEFAULT_PUSH_THIN;
754
755 /** Should push be all-or-nothing atomic behavior? */
756 private boolean pushAtomic;
757
758 /** Should push just check for operation result, not really push. */
759 private boolean dryRun;
760
761 /** Should an incoming (fetch) transfer validate objects? */
762 private ObjectChecker objectChecker;
763
764 /** Should refs no longer on the source be pruned from the destination? */
765 private boolean removeDeletedRefs;
766
767 /** Timeout in seconds to wait before aborting an IO read or write. */
768 private int timeout;
769
770 /** Pack configuration used by this transport to make pack file. */
771 private PackConfig packConfig;
772
773 /** Assists with authentication the connection. */
774 private CredentialsProvider credentialsProvider;
775
776 private PrintStream hookOutRedirect;
777
778 private PrePushHook prePush;
779 /**
780 * Create a new transport instance.
781 *
782 * @param local
783 * the repository this instance will fetch into, or push out of.
784 * This must be the repository passed to
785 * {@link #open(Repository, URIish)}.
786 * @param uri
787 * the URI used to access the remote repository. This must be the
788 * URI passed to {@link #open(Repository, URIish)}.
789 */
790 protected Transport(final Repository local, final URIish uri) {
791 final TransferConfig tc = local.getConfig().get(TransferConfig.KEY);
792 this.local = local;
793 this.uri = uri;
794 this.objectChecker = tc.newObjectChecker();
795 this.credentialsProvider = CredentialsProvider.getDefault();
796 prePush = Hooks.prePush(local, hookOutRedirect);
797 }
798
799 /**
800 * Create a minimal transport instance not tied to a single repository.
801 *
802 * @param uri
803 */
804 protected Transport(final URIish uri) {
805 this.uri = uri;
806 this.local = null;
807 this.objectChecker = new ObjectChecker();
808 this.credentialsProvider = CredentialsProvider.getDefault();
809 }
810
811 /**
812 * Get the URI this transport connects to.
813 * <p>
814 * Each transport instance connects to at most one URI at any point in time.
815 *
816 * @return the URI describing the location of the remote repository.
817 */
818 public URIish getURI() {
819 return uri;
820 }
821
822 /**
823 * Get the name of the remote executable providing upload-pack service.
824 *
825 * @return typically "git-upload-pack".
826 */
827 public String getOptionUploadPack() {
828 return optionUploadPack;
829 }
830
831 /**
832 * Set the name of the remote executable providing upload-pack services.
833 *
834 * @param where
835 * name of the executable.
836 */
837 public void setOptionUploadPack(final String where) {
838 if (where != null && where.length() > 0)
839 optionUploadPack = where;
840 else
841 optionUploadPack = RemoteConfig.DEFAULT_UPLOAD_PACK;
842 }
843
844 /**
845 * Get the description of how annotated tags should be treated during fetch.
846 *
847 * @return option indicating the behavior of annotated tags in fetch.
848 */
849 public TagOpt getTagOpt() {
850 return tagopt;
851 }
852
853 /**
854 * Set the description of how annotated tags should be treated on fetch.
855 *
856 * @param option
857 * method to use when handling annotated tags.
858 */
859 public void setTagOpt(final TagOpt option) {
860 tagopt = option != null ? option : TagOpt.AUTO_FOLLOW;
861 }
862
863 /**
864 * Default setting is: {@link #DEFAULT_FETCH_THIN}
865 *
866 * @return true if fetch should request thin-pack when possible; false
867 * otherwise
868 * @see PackTransport
869 */
870 public boolean isFetchThin() {
871 return fetchThin;
872 }
873
874 /**
875 * Set the thin-pack preference for fetch operation. Default setting is:
876 * {@link #DEFAULT_FETCH_THIN}
877 *
878 * @param fetchThin
879 * true when fetch should request thin-pack when possible; false
880 * when it shouldn't
881 * @see PackTransport
882 */
883 public void setFetchThin(final boolean fetchThin) {
884 this.fetchThin = fetchThin;
885 }
886
887 /**
888 * @return true if fetch will verify received objects are formatted
889 * correctly. Validating objects requires more CPU time on the
890 * client side of the connection.
891 */
892 public boolean isCheckFetchedObjects() {
893 return getObjectChecker() != null;
894 }
895
896 /**
897 * @param check
898 * true to enable checking received objects; false to assume all
899 * received objects are valid.
900 * @see #setObjectChecker(ObjectChecker)
901 */
902 public void setCheckFetchedObjects(final boolean check) {
903 if (check && objectChecker == null)
904 setObjectChecker(new ObjectChecker());
905 else if (!check && objectChecker != null)
906 setObjectChecker(null);
907 }
908
909 /**
910 * @return configured object checker for received objects, or null.
911 * @since 3.6
912 */
913 public ObjectChecker getObjectChecker() {
914 return objectChecker;
915 }
916
917 /**
918 * @param impl
919 * if non-null the object checking instance to verify each
920 * received object with; null to disable object checking.
921 * @since 3.6
922 */
923 public void setObjectChecker(ObjectChecker impl) {
924 objectChecker = impl;
925 }
926
927 /**
928 * Default setting is: {@link RemoteConfig#DEFAULT_RECEIVE_PACK}
929 *
930 * @return remote executable providing receive-pack service for pack
931 * transports.
932 * @see PackTransport
933 */
934 public String getOptionReceivePack() {
935 return optionReceivePack;
936 }
937
938 /**
939 * Set remote executable providing receive-pack service for pack transports.
940 * Default setting is: {@link RemoteConfig#DEFAULT_RECEIVE_PACK}
941 *
942 * @param optionReceivePack
943 * remote executable, if null or empty default one is set;
944 */
945 public void setOptionReceivePack(String optionReceivePack) {
946 if (optionReceivePack != null && optionReceivePack.length() > 0)
947 this.optionReceivePack = optionReceivePack;
948 else
949 this.optionReceivePack = RemoteConfig.DEFAULT_RECEIVE_PACK;
950 }
951
952 /**
953 * Default setting is: {@value #DEFAULT_PUSH_THIN}
954 *
955 * @return true if push should produce thin-pack in pack transports
956 * @see PackTransport
957 */
958 public boolean isPushThin() {
959 return pushThin;
960 }
961
962 /**
963 * Set thin-pack preference for push operation. Default setting is:
964 * {@value #DEFAULT_PUSH_THIN}
965 *
966 * @param pushThin
967 * true when push should produce thin-pack in pack transports;
968 * false when it shouldn't
969 * @see PackTransport
970 */
971 public void setPushThin(final boolean pushThin) {
972 this.pushThin = pushThin;
973 }
974
975 /**
976 * Default setting is false.
977 *
978 * @return true if push requires all-or-nothing atomic behavior.
979 * @since 4.2
980 */
981 public boolean isPushAtomic() {
982 return pushAtomic;
983 }
984
985 /**
986 * Request atomic push (all references succeed, or none do).
987 * <p>
988 * Server must also support atomic push. If the server does not support the
989 * feature the push will abort without making changes.
990 *
991 * @param atomic
992 * true when push should be an all-or-nothing operation.
993 * @see PackTransport
994 * @since 4.2
995 */
996 public void setPushAtomic(final boolean atomic) {
997 this.pushAtomic = atomic;
998 }
999
1000 /**
1001 * @return true if destination refs should be removed if they no longer
1002 * exist at the source repository.
1003 */
1004 public boolean isRemoveDeletedRefs() {
1005 return removeDeletedRefs;
1006 }
1007
1008 /**
1009 * Set whether or not to remove refs which no longer exist in the source.
1010 * <p>
1011 * If true, refs at the destination repository (local for fetch, remote for
1012 * push) are deleted if they no longer exist on the source side (remote for
1013 * fetch, local for push).
1014 * <p>
1015 * False by default, as this may cause data to become unreachable, and
1016 * eventually be deleted on the next GC.
1017 *
1018 * @param remove true to remove refs that no longer exist.
1019 */
1020 public void setRemoveDeletedRefs(final boolean remove) {
1021 removeDeletedRefs = remove;
1022 }
1023
1024 /**
1025 * Apply provided remote configuration on this transport.
1026 *
1027 * @param cfg
1028 * configuration to apply on this transport.
1029 */
1030 public void applyConfig(final RemoteConfig cfg) {
1031 setOptionUploadPack(cfg.getUploadPack());
1032 setOptionReceivePack(cfg.getReceivePack());
1033 setTagOpt(cfg.getTagOpt());
1034 fetch = cfg.getFetchRefSpecs();
1035 push = cfg.getPushRefSpecs();
1036 timeout = cfg.getTimeout();
1037 }
1038
1039 /**
1040 * @return true if push operation should just check for possible result and
1041 * not really update remote refs, false otherwise - when push should
1042 * act normally.
1043 */
1044 public boolean isDryRun() {
1045 return dryRun;
1046 }
1047
1048 /**
1049 * Set dry run option for push operation.
1050 *
1051 * @param dryRun
1052 * true if push operation should just check for possible result
1053 * and not really update remote refs, false otherwise - when push
1054 * should act normally.
1055 */
1056 public void setDryRun(final boolean dryRun) {
1057 this.dryRun = dryRun;
1058 }
1059
1060 /** @return timeout (in seconds) before aborting an IO operation. */
1061 public int getTimeout() {
1062 return timeout;
1063 }
1064
1065 /**
1066 * Set the timeout before willing to abort an IO call.
1067 *
1068 * @param seconds
1069 * number of seconds to wait (with no data transfer occurring)
1070 * before aborting an IO read or write operation with this
1071 * remote.
1072 */
1073 public void setTimeout(final int seconds) {
1074 timeout = seconds;
1075 }
1076
1077 /**
1078 * Get the configuration used by the pack generator to make packs.
1079 *
1080 * If {@link #setPackConfig(PackConfig)} was previously given null a new
1081 * PackConfig is created on demand by this method using the source
1082 * repository's settings.
1083 *
1084 * @return the pack configuration. Never null.
1085 */
1086 public PackConfig getPackConfig() {
1087 if (packConfig == null)
1088 packConfig = new PackConfig(local);
1089 return packConfig;
1090 }
1091
1092 /**
1093 * Set the configuration used by the pack generator.
1094 *
1095 * @param pc
1096 * configuration controlling packing parameters. If null the
1097 * source repository's settings will be used.
1098 */
1099 public void setPackConfig(PackConfig pc) {
1100 packConfig = pc;
1101 }
1102
1103 /**
1104 * A credentials provider to assist with authentication connections..
1105 *
1106 * @param credentialsProvider
1107 * the credentials provider, or null if there is none
1108 */
1109 public void setCredentialsProvider(CredentialsProvider credentialsProvider) {
1110 this.credentialsProvider = credentialsProvider;
1111 }
1112
1113 /**
1114 * The configured credentials provider.
1115 *
1116 * @return the credentials provider, or null if no credentials provider is
1117 * associated with this transport.
1118 */
1119 public CredentialsProvider getCredentialsProvider() {
1120 return credentialsProvider;
1121 }
1122
1123 /**
1124 * Fetch objects and refs from the remote repository to the local one.
1125 * <p>
1126 * This is a utility function providing standard fetch behavior. Local
1127 * tracking refs associated with the remote repository are automatically
1128 * updated if this transport was created from a {@link RemoteConfig} with
1129 * fetch RefSpecs defined.
1130 *
1131 * @param monitor
1132 * progress monitor to inform the user about our processing
1133 * activity. Must not be null. Use {@link NullProgressMonitor} if
1134 * progress updates are not interesting or necessary.
1135 * @param toFetch
1136 * specification of refs to fetch locally. May be null or the
1137 * empty collection to use the specifications from the
1138 * RemoteConfig. Source for each RefSpec can't be null.
1139 * @return information describing the tracking refs updated.
1140 * @throws NotSupportedException
1141 * this transport implementation does not support fetching
1142 * objects.
1143 * @throws TransportException
1144 * the remote connection could not be established or object
1145 * copying (if necessary) failed or update specification was
1146 * incorrect.
1147 */
1148 public FetchResult fetch(final ProgressMonitor monitor,
1149 Collection<RefSpec> toFetch) throws NotSupportedException,
1150 TransportException {
1151 if (toFetch == null || toFetch.isEmpty()) {
1152 // If the caller did not ask for anything use the defaults.
1153 //
1154 if (fetch.isEmpty())
1155 throw new TransportException(JGitText.get().nothingToFetch);
1156 toFetch = fetch;
1157 } else if (!fetch.isEmpty()) {
1158 // If the caller asked for something specific without giving
1159 // us the local tracking branch see if we can update any of
1160 // the local tracking branches without incurring additional
1161 // object transfer overheads.
1162 //
1163 final Collection<RefSpec> tmp = new ArrayList<RefSpec>(toFetch);
1164 for (final RefSpec requested : toFetch) {
1165 final String reqSrc = requested.getSource();
1166 for (final RefSpec configured : fetch) {
1167 final String cfgSrc = configured.getSource();
1168 final String cfgDst = configured.getDestination();
1169 if (cfgSrc.equals(reqSrc) && cfgDst != null) {
1170 tmp.add(configured);
1171 break;
1172 }
1173 }
1174 }
1175 toFetch = tmp;
1176 }
1177
1178 final FetchResult result = new FetchResult();
1179 new FetchProcess(this, toFetch).execute(monitor, result);
1180 return result;
1181 }
1182
1183 /**
1184 * Push objects and refs from the local repository to the remote one.
1185 * <p>
1186 * This is a utility function providing standard push behavior. It updates
1187 * remote refs and send there necessary objects according to remote ref
1188 * update specification. After successful remote ref update, associated
1189 * locally stored tracking branch is updated if set up accordingly. Detailed
1190 * operation result is provided after execution.
1191 * <p>
1192 * For setting up remote ref update specification from ref spec, see helper
1193 * method {@link #findRemoteRefUpdatesFor(Collection)}, predefined refspecs
1194 * ({@link #REFSPEC_TAGS}, {@link #REFSPEC_PUSH_ALL}) or consider using
1195 * directly {@link RemoteRefUpdate} for more possibilities.
1196 * <p>
1197 * When {@link #isDryRun()} is true, result of this operation is just
1198 * estimation of real operation result, no real action is performed.
1199 *
1200 * @see RemoteRefUpdate
1201 *
1202 * @param monitor
1203 * progress monitor to inform the user about our processing
1204 * activity. Must not be null. Use {@link NullProgressMonitor} if
1205 * progress updates are not interesting or necessary.
1206 * @param toPush
1207 * specification of refs to push. May be null or the empty
1208 * collection to use the specifications from the RemoteConfig
1209 * converted by {@link #findRemoteRefUpdatesFor(Collection)}. No
1210 * more than 1 RemoteRefUpdate with the same remoteName is
1211 * allowed. These objects are modified during this call.
1212 * @param out
1213 * output stream to write messages to
1214 * @return information about results of remote refs updates, tracking refs
1215 * updates and refs advertised by remote repository.
1216 * @throws NotSupportedException
1217 * this transport implementation does not support pushing
1218 * objects.
1219 * @throws TransportException
1220 * the remote connection could not be established or object
1221 * copying (if necessary) failed at I/O or protocol level or
1222 * update specification was incorrect.
1223 * @since 3.0
1224 */
1225 public PushResult push(final ProgressMonitor monitor,
1226 Collection<RemoteRefUpdate> toPush, OutputStream out)
1227 throws NotSupportedException,
1228 TransportException {
1229 if (toPush == null || toPush.isEmpty()) {
1230 // If the caller did not ask for anything use the defaults.
1231 try {
1232 toPush = findRemoteRefUpdatesFor(push);
1233 } catch (final IOException e) {
1234 throw new TransportException(MessageFormat.format(
1235 JGitText.get().problemWithResolvingPushRefSpecsLocally, e.getMessage()), e);
1236 }
1237 if (toPush.isEmpty())
1238 throw new TransportException(JGitText.get().nothingToPush);
1239 }
1240 if (prePush != null) {
1241 try {
1242 prePush.setRefs(toPush);
1243 prePush.call();
1244 } catch (AbortedByHookException | IOException e) {
1245 throw new TransportException(e.getMessage(), e);
1246 }
1247 }
1248
1249 final PushProcess pushProcess = new PushProcess(this, toPush, out);
1250 return pushProcess.execute(monitor);
1251 }
1252
1253 /**
1254 * Push objects and refs from the local repository to the remote one.
1255 * <p>
1256 * This is a utility function providing standard push behavior. It updates
1257 * remote refs and sends necessary objects according to remote ref update
1258 * specification. After successful remote ref update, associated locally
1259 * stored tracking branch is updated if set up accordingly. Detailed
1260 * operation result is provided after execution.
1261 * <p>
1262 * For setting up remote ref update specification from ref spec, see helper
1263 * method {@link #findRemoteRefUpdatesFor(Collection)}, predefined refspecs
1264 * ({@link #REFSPEC_TAGS}, {@link #REFSPEC_PUSH_ALL}) or consider using
1265 * directly {@link RemoteRefUpdate} for more possibilities.
1266 * <p>
1267 * When {@link #isDryRun()} is true, result of this operation is just
1268 * estimation of real operation result, no real action is performed.
1269 *
1270 * @see RemoteRefUpdate
1271 *
1272 * @param monitor
1273 * progress monitor to inform the user about our processing
1274 * activity. Must not be null. Use {@link NullProgressMonitor} if
1275 * progress updates are not interesting or necessary.
1276 * @param toPush
1277 * specification of refs to push. May be null or the empty
1278 * collection to use the specifications from the RemoteConfig
1279 * converted by {@link #findRemoteRefUpdatesFor(Collection)}. No
1280 * more than 1 RemoteRefUpdate with the same remoteName is
1281 * allowed. These objects are modified during this call.
1282 *
1283 * @return information about results of remote refs updates, tracking refs
1284 * updates and refs advertised by remote repository.
1285 * @throws NotSupportedException
1286 * this transport implementation does not support pushing
1287 * objects.
1288 * @throws TransportException
1289 * the remote connection could not be established or object
1290 * copying (if necessary) failed at I/O or protocol level or
1291 * update specification was incorrect.
1292 */
1293 public PushResult push(final ProgressMonitor monitor,
1294 Collection<RemoteRefUpdate> toPush) throws NotSupportedException,
1295 TransportException {
1296 return push(monitor, toPush, null);
1297 }
1298
1299 /**
1300 * Convert push remote refs update specification from {@link RefSpec} form
1301 * to {@link RemoteRefUpdate}. Conversion expands wildcards by matching
1302 * source part to local refs. expectedOldObjectId in RemoteRefUpdate is
1303 * always set as null. Tracking branch is configured if RefSpec destination
1304 * matches source of any fetch ref spec for this transport remote
1305 * configuration.
1306 * <p>
1307 * Conversion is performed for context of this transport (database, fetch
1308 * specifications).
1309 *
1310 * @param specs
1311 * collection of RefSpec to convert.
1312 * @return collection of set up {@link RemoteRefUpdate}.
1313 * @throws IOException
1314 * when problem occurred during conversion or specification set
1315 * up: most probably, missing objects or refs.
1316 */
1317 public Collection<RemoteRefUpdate> findRemoteRefUpdatesFor(
1318 final Collection<RefSpec> specs) throws IOException {
1319 return findRemoteRefUpdatesFor(local, specs, fetch);
1320 }
1321
1322 /**
1323 * Begins a new connection for fetching from the remote repository.
1324 * <p>
1325 * If the transport has no local repository, the fetch connection can only
1326 * be used for reading remote refs.
1327 *
1328 * @return a fresh connection to fetch from the remote repository.
1329 * @throws NotSupportedException
1330 * the implementation does not support fetching.
1331 * @throws TransportException
1332 * the remote connection could not be established.
1333 */
1334 public abstract FetchConnection openFetch() throws NotSupportedException,
1335 TransportException;
1336
1337 /**
1338 * Begins a new connection for pushing into the remote repository.
1339 *
1340 * @return a fresh connection to push into the remote repository.
1341 * @throws NotSupportedException
1342 * the implementation does not support pushing.
1343 * @throws TransportException
1344 * the remote connection could not be established
1345 */
1346 public abstract PushConnection openPush() throws NotSupportedException,
1347 TransportException;
1348
1349 /**
1350 * Close any resources used by this transport.
1351 * <p>
1352 * If the remote repository is contacted by a network socket this method
1353 * must close that network socket, disconnecting the two peers. If the
1354 * remote repository is actually local (same system) this method must close
1355 * any open file handles used to read the "remote" repository.
1356 * <p>
1357 * {@code AutoClosable.close()} declares that it throws {@link Exception}.
1358 * Implementers shouldn't throw checked exceptions. This override narrows
1359 * the signature to prevent them from doing so.
1360 */
1361 public abstract void close();
1362 }