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.findRef(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 /** The option strings associated with the push operation. */
777 private List<String> pushOptions;
778
779 private PrintStream hookOutRedirect;
780
781 private PrePushHook prePush;
782 /**
783 * Create a new transport instance.
784 *
785 * @param local
786 * the repository this instance will fetch into, or push out of.
787 * This must be the repository passed to
788 * {@link #open(Repository, URIish)}.
789 * @param uri
790 * the URI used to access the remote repository. This must be the
791 * URI passed to {@link #open(Repository, URIish)}.
792 */
793 protected Transport(final Repository local, final URIish uri) {
794 final TransferConfig tc = local.getConfig().get(TransferConfig.KEY);
795 this.local = local;
796 this.uri = uri;
797 this.objectChecker = tc.newObjectChecker();
798 this.credentialsProvider = CredentialsProvider.getDefault();
799 prePush = Hooks.prePush(local, hookOutRedirect);
800 }
801
802 /**
803 * Create a minimal transport instance not tied to a single repository.
804 *
805 * @param uri
806 */
807 protected Transport(final URIish uri) {
808 this.uri = uri;
809 this.local = null;
810 this.objectChecker = new ObjectChecker();
811 this.credentialsProvider = CredentialsProvider.getDefault();
812 }
813
814 /**
815 * Get the URI this transport connects to.
816 * <p>
817 * Each transport instance connects to at most one URI at any point in time.
818 *
819 * @return the URI describing the location of the remote repository.
820 */
821 public URIish getURI() {
822 return uri;
823 }
824
825 /**
826 * Get the name of the remote executable providing upload-pack service.
827 *
828 * @return typically "git-upload-pack".
829 */
830 public String getOptionUploadPack() {
831 return optionUploadPack;
832 }
833
834 /**
835 * Set the name of the remote executable providing upload-pack services.
836 *
837 * @param where
838 * name of the executable.
839 */
840 public void setOptionUploadPack(final String where) {
841 if (where != null && where.length() > 0)
842 optionUploadPack = where;
843 else
844 optionUploadPack = RemoteConfig.DEFAULT_UPLOAD_PACK;
845 }
846
847 /**
848 * Get the description of how annotated tags should be treated during fetch.
849 *
850 * @return option indicating the behavior of annotated tags in fetch.
851 */
852 public TagOpt getTagOpt() {
853 return tagopt;
854 }
855
856 /**
857 * Set the description of how annotated tags should be treated on fetch.
858 *
859 * @param option
860 * method to use when handling annotated tags.
861 */
862 public void setTagOpt(final TagOpt option) {
863 tagopt = option != null ? option : TagOpt.AUTO_FOLLOW;
864 }
865
866 /**
867 * Default setting is: {@link #DEFAULT_FETCH_THIN}
868 *
869 * @return true if fetch should request thin-pack when possible; false
870 * otherwise
871 * @see PackTransport
872 */
873 public boolean isFetchThin() {
874 return fetchThin;
875 }
876
877 /**
878 * Set the thin-pack preference for fetch operation. Default setting is:
879 * {@link #DEFAULT_FETCH_THIN}
880 *
881 * @param fetchThin
882 * true when fetch should request thin-pack when possible; false
883 * when it shouldn't
884 * @see PackTransport
885 */
886 public void setFetchThin(final boolean fetchThin) {
887 this.fetchThin = fetchThin;
888 }
889
890 /**
891 * @return true if fetch will verify received objects are formatted
892 * correctly. Validating objects requires more CPU time on the
893 * client side of the connection.
894 */
895 public boolean isCheckFetchedObjects() {
896 return getObjectChecker() != null;
897 }
898
899 /**
900 * @param check
901 * true to enable checking received objects; false to assume all
902 * received objects are valid.
903 * @see #setObjectChecker(ObjectChecker)
904 */
905 public void setCheckFetchedObjects(final boolean check) {
906 if (check && objectChecker == null)
907 setObjectChecker(new ObjectChecker());
908 else if (!check && objectChecker != null)
909 setObjectChecker(null);
910 }
911
912 /**
913 * @return configured object checker for received objects, or null.
914 * @since 3.6
915 */
916 public ObjectChecker getObjectChecker() {
917 return objectChecker;
918 }
919
920 /**
921 * @param impl
922 * if non-null the object checking instance to verify each
923 * received object with; null to disable object checking.
924 * @since 3.6
925 */
926 public void setObjectChecker(ObjectChecker impl) {
927 objectChecker = impl;
928 }
929
930 /**
931 * Default setting is: {@link RemoteConfig#DEFAULT_RECEIVE_PACK}
932 *
933 * @return remote executable providing receive-pack service for pack
934 * transports.
935 * @see PackTransport
936 */
937 public String getOptionReceivePack() {
938 return optionReceivePack;
939 }
940
941 /**
942 * Set remote executable providing receive-pack service for pack transports.
943 * Default setting is: {@link RemoteConfig#DEFAULT_RECEIVE_PACK}
944 *
945 * @param optionReceivePack
946 * remote executable, if null or empty default one is set;
947 */
948 public void setOptionReceivePack(String optionReceivePack) {
949 if (optionReceivePack != null && optionReceivePack.length() > 0)
950 this.optionReceivePack = optionReceivePack;
951 else
952 this.optionReceivePack = RemoteConfig.DEFAULT_RECEIVE_PACK;
953 }
954
955 /**
956 * Default setting is: {@value #DEFAULT_PUSH_THIN}
957 *
958 * @return true if push should produce thin-pack in pack transports
959 * @see PackTransport
960 */
961 public boolean isPushThin() {
962 return pushThin;
963 }
964
965 /**
966 * Set thin-pack preference for push operation. Default setting is:
967 * {@value #DEFAULT_PUSH_THIN}
968 *
969 * @param pushThin
970 * true when push should produce thin-pack in pack transports;
971 * false when it shouldn't
972 * @see PackTransport
973 */
974 public void setPushThin(final boolean pushThin) {
975 this.pushThin = pushThin;
976 }
977
978 /**
979 * Default setting is false.
980 *
981 * @return true if push requires all-or-nothing atomic behavior.
982 * @since 4.2
983 */
984 public boolean isPushAtomic() {
985 return pushAtomic;
986 }
987
988 /**
989 * Request atomic push (all references succeed, or none do).
990 * <p>
991 * Server must also support atomic push. If the server does not support the
992 * feature the push will abort without making changes.
993 *
994 * @param atomic
995 * true when push should be an all-or-nothing operation.
996 * @see PackTransport
997 * @since 4.2
998 */
999 public void setPushAtomic(final boolean atomic) {
1000 this.pushAtomic = atomic;
1001 }
1002
1003 /**
1004 * @return true if destination refs should be removed if they no longer
1005 * exist at the source repository.
1006 */
1007 public boolean isRemoveDeletedRefs() {
1008 return removeDeletedRefs;
1009 }
1010
1011 /**
1012 * Set whether or not to remove refs which no longer exist in the source.
1013 * <p>
1014 * If true, refs at the destination repository (local for fetch, remote for
1015 * push) are deleted if they no longer exist on the source side (remote for
1016 * fetch, local for push).
1017 * <p>
1018 * False by default, as this may cause data to become unreachable, and
1019 * eventually be deleted on the next GC.
1020 *
1021 * @param remove true to remove refs that no longer exist.
1022 */
1023 public void setRemoveDeletedRefs(final boolean remove) {
1024 removeDeletedRefs = remove;
1025 }
1026
1027 /**
1028 * Apply provided remote configuration on this transport.
1029 *
1030 * @param cfg
1031 * configuration to apply on this transport.
1032 */
1033 public void applyConfig(final RemoteConfig cfg) {
1034 setOptionUploadPack(cfg.getUploadPack());
1035 setOptionReceivePack(cfg.getReceivePack());
1036 setTagOpt(cfg.getTagOpt());
1037 fetch = cfg.getFetchRefSpecs();
1038 push = cfg.getPushRefSpecs();
1039 timeout = cfg.getTimeout();
1040 }
1041
1042 /**
1043 * @return true if push operation should just check for possible result and
1044 * not really update remote refs, false otherwise - when push should
1045 * act normally.
1046 */
1047 public boolean isDryRun() {
1048 return dryRun;
1049 }
1050
1051 /**
1052 * Set dry run option for push operation.
1053 *
1054 * @param dryRun
1055 * true if push operation should just check for possible result
1056 * and not really update remote refs, false otherwise - when push
1057 * should act normally.
1058 */
1059 public void setDryRun(final boolean dryRun) {
1060 this.dryRun = dryRun;
1061 }
1062
1063 /** @return timeout (in seconds) before aborting an IO operation. */
1064 public int getTimeout() {
1065 return timeout;
1066 }
1067
1068 /**
1069 * Set the timeout before willing to abort an IO call.
1070 *
1071 * @param seconds
1072 * number of seconds to wait (with no data transfer occurring)
1073 * before aborting an IO read or write operation with this
1074 * remote.
1075 */
1076 public void setTimeout(final int seconds) {
1077 timeout = seconds;
1078 }
1079
1080 /**
1081 * Get the configuration used by the pack generator to make packs.
1082 *
1083 * If {@link #setPackConfig(PackConfig)} was previously given null a new
1084 * PackConfig is created on demand by this method using the source
1085 * repository's settings.
1086 *
1087 * @return the pack configuration. Never null.
1088 */
1089 public PackConfig getPackConfig() {
1090 if (packConfig == null)
1091 packConfig = new PackConfig(local);
1092 return packConfig;
1093 }
1094
1095 /**
1096 * Set the configuration used by the pack generator.
1097 *
1098 * @param pc
1099 * configuration controlling packing parameters. If null the
1100 * source repository's settings will be used.
1101 */
1102 public void setPackConfig(PackConfig pc) {
1103 packConfig = pc;
1104 }
1105
1106 /**
1107 * A credentials provider to assist with authentication connections..
1108 *
1109 * @param credentialsProvider
1110 * the credentials provider, or null if there is none
1111 */
1112 public void setCredentialsProvider(CredentialsProvider credentialsProvider) {
1113 this.credentialsProvider = credentialsProvider;
1114 }
1115
1116 /**
1117 * The configured credentials provider.
1118 *
1119 * @return the credentials provider, or null if no credentials provider is
1120 * associated with this transport.
1121 */
1122 public CredentialsProvider getCredentialsProvider() {
1123 return credentialsProvider;
1124 }
1125
1126 /**
1127 * @return the option strings associated with the push operation
1128 * @since 4.5
1129 */
1130 public List<String> getPushOptions() {
1131 return pushOptions;
1132 }
1133
1134 /**
1135 * Sets the option strings associated with the push operation.
1136 *
1137 * @param pushOptions
1138 * null if push options are unsupported
1139 * @since 4.5
1140 */
1141 public void setPushOptions(final List<String> pushOptions) {
1142 this.pushOptions = pushOptions;
1143 }
1144
1145 /**
1146 * Fetch objects and refs from the remote repository to the local one.
1147 * <p>
1148 * This is a utility function providing standard fetch behavior. Local
1149 * tracking refs associated with the remote repository are automatically
1150 * updated if this transport was created from a {@link RemoteConfig} with
1151 * fetch RefSpecs defined.
1152 *
1153 * @param monitor
1154 * progress monitor to inform the user about our processing
1155 * activity. Must not be null. Use {@link NullProgressMonitor} if
1156 * progress updates are not interesting or necessary.
1157 * @param toFetch
1158 * specification of refs to fetch locally. May be null or the
1159 * empty collection to use the specifications from the
1160 * RemoteConfig. Source for each RefSpec can't be null.
1161 * @return information describing the tracking refs updated.
1162 * @throws NotSupportedException
1163 * this transport implementation does not support fetching
1164 * objects.
1165 * @throws TransportException
1166 * the remote connection could not be established or object
1167 * copying (if necessary) failed or update specification was
1168 * incorrect.
1169 */
1170 public FetchResult fetch(final ProgressMonitor monitor,
1171 Collection<RefSpec> toFetch) throws NotSupportedException,
1172 TransportException {
1173 if (toFetch == null || toFetch.isEmpty()) {
1174 // If the caller did not ask for anything use the defaults.
1175 //
1176 if (fetch.isEmpty())
1177 throw new TransportException(JGitText.get().nothingToFetch);
1178 toFetch = fetch;
1179 } else if (!fetch.isEmpty()) {
1180 // If the caller asked for something specific without giving
1181 // us the local tracking branch see if we can update any of
1182 // the local tracking branches without incurring additional
1183 // object transfer overheads.
1184 //
1185 final Collection<RefSpec> tmp = new ArrayList<RefSpec>(toFetch);
1186 for (final RefSpec requested : toFetch) {
1187 final String reqSrc = requested.getSource();
1188 for (final RefSpec configured : fetch) {
1189 final String cfgSrc = configured.getSource();
1190 final String cfgDst = configured.getDestination();
1191 if (cfgSrc.equals(reqSrc) && cfgDst != null) {
1192 tmp.add(configured);
1193 break;
1194 }
1195 }
1196 }
1197 toFetch = tmp;
1198 }
1199
1200 final FetchResult result = new FetchResult();
1201 new FetchProcess(this, toFetch).execute(monitor, result);
1202 return result;
1203 }
1204
1205 /**
1206 * Push objects and refs from the local repository to the remote one.
1207 * <p>
1208 * This is a utility function providing standard push behavior. It updates
1209 * remote refs and send there necessary objects according to remote ref
1210 * update specification. After successful remote ref update, associated
1211 * locally stored tracking branch is updated if set up accordingly. Detailed
1212 * operation result is provided after execution.
1213 * <p>
1214 * For setting up remote ref update specification from ref spec, see helper
1215 * method {@link #findRemoteRefUpdatesFor(Collection)}, predefined refspecs
1216 * ({@link #REFSPEC_TAGS}, {@link #REFSPEC_PUSH_ALL}) or consider using
1217 * directly {@link RemoteRefUpdate} for more possibilities.
1218 * <p>
1219 * When {@link #isDryRun()} is true, result of this operation is just
1220 * estimation of real operation result, no real action is performed.
1221 *
1222 * @see RemoteRefUpdate
1223 *
1224 * @param monitor
1225 * progress monitor to inform the user about our processing
1226 * activity. Must not be null. Use {@link NullProgressMonitor} if
1227 * progress updates are not interesting or necessary.
1228 * @param toPush
1229 * specification of refs to push. May be null or the empty
1230 * collection to use the specifications from the RemoteConfig
1231 * converted by {@link #findRemoteRefUpdatesFor(Collection)}. No
1232 * more than 1 RemoteRefUpdate with the same remoteName is
1233 * allowed. These objects are modified during this call.
1234 * @param out
1235 * output stream to write messages to
1236 * @return information about results of remote refs updates, tracking refs
1237 * updates and refs advertised by remote repository.
1238 * @throws NotSupportedException
1239 * this transport implementation does not support pushing
1240 * objects.
1241 * @throws TransportException
1242 * the remote connection could not be established or object
1243 * copying (if necessary) failed at I/O or protocol level or
1244 * update specification was incorrect.
1245 * @since 3.0
1246 */
1247 public PushResult push(final ProgressMonitor monitor,
1248 Collection<RemoteRefUpdate> toPush, OutputStream out)
1249 throws NotSupportedException,
1250 TransportException {
1251 if (toPush == null || toPush.isEmpty()) {
1252 // If the caller did not ask for anything use the defaults.
1253 try {
1254 toPush = findRemoteRefUpdatesFor(push);
1255 } catch (final IOException e) {
1256 throw new TransportException(MessageFormat.format(
1257 JGitText.get().problemWithResolvingPushRefSpecsLocally, e.getMessage()), e);
1258 }
1259 if (toPush.isEmpty())
1260 throw new TransportException(JGitText.get().nothingToPush);
1261 }
1262 if (prePush != null) {
1263 try {
1264 prePush.setRefs(toPush);
1265 prePush.call();
1266 } catch (AbortedByHookException | IOException e) {
1267 throw new TransportException(e.getMessage(), e);
1268 }
1269 }
1270
1271 final PushProcess pushProcess = new PushProcess(this, toPush, out);
1272 return pushProcess.execute(monitor);
1273 }
1274
1275 /**
1276 * Push objects and refs from the local repository to the remote one.
1277 * <p>
1278 * This is a utility function providing standard push behavior. It updates
1279 * remote refs and sends necessary objects according to remote ref update
1280 * specification. After successful remote ref update, associated locally
1281 * stored tracking branch is updated if set up accordingly. Detailed
1282 * operation result is provided after execution.
1283 * <p>
1284 * For setting up remote ref update specification from ref spec, see helper
1285 * method {@link #findRemoteRefUpdatesFor(Collection)}, predefined refspecs
1286 * ({@link #REFSPEC_TAGS}, {@link #REFSPEC_PUSH_ALL}) or consider using
1287 * directly {@link RemoteRefUpdate} for more possibilities.
1288 * <p>
1289 * When {@link #isDryRun()} is true, result of this operation is just
1290 * estimation of real operation result, no real action is performed.
1291 *
1292 * @see RemoteRefUpdate
1293 *
1294 * @param monitor
1295 * progress monitor to inform the user about our processing
1296 * activity. Must not be null. Use {@link NullProgressMonitor} if
1297 * progress updates are not interesting or necessary.
1298 * @param toPush
1299 * specification of refs to push. May be null or the empty
1300 * collection to use the specifications from the RemoteConfig
1301 * converted by {@link #findRemoteRefUpdatesFor(Collection)}. No
1302 * more than 1 RemoteRefUpdate with the same remoteName is
1303 * allowed. These objects are modified during this call.
1304 *
1305 * @return information about results of remote refs updates, tracking refs
1306 * updates and refs advertised by remote repository.
1307 * @throws NotSupportedException
1308 * this transport implementation does not support pushing
1309 * objects.
1310 * @throws TransportException
1311 * the remote connection could not be established or object
1312 * copying (if necessary) failed at I/O or protocol level or
1313 * update specification was incorrect.
1314 */
1315 public PushResult push(final ProgressMonitor monitor,
1316 Collection<RemoteRefUpdate> toPush) throws NotSupportedException,
1317 TransportException {
1318 return push(monitor, toPush, null);
1319 }
1320
1321 /**
1322 * Convert push remote refs update specification from {@link RefSpec} form
1323 * to {@link RemoteRefUpdate}. Conversion expands wildcards by matching
1324 * source part to local refs. expectedOldObjectId in RemoteRefUpdate is
1325 * always set as null. Tracking branch is configured if RefSpec destination
1326 * matches source of any fetch ref spec for this transport remote
1327 * configuration.
1328 * <p>
1329 * Conversion is performed for context of this transport (database, fetch
1330 * specifications).
1331 *
1332 * @param specs
1333 * collection of RefSpec to convert.
1334 * @return collection of set up {@link RemoteRefUpdate}.
1335 * @throws IOException
1336 * when problem occurred during conversion or specification set
1337 * up: most probably, missing objects or refs.
1338 */
1339 public Collection<RemoteRefUpdate> findRemoteRefUpdatesFor(
1340 final Collection<RefSpec> specs) throws IOException {
1341 return findRemoteRefUpdatesFor(local, specs, fetch);
1342 }
1343
1344 /**
1345 * Begins a new connection for fetching from the remote repository.
1346 * <p>
1347 * If the transport has no local repository, the fetch connection can only
1348 * be used for reading remote refs.
1349 *
1350 * @return a fresh connection to fetch from the remote repository.
1351 * @throws NotSupportedException
1352 * the implementation does not support fetching.
1353 * @throws TransportException
1354 * the remote connection could not be established.
1355 */
1356 public abstract FetchConnection openFetch() throws NotSupportedException,
1357 TransportException;
1358
1359 /**
1360 * Begins a new connection for pushing into the remote repository.
1361 *
1362 * @return a fresh connection to push into the remote repository.
1363 * @throws NotSupportedException
1364 * the implementation does not support pushing.
1365 * @throws TransportException
1366 * the remote connection could not be established
1367 */
1368 public abstract PushConnection openPush() throws NotSupportedException,
1369 TransportException;
1370
1371 /**
1372 * Close any resources used by this transport.
1373 * <p>
1374 * If the remote repository is contacted by a network socket this method
1375 * must close that network socket, disconnecting the two peers. If the
1376 * remote repository is actually local (same system) this method must close
1377 * any open file handles used to read the "remote" repository.
1378 * <p>
1379 * {@code AutoClosable.close()} declares that it throws {@link Exception}.
1380 * Implementers shouldn't throw checked exceptions. This override narrows
1381 * the signature to prevent them from doing so.
1382 */
1383 public abstract void close();
1384 }