RemoteConfig.java

  1. /*
  2.  * Copyright (C) 2009, Google Inc.
  3.  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
  4.  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
  5.  *
  6.  * This program and the accompanying materials are made available under the
  7.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  8.  * https://www.eclipse.org/org/documents/edl-v10.php.
  9.  *
  10.  * SPDX-License-Identifier: BSD-3-Clause
  11.  */

  12. package org.eclipse.jgit.transport;

  13. import java.io.Serializable;
  14. import java.net.URISyntaxException;
  15. import java.util.ArrayList;
  16. import java.util.Collections;
  17. import java.util.HashMap;
  18. import java.util.List;
  19. import java.util.Map;
  20. import java.util.Map.Entry;

  21. import org.eclipse.jgit.lib.Config;

  22. /**
  23.  * A remembered remote repository, including URLs and RefSpecs.
  24.  * <p>
  25.  * A remote configuration remembers one or more URLs for a frequently accessed
  26.  * remote repository as well as zero or more fetch and push specifications
  27.  * describing how refs should be transferred between this repository and the
  28.  * remote repository.
  29.  */
  30. public class RemoteConfig implements Serializable {
  31.     private static final long serialVersionUID = 1L;

  32.     private static final String SECTION = "remote"; //$NON-NLS-1$

  33.     private static final String KEY_URL = "url"; //$NON-NLS-1$

  34.     private static final String KEY_PUSHURL = "pushurl"; //$NON-NLS-1$

  35.     private static final String KEY_FETCH = "fetch"; //$NON-NLS-1$

  36.     private static final String KEY_PUSH = "push"; //$NON-NLS-1$

  37.     private static final String KEY_UPLOADPACK = "uploadpack"; //$NON-NLS-1$

  38.     private static final String KEY_RECEIVEPACK = "receivepack"; //$NON-NLS-1$

  39.     private static final String KEY_TAGOPT = "tagopt"; //$NON-NLS-1$

  40.     private static final String KEY_MIRROR = "mirror"; //$NON-NLS-1$

  41.     private static final String KEY_TIMEOUT = "timeout"; //$NON-NLS-1$

  42.     private static final String KEY_INSTEADOF = "insteadof"; //$NON-NLS-1$

  43.     private static final String KEY_PUSHINSTEADOF = "pushinsteadof"; //$NON-NLS-1$

  44.     private static final boolean DEFAULT_MIRROR = false;

  45.     /** Default value for {@link #getUploadPack()} if not specified. */
  46.     public static final String DEFAULT_UPLOAD_PACK = "git-upload-pack"; //$NON-NLS-1$

  47.     /** Default value for {@link #getReceivePack()} if not specified. */
  48.     public static final String DEFAULT_RECEIVE_PACK = "git-receive-pack"; //$NON-NLS-1$

  49.     /**
  50.      * Parse all remote blocks in an existing configuration file, looking for
  51.      * remotes configuration.
  52.      *
  53.      * @param rc
  54.      *            the existing configuration to get the remote settings from.
  55.      *            The configuration must already be loaded into memory.
  56.      * @return all remotes configurations existing in provided repository
  57.      *         configuration. Returned configurations are ordered
  58.      *         lexicographically by names.
  59.      * @throws java.net.URISyntaxException
  60.      *             one of the URIs within the remote's configuration is invalid.
  61.      */
  62.     public static List<RemoteConfig> getAllRemoteConfigs(Config rc)
  63.             throws URISyntaxException {
  64.         final List<String> names = new ArrayList<>(rc
  65.                 .getSubsections(SECTION));
  66.         Collections.sort(names);

  67.         final List<RemoteConfig> result = new ArrayList<>(names
  68.                 .size());
  69.         for (String name : names)
  70.             result.add(new RemoteConfig(rc, name));
  71.         return result;
  72.     }

  73.     private String name;

  74.     private List<URIish> uris;

  75.     private List<URIish> pushURIs;

  76.     private List<RefSpec> fetch;

  77.     private List<RefSpec> push;

  78.     private String uploadpack;

  79.     private String receivepack;

  80.     private TagOpt tagopt;

  81.     private boolean mirror;

  82.     private int timeout;

  83.     /**
  84.      * Parse a remote block from an existing configuration file.
  85.      * <p>
  86.      * This constructor succeeds even if the requested remote is not defined
  87.      * within the supplied configuration file. If that occurs then there will be
  88.      * no URIs and no ref specifications known to the new instance.
  89.      *
  90.      * @param rc
  91.      *            the existing configuration to get the remote settings from.
  92.      *            The configuration must already be loaded into memory.
  93.      * @param remoteName
  94.      *            subsection key indicating the name of this remote.
  95.      * @throws java.net.URISyntaxException
  96.      *             one of the URIs within the remote's configuration is invalid.
  97.      */
  98.     public RemoteConfig(Config rc, String remoteName)
  99.             throws URISyntaxException {
  100.         name = remoteName;

  101.         String[] vlst;
  102.         String val;

  103.         vlst = rc.getStringList(SECTION, name, KEY_URL);
  104.         Map<String, String> insteadOf = getReplacements(rc, KEY_INSTEADOF);
  105.         uris = new ArrayList<>(vlst.length);
  106.         for (String s : vlst) {
  107.             uris.add(new URIish(replaceUri(s, insteadOf)));
  108.         }
  109.         String[] plst = rc.getStringList(SECTION, name, KEY_PUSHURL);
  110.         pushURIs = new ArrayList<>(plst.length);
  111.         for (String s : plst) {
  112.             pushURIs.add(new URIish(s));
  113.         }
  114.         if (pushURIs.isEmpty()) {
  115.             // Would default to the uris. If we have pushinsteadof, we must
  116.             // supply rewritten push uris.
  117.             Map<String, String> pushInsteadOf = getReplacements(rc,
  118.                     KEY_PUSHINSTEADOF);
  119.             if (!pushInsteadOf.isEmpty()) {
  120.                 for (String s : vlst) {
  121.                     String replaced = replaceUri(s, pushInsteadOf);
  122.                     if (!s.equals(replaced)) {
  123.                         pushURIs.add(new URIish(replaced));
  124.                     }
  125.                 }
  126.             }
  127.         }
  128.         fetch = rc.getRefSpecs(SECTION, name, KEY_FETCH);
  129.         push = rc.getRefSpecs(SECTION, name, KEY_PUSH);
  130.         val = rc.getString(SECTION, name, KEY_UPLOADPACK);
  131.         if (val == null) {
  132.             val = DEFAULT_UPLOAD_PACK;
  133.         }
  134.         uploadpack = val;

  135.         val = rc.getString(SECTION, name, KEY_RECEIVEPACK);
  136.         if (val == null) {
  137.             val = DEFAULT_RECEIVE_PACK;
  138.         }
  139.         receivepack = val;

  140.         try {
  141.             val = rc.getString(SECTION, name, KEY_TAGOPT);
  142.             tagopt = TagOpt.fromOption(val);
  143.         } catch (IllegalArgumentException e) {
  144.             // C git silently ignores invalid tagopt values.
  145.             tagopt = TagOpt.AUTO_FOLLOW;
  146.         }
  147.         mirror = rc.getBoolean(SECTION, name, KEY_MIRROR, DEFAULT_MIRROR);
  148.         timeout = rc.getInt(SECTION, name, KEY_TIMEOUT, 0);
  149.     }

  150.     /**
  151.      * Update this remote's definition within the configuration.
  152.      *
  153.      * @param rc
  154.      *            the configuration file to store ourselves into.
  155.      */
  156.     public void update(Config rc) {
  157.         final List<String> vlst = new ArrayList<>();

  158.         vlst.clear();
  159.         for (URIish u : getURIs())
  160.             vlst.add(u.toPrivateString());
  161.         rc.setStringList(SECTION, getName(), KEY_URL, vlst);

  162.         vlst.clear();
  163.         for (URIish u : getPushURIs())
  164.             vlst.add(u.toPrivateString());
  165.         rc.setStringList(SECTION, getName(), KEY_PUSHURL, vlst);

  166.         vlst.clear();
  167.         for (RefSpec u : getFetchRefSpecs())
  168.             vlst.add(u.toString());
  169.         rc.setStringList(SECTION, getName(), KEY_FETCH, vlst);

  170.         vlst.clear();
  171.         for (RefSpec u : getPushRefSpecs())
  172.             vlst.add(u.toString());
  173.         rc.setStringList(SECTION, getName(), KEY_PUSH, vlst);

  174.         set(rc, KEY_UPLOADPACK, getUploadPack(), DEFAULT_UPLOAD_PACK);
  175.         set(rc, KEY_RECEIVEPACK, getReceivePack(), DEFAULT_RECEIVE_PACK);
  176.         set(rc, KEY_TAGOPT, getTagOpt().option(), TagOpt.AUTO_FOLLOW.option());
  177.         set(rc, KEY_MIRROR, mirror, DEFAULT_MIRROR);
  178.         set(rc, KEY_TIMEOUT, timeout, 0);
  179.     }

  180.     private void set(final Config rc, final String key,
  181.             final String currentValue, final String defaultValue) {
  182.         if (defaultValue.equals(currentValue))
  183.             unset(rc, key);
  184.         else
  185.             rc.setString(SECTION, getName(), key, currentValue);
  186.     }

  187.     private void set(final Config rc, final String key,
  188.             final boolean currentValue, final boolean defaultValue) {
  189.         if (defaultValue == currentValue)
  190.             unset(rc, key);
  191.         else
  192.             rc.setBoolean(SECTION, getName(), key, currentValue);
  193.     }

  194.     private void set(final Config rc, final String key, final int currentValue,
  195.             final int defaultValue) {
  196.         if (defaultValue == currentValue)
  197.             unset(rc, key);
  198.         else
  199.             rc.setInt(SECTION, getName(), key, currentValue);
  200.     }

  201.     private void unset(Config rc, String key) {
  202.         rc.unset(SECTION, getName(), key);
  203.     }

  204.     private Map<String, String> getReplacements(final Config config,
  205.             final String keyName) {
  206.         final Map<String, String> replacements = new HashMap<>();
  207.         for (String url : config.getSubsections(KEY_URL))
  208.             for (String insteadOf : config.getStringList(KEY_URL, url, keyName))
  209.                 replacements.put(insteadOf, url);
  210.         return replacements;
  211.     }

  212.     private String replaceUri(final String uri,
  213.             final Map<String, String> replacements) {
  214.         if (replacements.isEmpty()) {
  215.             return uri;
  216.         }
  217.         Entry<String, String> match = null;
  218.         for (Entry<String, String> replacement : replacements.entrySet()) {
  219.             // Ignore current entry if not longer than previous match
  220.             if (match != null
  221.                     && match.getKey().length() > replacement.getKey()
  222.                             .length()) {
  223.                 continue;
  224.             }
  225.             if (!uri.startsWith(replacement.getKey())) {
  226.                 continue;
  227.             }
  228.             match = replacement;
  229.         }
  230.         if (match != null) {
  231.             return match.getValue() + uri.substring(match.getKey().length());
  232.         }
  233.         return uri;
  234.     }

  235.     /**
  236.      * Get the local name this remote configuration is recognized as.
  237.      *
  238.      * @return name assigned by the user to this configuration block.
  239.      */
  240.     public String getName() {
  241.         return name;
  242.     }

  243.     /**
  244.      * Get all configured URIs under this remote.
  245.      *
  246.      * @return the set of URIs known to this remote.
  247.      */
  248.     public List<URIish> getURIs() {
  249.         return Collections.unmodifiableList(uris);
  250.     }

  251.     /**
  252.      * Add a new URI to the end of the list of URIs.
  253.      *
  254.      * @param toAdd
  255.      *            the new URI to add to this remote.
  256.      * @return true if the URI was added; false if it already exists.
  257.      */
  258.     public boolean addURI(URIish toAdd) {
  259.         if (uris.contains(toAdd))
  260.             return false;
  261.         return uris.add(toAdd);
  262.     }

  263.     /**
  264.      * Remove a URI from the list of URIs.
  265.      *
  266.      * @param toRemove
  267.      *            the URI to remove from this remote.
  268.      * @return true if the URI was added; false if it already exists.
  269.      */
  270.     public boolean removeURI(URIish toRemove) {
  271.         return uris.remove(toRemove);
  272.     }

  273.     /**
  274.      * Get all configured push-only URIs under this remote.
  275.      *
  276.      * @return the set of URIs known to this remote.
  277.      */
  278.     public List<URIish> getPushURIs() {
  279.         return Collections.unmodifiableList(pushURIs);
  280.     }

  281.     /**
  282.      * Add a new push-only URI to the end of the list of URIs.
  283.      *
  284.      * @param toAdd
  285.      *            the new URI to add to this remote.
  286.      * @return true if the URI was added; false if it already exists.
  287.      */
  288.     public boolean addPushURI(URIish toAdd) {
  289.         if (pushURIs.contains(toAdd))
  290.             return false;
  291.         return pushURIs.add(toAdd);
  292.     }

  293.     /**
  294.      * Remove a push-only URI from the list of URIs.
  295.      *
  296.      * @param toRemove
  297.      *            the URI to remove from this remote.
  298.      * @return true if the URI was added; false if it already exists.
  299.      */
  300.     public boolean removePushURI(URIish toRemove) {
  301.         return pushURIs.remove(toRemove);
  302.     }

  303.     /**
  304.      * Remembered specifications for fetching from a repository.
  305.      *
  306.      * @return set of specs used by default when fetching.
  307.      */
  308.     public List<RefSpec> getFetchRefSpecs() {
  309.         return Collections.unmodifiableList(fetch);
  310.     }

  311.     /**
  312.      * Add a new fetch RefSpec to this remote.
  313.      *
  314.      * @param s
  315.      *            the new specification to add.
  316.      * @return true if the specification was added; false if it already exists.
  317.      */
  318.     public boolean addFetchRefSpec(RefSpec s) {
  319.         if (fetch.contains(s))
  320.             return false;
  321.         return fetch.add(s);
  322.     }

  323.     /**
  324.      * Override existing fetch specifications with new ones.
  325.      *
  326.      * @param specs
  327.      *            list of fetch specifications to set. List is copied, it can be
  328.      *            modified after this call.
  329.      */
  330.     public void setFetchRefSpecs(List<RefSpec> specs) {
  331.         fetch.clear();
  332.         fetch.addAll(specs);
  333.     }

  334.     /**
  335.      * Override existing push specifications with new ones.
  336.      *
  337.      * @param specs
  338.      *            list of push specifications to set. List is copied, it can be
  339.      *            modified after this call.
  340.      */
  341.     public void setPushRefSpecs(List<RefSpec> specs) {
  342.         push.clear();
  343.         push.addAll(specs);
  344.     }

  345.     /**
  346.      * Remove a fetch RefSpec from this remote.
  347.      *
  348.      * @param s
  349.      *            the specification to remove.
  350.      * @return true if the specification existed and was removed.
  351.      */
  352.     public boolean removeFetchRefSpec(RefSpec s) {
  353.         return fetch.remove(s);
  354.     }

  355.     /**
  356.      * Remembered specifications for pushing to a repository.
  357.      *
  358.      * @return set of specs used by default when pushing.
  359.      */
  360.     public List<RefSpec> getPushRefSpecs() {
  361.         return Collections.unmodifiableList(push);
  362.     }

  363.     /**
  364.      * Add a new push RefSpec to this remote.
  365.      *
  366.      * @param s
  367.      *            the new specification to add.
  368.      * @return true if the specification was added; false if it already exists.
  369.      */
  370.     public boolean addPushRefSpec(RefSpec s) {
  371.         if (push.contains(s))
  372.             return false;
  373.         return push.add(s);
  374.     }

  375.     /**
  376.      * Remove a push RefSpec from this remote.
  377.      *
  378.      * @param s
  379.      *            the specification to remove.
  380.      * @return true if the specification existed and was removed.
  381.      */
  382.     public boolean removePushRefSpec(RefSpec s) {
  383.         return push.remove(s);
  384.     }

  385.     /**
  386.      * Override for the location of 'git-upload-pack' on the remote system.
  387.      * <p>
  388.      * This value is only useful for an SSH style connection, where Git is
  389.      * asking the remote system to execute a program that provides the necessary
  390.      * network protocol.
  391.      *
  392.      * @return location of 'git-upload-pack' on the remote system. If no
  393.      *         location has been configured the default of 'git-upload-pack' is
  394.      *         returned instead.
  395.      */
  396.     public String getUploadPack() {
  397.         return uploadpack;
  398.     }

  399.     /**
  400.      * Override for the location of 'git-receive-pack' on the remote system.
  401.      * <p>
  402.      * This value is only useful for an SSH style connection, where Git is
  403.      * asking the remote system to execute a program that provides the necessary
  404.      * network protocol.
  405.      *
  406.      * @return location of 'git-receive-pack' on the remote system. If no
  407.      *         location has been configured the default of 'git-receive-pack' is
  408.      *         returned instead.
  409.      */
  410.     public String getReceivePack() {
  411.         return receivepack;
  412.     }

  413.     /**
  414.      * Get the description of how annotated tags should be treated during fetch.
  415.      *
  416.      * @return option indicating the behavior of annotated tags in fetch.
  417.      */
  418.     public TagOpt getTagOpt() {
  419.         return tagopt;
  420.     }

  421.     /**
  422.      * Set the description of how annotated tags should be treated on fetch.
  423.      *
  424.      * @param option
  425.      *            method to use when handling annotated tags.
  426.      */
  427.     public void setTagOpt(TagOpt option) {
  428.         tagopt = option != null ? option : TagOpt.AUTO_FOLLOW;
  429.     }

  430.     /**
  431.      * Whether pushing to the remote automatically deletes remote refs which
  432.      * don't exist on the source side.
  433.      *
  434.      * @return true if pushing to the remote automatically deletes remote refs
  435.      *         which don't exist on the source side.
  436.      */
  437.     public boolean isMirror() {
  438.         return mirror;
  439.     }

  440.     /**
  441.      * Set the mirror flag to automatically delete remote refs.
  442.      *
  443.      * @param m
  444.      *            true to automatically delete remote refs during push.
  445.      */
  446.     public void setMirror(boolean m) {
  447.         mirror = m;
  448.     }

  449.     /**
  450.      * Get timeout (in seconds) before aborting an IO operation.
  451.      *
  452.      * @return timeout (in seconds) before aborting an IO operation.
  453.      */
  454.     public int getTimeout() {
  455.         return timeout;
  456.     }

  457.     /**
  458.      * Set the timeout before willing to abort an IO call.
  459.      *
  460.      * @param seconds
  461.      *            number of seconds to wait (with no data transfer occurring)
  462.      *            before aborting an IO read or write operation with this
  463.      *            remote.  A timeout of 0 will block indefinitely.
  464.      */
  465.     public void setTimeout(int seconds) {
  466.         timeout = seconds;
  467.     }
  468. }