org.eclipse.jgit.transport.RemoteConfig.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jgit.transport.RemoteConfig.java

Source

/*
 * Copyright (C) 2009, Google Inc.
 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
 * and other copyright owners as documented in the project's IP log.
 *
 * This program and the accompanying materials are made available
 * under the terms of the Eclipse Distribution License v1.0 which
 * accompanies this distribution, is reproduced below, and is
 * available at http://www.eclipse.org/org/documents/edl-v10.php
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above
 *   copyright notice, this list of conditions and the following
 *   disclaimer in the documentation and/or other materials provided
 *   with the distribution.
 *
 * - Neither the name of the Eclipse Foundation, Inc. nor the
 *   names of its contributors may be used to endorse or promote
 *   products derived from this software without specific prior
 *   written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.eclipse.jgit.transport;

import java.io.Serializable;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.eclipse.jgit.lib.Config;

/**
 * A remembered remote repository, including URLs and RefSpecs.
 * <p>
 * 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 String KEY_INSTEADOF = "insteadof"; //$NON-NLS-1$

    private static final String KEY_PUSHINSTEADOF = "pushinsteadof"; //$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<RemoteConfig> getAllRemoteConfigs(Config rc) throws URISyntaxException {
        final List<String> names = new ArrayList<>(rc.getSubsections(SECTION));
        Collections.sort(names);

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

    private String name;

    private List<URIish> uris;

    private List<URIish> pushURIs;

    private List<RefSpec> fetch;

    private List<RefSpec> 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.
     * <p>
     * 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);
        Map<String, String> insteadOf = getReplacements(rc, KEY_INSTEADOF);
        uris = new ArrayList<>(vlst.length);
        for (String s : vlst) {
            uris.add(new URIish(replaceUri(s, insteadOf)));
        }
        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.
            Map<String, String> pushInsteadOf = getReplacements(rc, KEY_PUSHINSTEADOF);
            if (!pushInsteadOf.isEmpty()) {
                for (String s : vlst) {
                    String replaced = replaceUri(s, pushInsteadOf);
                    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<String> 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);
    }

    private Map<String, String> getReplacements(final Config config, final String keyName) {
        final Map<String, String> replacements = new HashMap<>();
        for (String url : config.getSubsections(KEY_URL))
            for (String insteadOf : config.getStringList(KEY_URL, url, keyName))
                replacements.put(insteadOf, url);
        return replacements;
    }

    private String replaceUri(final String uri, final Map<String, String> replacements) {
        if (replacements.isEmpty()) {
            return uri;
        }
        Entry<String, String> match = null;
        for (Entry<String, String> replacement : replacements.entrySet()) {
            // Ignore current entry if not longer than previous match
            if (match != null && match.getKey().length() > replacement.getKey().length()) {
                continue;
            }
            if (!uri.startsWith(replacement.getKey())) {
                continue;
            }
            match = replacement;
        }
        if (match != null) {
            return match.getValue() + uri.substring(match.getKey().length());
        }
        return uri;
    }

    /**
     * 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<URIish> 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<URIish> 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<RefSpec> 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<RefSpec> 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<RefSpec> 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<RefSpec> 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.
     * <p>
     * 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.
     * <p>
     * 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;
    }
}