/* * Copyright (C) 2009, Google Inc. * Copyright (C) 2008, Robin Rosenberg * Copyright (C) 2008, Shawn O. Pearce and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at * https://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: BSD-3-Clause */ package org.eclipse.jgit.transport; import java.io.Serializable; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.eclipse.jgit.lib.Config; /** * A remembered remote repository, including URLs and RefSpecs. *

* A remote configuration remembers one or more URLs for a frequently accessed * remote repository as well as zero or more fetch and push specifications * describing how refs should be transferred between this repository and the * remote repository. */ public class RemoteConfig implements Serializable { private static final long serialVersionUID = 1L; private static final String SECTION = "remote"; //$NON-NLS-1$ private static final String KEY_URL = "url"; //$NON-NLS-1$ private static final String KEY_PUSHURL = "pushurl"; //$NON-NLS-1$ private static final String KEY_FETCH = "fetch"; //$NON-NLS-1$ private static final String KEY_PUSH = "push"; //$NON-NLS-1$ private static final String KEY_UPLOADPACK = "uploadpack"; //$NON-NLS-1$ private static final String KEY_RECEIVEPACK = "receivepack"; //$NON-NLS-1$ private static final String KEY_TAGOPT = "tagopt"; //$NON-NLS-1$ private static final String KEY_MIRROR = "mirror"; //$NON-NLS-1$ private static final String KEY_TIMEOUT = "timeout"; //$NON-NLS-1$ private static final boolean DEFAULT_MIRROR = false; /** Default value for {@link #getUploadPack()} if not specified. */ public static final String DEFAULT_UPLOAD_PACK = "git-upload-pack"; //$NON-NLS-1$ /** Default value for {@link #getReceivePack()} if not specified. */ public static final String DEFAULT_RECEIVE_PACK = "git-receive-pack"; //$NON-NLS-1$ /** * Parse all remote blocks in an existing configuration file, looking for * remotes configuration. * * @param rc * the existing configuration to get the remote settings from. * The configuration must already be loaded into memory. * @return all remotes configurations existing in provided repository * configuration. Returned configurations are ordered * lexicographically by names. * @throws java.net.URISyntaxException * one of the URIs within the remote's configuration is invalid. */ public static List getAllRemoteConfigs(Config rc) throws URISyntaxException { final List names = new ArrayList<>(rc .getSubsections(SECTION)); Collections.sort(names); final List result = new ArrayList<>(names .size()); for (String name : names) result.add(new RemoteConfig(rc, name)); return result; } private String name; private List uris; private List pushURIs; private List fetch; private List push; private String uploadpack; private String receivepack; private TagOpt tagopt; private boolean mirror; private int timeout; /** * Parse a remote block from an existing configuration file. *

* This constructor succeeds even if the requested remote is not defined * within the supplied configuration file. If that occurs then there will be * no URIs and no ref specifications known to the new instance. * * @param rc * the existing configuration to get the remote settings from. * The configuration must already be loaded into memory. * @param remoteName * subsection key indicating the name of this remote. * @throws java.net.URISyntaxException * one of the URIs within the remote's configuration is invalid. */ public RemoteConfig(Config rc, String remoteName) throws URISyntaxException { name = remoteName; String[] vlst; String val; vlst = rc.getStringList(SECTION, name, KEY_URL); UrlConfig urls = new UrlConfig(rc); uris = new ArrayList<>(vlst.length); for (String s : vlst) { uris.add(new URIish(urls.replace(s))); } String[] plst = rc.getStringList(SECTION, name, KEY_PUSHURL); pushURIs = new ArrayList<>(plst.length); for (String s : plst) { pushURIs.add(new URIish(s)); } if (pushURIs.isEmpty()) { // Would default to the uris. If we have pushinsteadof, we must // supply rewritten push uris. if (urls.hasPushReplacements()) { for (String s : vlst) { String replaced = urls.replacePush(s); if (!s.equals(replaced)) { pushURIs.add(new URIish(replaced)); } } } } fetch = rc.getRefSpecs(SECTION, name, KEY_FETCH); push = rc.getRefSpecs(SECTION, name, KEY_PUSH); val = rc.getString(SECTION, name, KEY_UPLOADPACK); if (val == null) { val = DEFAULT_UPLOAD_PACK; } uploadpack = val; val = rc.getString(SECTION, name, KEY_RECEIVEPACK); if (val == null) { val = DEFAULT_RECEIVE_PACK; } receivepack = val; try { val = rc.getString(SECTION, name, KEY_TAGOPT); tagopt = TagOpt.fromOption(val); } catch (IllegalArgumentException e) { // C git silently ignores invalid tagopt values. tagopt = TagOpt.AUTO_FOLLOW; } mirror = rc.getBoolean(SECTION, name, KEY_MIRROR, DEFAULT_MIRROR); timeout = rc.getInt(SECTION, name, KEY_TIMEOUT, 0); } /** * Update this remote's definition within the configuration. * * @param rc * the configuration file to store ourselves into. */ public void update(Config rc) { final List vlst = new ArrayList<>(); vlst.clear(); for (URIish u : getURIs()) vlst.add(u.toPrivateString()); rc.setStringList(SECTION, getName(), KEY_URL, vlst); vlst.clear(); for (URIish u : getPushURIs()) vlst.add(u.toPrivateString()); rc.setStringList(SECTION, getName(), KEY_PUSHURL, vlst); vlst.clear(); for (RefSpec u : getFetchRefSpecs()) vlst.add(u.toString()); rc.setStringList(SECTION, getName(), KEY_FETCH, vlst); vlst.clear(); for (RefSpec u : getPushRefSpecs()) vlst.add(u.toString()); rc.setStringList(SECTION, getName(), KEY_PUSH, vlst); set(rc, KEY_UPLOADPACK, getUploadPack(), DEFAULT_UPLOAD_PACK); set(rc, KEY_RECEIVEPACK, getReceivePack(), DEFAULT_RECEIVE_PACK); set(rc, KEY_TAGOPT, getTagOpt().option(), TagOpt.AUTO_FOLLOW.option()); set(rc, KEY_MIRROR, mirror, DEFAULT_MIRROR); set(rc, KEY_TIMEOUT, timeout, 0); } private void set(final Config rc, final String key, final String currentValue, final String defaultValue) { if (defaultValue.equals(currentValue)) unset(rc, key); else rc.setString(SECTION, getName(), key, currentValue); } private void set(final Config rc, final String key, final boolean currentValue, final boolean defaultValue) { if (defaultValue == currentValue) unset(rc, key); else rc.setBoolean(SECTION, getName(), key, currentValue); } private void set(final Config rc, final String key, final int currentValue, final int defaultValue) { if (defaultValue == currentValue) unset(rc, key); else rc.setInt(SECTION, getName(), key, currentValue); } private void unset(Config rc, String key) { rc.unset(SECTION, getName(), key); } /** * Get the local name this remote configuration is recognized as. * * @return name assigned by the user to this configuration block. */ public String getName() { return name; } /** * Get all configured URIs under this remote. * * @return the set of URIs known to this remote. */ public List getURIs() { return Collections.unmodifiableList(uris); } /** * Add a new URI to the end of the list of URIs. * * @param toAdd * the new URI to add to this remote. * @return true if the URI was added; false if it already exists. */ public boolean addURI(URIish toAdd) { if (uris.contains(toAdd)) return false; return uris.add(toAdd); } /** * Remove a URI from the list of URIs. * * @param toRemove * the URI to remove from this remote. * @return true if the URI was added; false if it already exists. */ public boolean removeURI(URIish toRemove) { return uris.remove(toRemove); } /** * Get all configured push-only URIs under this remote. * * @return the set of URIs known to this remote. */ public List getPushURIs() { return Collections.unmodifiableList(pushURIs); } /** * Add a new push-only URI to the end of the list of URIs. * * @param toAdd * the new URI to add to this remote. * @return true if the URI was added; false if it already exists. */ public boolean addPushURI(URIish toAdd) { if (pushURIs.contains(toAdd)) return false; return pushURIs.add(toAdd); } /** * Remove a push-only URI from the list of URIs. * * @param toRemove * the URI to remove from this remote. * @return true if the URI was added; false if it already exists. */ public boolean removePushURI(URIish toRemove) { return pushURIs.remove(toRemove); } /** * Remembered specifications for fetching from a repository. * * @return set of specs used by default when fetching. */ public List getFetchRefSpecs() { return Collections.unmodifiableList(fetch); } /** * Add a new fetch RefSpec to this remote. * * @param s * the new specification to add. * @return true if the specification was added; false if it already exists. */ public boolean addFetchRefSpec(RefSpec s) { if (fetch.contains(s)) return false; return fetch.add(s); } /** * Override existing fetch specifications with new ones. * * @param specs * list of fetch specifications to set. List is copied, it can be * modified after this call. */ public void setFetchRefSpecs(List specs) { fetch.clear(); fetch.addAll(specs); } /** * Override existing push specifications with new ones. * * @param specs * list of push specifications to set. List is copied, it can be * modified after this call. */ public void setPushRefSpecs(List specs) { push.clear(); push.addAll(specs); } /** * Remove a fetch RefSpec from this remote. * * @param s * the specification to remove. * @return true if the specification existed and was removed. */ public boolean removeFetchRefSpec(RefSpec s) { return fetch.remove(s); } /** * Remembered specifications for pushing to a repository. * * @return set of specs used by default when pushing. */ public List getPushRefSpecs() { return Collections.unmodifiableList(push); } /** * Add a new push RefSpec to this remote. * * @param s * the new specification to add. * @return true if the specification was added; false if it already exists. */ public boolean addPushRefSpec(RefSpec s) { if (push.contains(s)) return false; return push.add(s); } /** * Remove a push RefSpec from this remote. * * @param s * the specification to remove. * @return true if the specification existed and was removed. */ public boolean removePushRefSpec(RefSpec s) { return push.remove(s); } /** * Override for the location of 'git-upload-pack' on the remote system. *

* This value is only useful for an SSH style connection, where Git is * asking the remote system to execute a program that provides the necessary * network protocol. * * @return location of 'git-upload-pack' on the remote system. If no * location has been configured the default of 'git-upload-pack' is * returned instead. */ public String getUploadPack() { return uploadpack; } /** * Override for the location of 'git-receive-pack' on the remote system. *

* This value is only useful for an SSH style connection, where Git is * asking the remote system to execute a program that provides the necessary * network protocol. * * @return location of 'git-receive-pack' on the remote system. If no * location has been configured the default of 'git-receive-pack' is * returned instead. */ public String getReceivePack() { return receivepack; } /** * Get the description of how annotated tags should be treated during fetch. * * @return option indicating the behavior of annotated tags in fetch. */ public TagOpt getTagOpt() { return tagopt; } /** * Set the description of how annotated tags should be treated on fetch. * * @param option * method to use when handling annotated tags. */ public void setTagOpt(TagOpt option) { tagopt = option != null ? option : TagOpt.AUTO_FOLLOW; } /** * Whether pushing to the remote automatically deletes remote refs which * don't exist on the source side. * * @return true if pushing to the remote automatically deletes remote refs * which don't exist on the source side. */ public boolean isMirror() { return mirror; } /** * Set the mirror flag to automatically delete remote refs. * * @param m * true to automatically delete remote refs during push. */ public void setMirror(boolean m) { mirror = m; } /** * Get timeout (in seconds) before aborting an IO operation. * * @return timeout (in seconds) before aborting an IO operation. */ public int getTimeout() { return timeout; } /** * Set the timeout before willing to abort an IO call. * * @param seconds * number of seconds to wait (with no data transfer occurring) * before aborting an IO read or write operation with this * remote. A timeout of 0 will block indefinitely. */ public void setTimeout(int seconds) { timeout = seconds; } }