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