View Javadoc
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  
13  package org.eclipse.jgit.transport;
14  
15  import java.io.Serializable;
16  import java.net.URISyntaxException;
17  import java.util.ArrayList;
18  import java.util.Collections;
19  import java.util.List;
20  
21  import org.eclipse.jgit.lib.Config;
22  
23  /**
24   * A remembered remote repository, including URLs and RefSpecs.
25   * <p>
26   * A remote configuration remembers one or more URLs for a frequently accessed
27   * remote repository as well as zero or more fetch and push specifications
28   * describing how refs should be transferred between this repository and the
29   * remote repository.
30   */
31  public class RemoteConfig implements Serializable {
32  	private static final long serialVersionUID = 1L;
33  
34  	private static final String SECTION = "remote"; //$NON-NLS-1$
35  
36  	private static final String KEY_URL = "url"; //$NON-NLS-1$
37  
38  	private static final String KEY_PUSHURL = "pushurl"; //$NON-NLS-1$
39  
40  	private static final String KEY_FETCH = "fetch"; //$NON-NLS-1$
41  
42  	private static final String KEY_PUSH = "push"; //$NON-NLS-1$
43  
44  	private static final String KEY_UPLOADPACK = "uploadpack"; //$NON-NLS-1$
45  
46  	private static final String KEY_RECEIVEPACK = "receivepack"; //$NON-NLS-1$
47  
48  	private static final String KEY_TAGOPT = "tagopt"; //$NON-NLS-1$
49  
50  	private static final String KEY_MIRROR = "mirror"; //$NON-NLS-1$
51  
52  	private static final String KEY_TIMEOUT = "timeout"; //$NON-NLS-1$
53  
54  	private static final boolean DEFAULT_MIRROR = false;
55  
56  	/** Default value for {@link #getUploadPack()} if not specified. */
57  	public static final String DEFAULT_UPLOAD_PACK = "git-upload-pack"; //$NON-NLS-1$
58  
59  	/** Default value for {@link #getReceivePack()} if not specified. */
60  	public static final String DEFAULT_RECEIVE_PACK = "git-receive-pack"; //$NON-NLS-1$
61  
62  	/**
63  	 * Parse all remote blocks in an existing configuration file, looking for
64  	 * remotes configuration.
65  	 *
66  	 * @param rc
67  	 *            the existing configuration to get the remote settings from.
68  	 *            The configuration must already be loaded into memory.
69  	 * @return all remotes configurations existing in provided repository
70  	 *         configuration. Returned configurations are ordered
71  	 *         lexicographically by names.
72  	 * @throws java.net.URISyntaxException
73  	 *             one of the URIs within the remote's configuration is invalid.
74  	 */
75  	public static List<RemoteConfig> getAllRemoteConfigs(Config rc)
76  			throws URISyntaxException {
77  		final List<String> names = new ArrayList<>(rc
78  				.getSubsections(SECTION));
79  		Collections.sort(names);
80  
81  		final List<RemoteConfig> result = new ArrayList<>(names
82  				.size());
83  		for (String name : names)
84  			result.add(new RemoteConfig(rc, name));
85  		return result;
86  	}
87  
88  	private String name;
89  
90  	private List<URIish> uris;
91  
92  	private List<URIish> pushURIs;
93  
94  	private List<RefSpec> fetch;
95  
96  	private List<RefSpec> push;
97  
98  	private String uploadpack;
99  
100 	private String receivepack;
101 
102 	private TagOpt tagopt;
103 
104 	private boolean mirror;
105 
106 	private int timeout;
107 
108 	/**
109 	 * Parse a remote block from an existing configuration file.
110 	 * <p>
111 	 * This constructor succeeds even if the requested remote is not defined
112 	 * within the supplied configuration file. If that occurs then there will be
113 	 * no URIs and no ref specifications known to the new instance.
114 	 *
115 	 * @param rc
116 	 *            the existing configuration to get the remote settings from.
117 	 *            The configuration must already be loaded into memory.
118 	 * @param remoteName
119 	 *            subsection key indicating the name of this remote.
120 	 * @throws java.net.URISyntaxException
121 	 *             one of the URIs within the remote's configuration is invalid.
122 	 */
123 	public RemoteConfig(Config rc, String remoteName)
124 			throws URISyntaxException {
125 		name = remoteName;
126 
127 		String[] vlst;
128 		String val;
129 
130 		vlst = rc.getStringList(SECTION, name, KEY_URL);
131 		UrlConfig urls = new UrlConfig(rc);
132 		uris = new ArrayList<>(vlst.length);
133 		for (String s : vlst) {
134 			uris.add(new URIish(urls.replace(s)));
135 		}
136 		String[] plst = rc.getStringList(SECTION, name, KEY_PUSHURL);
137 		pushURIs = new ArrayList<>(plst.length);
138 		for (String s : plst) {
139 			pushURIs.add(new URIish(s));
140 		}
141 		if (pushURIs.isEmpty()) {
142 			// Would default to the uris. If we have pushinsteadof, we must
143 			// supply rewritten push uris.
144 			if (urls.hasPushReplacements()) {
145 				for (String s : vlst) {
146 					String replaced = urls.replacePush(s);
147 					if (!s.equals(replaced)) {
148 						pushURIs.add(new URIish(replaced));
149 					}
150 				}
151 			}
152 		}
153 		fetch = rc.getRefSpecs(SECTION, name, KEY_FETCH);
154 		push = rc.getRefSpecs(SECTION, name, KEY_PUSH);
155 		val = rc.getString(SECTION, name, KEY_UPLOADPACK);
156 		if (val == null) {
157 			val = DEFAULT_UPLOAD_PACK;
158 		}
159 		uploadpack = val;
160 
161 		val = rc.getString(SECTION, name, KEY_RECEIVEPACK);
162 		if (val == null) {
163 			val = DEFAULT_RECEIVE_PACK;
164 		}
165 		receivepack = val;
166 
167 		try {
168 			val = rc.getString(SECTION, name, KEY_TAGOPT);
169 			tagopt = TagOpt.fromOption(val);
170 		} catch (IllegalArgumentException e) {
171 			// C git silently ignores invalid tagopt values.
172 			tagopt = TagOpt.AUTO_FOLLOW;
173 		}
174 		mirror = rc.getBoolean(SECTION, name, KEY_MIRROR, DEFAULT_MIRROR);
175 		timeout = rc.getInt(SECTION, name, KEY_TIMEOUT, 0);
176 	}
177 
178 	/**
179 	 * Update this remote's definition within the configuration.
180 	 *
181 	 * @param rc
182 	 *            the configuration file to store ourselves into.
183 	 */
184 	public void update(Config rc) {
185 		final List<String> vlst = new ArrayList<>();
186 
187 		vlst.clear();
188 		for (URIish u : getURIs())
189 			vlst.add(u.toPrivateString());
190 		rc.setStringList(SECTION, getName(), KEY_URL, vlst);
191 
192 		vlst.clear();
193 		for (URIish u : getPushURIs())
194 			vlst.add(u.toPrivateString());
195 		rc.setStringList(SECTION, getName(), KEY_PUSHURL, vlst);
196 
197 		vlst.clear();
198 		for (RefSpec u : getFetchRefSpecs())
199 			vlst.add(u.toString());
200 		rc.setStringList(SECTION, getName(), KEY_FETCH, vlst);
201 
202 		vlst.clear();
203 		for (RefSpec u : getPushRefSpecs())
204 			vlst.add(u.toString());
205 		rc.setStringList(SECTION, getName(), KEY_PUSH, vlst);
206 
207 		set(rc, KEY_UPLOADPACK, getUploadPack(), DEFAULT_UPLOAD_PACK);
208 		set(rc, KEY_RECEIVEPACK, getReceivePack(), DEFAULT_RECEIVE_PACK);
209 		set(rc, KEY_TAGOPT, getTagOpt().option(), TagOpt.AUTO_FOLLOW.option());
210 		set(rc, KEY_MIRROR, mirror, DEFAULT_MIRROR);
211 		set(rc, KEY_TIMEOUT, timeout, 0);
212 	}
213 
214 	private void set(final Config rc, final String key,
215 			final String currentValue, final String defaultValue) {
216 		if (defaultValue.equals(currentValue))
217 			unset(rc, key);
218 		else
219 			rc.setString(SECTION, getName(), key, currentValue);
220 	}
221 
222 	private void set(final Config rc, final String key,
223 			final boolean currentValue, final boolean defaultValue) {
224 		if (defaultValue == currentValue)
225 			unset(rc, key);
226 		else
227 			rc.setBoolean(SECTION, getName(), key, currentValue);
228 	}
229 
230 	private void set(final Config rc, final String key, final int currentValue,
231 			final int defaultValue) {
232 		if (defaultValue == currentValue)
233 			unset(rc, key);
234 		else
235 			rc.setInt(SECTION, getName(), key, currentValue);
236 	}
237 
238 	private void unset(Config rc, String key) {
239 		rc.unset(SECTION, getName(), key);
240 	}
241 
242 	/**
243 	 * Get the local name this remote configuration is recognized as.
244 	 *
245 	 * @return name assigned by the user to this configuration block.
246 	 */
247 	public String getName() {
248 		return name;
249 	}
250 
251 	/**
252 	 * Get all configured URIs under this remote.
253 	 *
254 	 * @return the set of URIs known to this remote.
255 	 */
256 	public List<URIish> getURIs() {
257 		return Collections.unmodifiableList(uris);
258 	}
259 
260 	/**
261 	 * Add a new URI to the end of the list of URIs.
262 	 *
263 	 * @param toAdd
264 	 *            the new URI to add to this remote.
265 	 * @return true if the URI was added; false if it already exists.
266 	 */
267 	public boolean addURI(URIish toAdd) {
268 		if (uris.contains(toAdd))
269 			return false;
270 		return uris.add(toAdd);
271 	}
272 
273 	/**
274 	 * Remove a URI from the list of URIs.
275 	 *
276 	 * @param toRemove
277 	 *            the URI to remove from this remote.
278 	 * @return true if the URI was added; false if it already exists.
279 	 */
280 	public boolean removeURI(URIish toRemove) {
281 		return uris.remove(toRemove);
282 	}
283 
284 	/**
285 	 * Get all configured push-only URIs under this remote.
286 	 *
287 	 * @return the set of URIs known to this remote.
288 	 */
289 	public List<URIish> getPushURIs() {
290 		return Collections.unmodifiableList(pushURIs);
291 	}
292 
293 	/**
294 	 * Add a new push-only URI to the end of the list of URIs.
295 	 *
296 	 * @param toAdd
297 	 *            the new URI to add to this remote.
298 	 * @return true if the URI was added; false if it already exists.
299 	 */
300 	public boolean addPushURI(URIish toAdd) {
301 		if (pushURIs.contains(toAdd))
302 			return false;
303 		return pushURIs.add(toAdd);
304 	}
305 
306 	/**
307 	 * Remove a push-only URI from the list of URIs.
308 	 *
309 	 * @param toRemove
310 	 *            the URI to remove from this remote.
311 	 * @return true if the URI was added; false if it already exists.
312 	 */
313 	public boolean removePushURI(URIish toRemove) {
314 		return pushURIs.remove(toRemove);
315 	}
316 
317 	/**
318 	 * Remembered specifications for fetching from a repository.
319 	 *
320 	 * @return set of specs used by default when fetching.
321 	 */
322 	public List<RefSpec> getFetchRefSpecs() {
323 		return Collections.unmodifiableList(fetch);
324 	}
325 
326 	/**
327 	 * Add a new fetch RefSpec to this remote.
328 	 *
329 	 * @param s
330 	 *            the new specification to add.
331 	 * @return true if the specification was added; false if it already exists.
332 	 */
333 	public boolean addFetchRefSpec(RefSpec s) {
334 		if (fetch.contains(s))
335 			return false;
336 		return fetch.add(s);
337 	}
338 
339 	/**
340 	 * Override existing fetch specifications with new ones.
341 	 *
342 	 * @param specs
343 	 *            list of fetch specifications to set. List is copied, it can be
344 	 *            modified after this call.
345 	 */
346 	public void setFetchRefSpecs(List<RefSpec> specs) {
347 		fetch.clear();
348 		fetch.addAll(specs);
349 	}
350 
351 	/**
352 	 * Override existing push specifications with new ones.
353 	 *
354 	 * @param specs
355 	 *            list of push specifications to set. List is copied, it can be
356 	 *            modified after this call.
357 	 */
358 	public void setPushRefSpecs(List<RefSpec> specs) {
359 		push.clear();
360 		push.addAll(specs);
361 	}
362 
363 	/**
364 	 * Remove a fetch RefSpec from this remote.
365 	 *
366 	 * @param s
367 	 *            the specification to remove.
368 	 * @return true if the specification existed and was removed.
369 	 */
370 	public boolean removeFetchRefSpec(RefSpec s) {
371 		return fetch.remove(s);
372 	}
373 
374 	/**
375 	 * Remembered specifications for pushing to a repository.
376 	 *
377 	 * @return set of specs used by default when pushing.
378 	 */
379 	public List<RefSpec> getPushRefSpecs() {
380 		return Collections.unmodifiableList(push);
381 	}
382 
383 	/**
384 	 * Add a new push RefSpec to this remote.
385 	 *
386 	 * @param s
387 	 *            the new specification to add.
388 	 * @return true if the specification was added; false if it already exists.
389 	 */
390 	public boolean addPushRefSpec(RefSpec s) {
391 		if (push.contains(s))
392 			return false;
393 		return push.add(s);
394 	}
395 
396 	/**
397 	 * Remove a push RefSpec from this remote.
398 	 *
399 	 * @param s
400 	 *            the specification to remove.
401 	 * @return true if the specification existed and was removed.
402 	 */
403 	public boolean removePushRefSpec(RefSpec s) {
404 		return push.remove(s);
405 	}
406 
407 	/**
408 	 * Override for the location of 'git-upload-pack' on the remote system.
409 	 * <p>
410 	 * This value is only useful for an SSH style connection, where Git is
411 	 * asking the remote system to execute a program that provides the necessary
412 	 * network protocol.
413 	 *
414 	 * @return location of 'git-upload-pack' on the remote system. If no
415 	 *         location has been configured the default of 'git-upload-pack' is
416 	 *         returned instead.
417 	 */
418 	public String getUploadPack() {
419 		return uploadpack;
420 	}
421 
422 	/**
423 	 * Override for the location of 'git-receive-pack' on the remote system.
424 	 * <p>
425 	 * This value is only useful for an SSH style connection, where Git is
426 	 * asking the remote system to execute a program that provides the necessary
427 	 * network protocol.
428 	 *
429 	 * @return location of 'git-receive-pack' on the remote system. If no
430 	 *         location has been configured the default of 'git-receive-pack' is
431 	 *         returned instead.
432 	 */
433 	public String getReceivePack() {
434 		return receivepack;
435 	}
436 
437 	/**
438 	 * Get the description of how annotated tags should be treated during fetch.
439 	 *
440 	 * @return option indicating the behavior of annotated tags in fetch.
441 	 */
442 	public TagOpt getTagOpt() {
443 		return tagopt;
444 	}
445 
446 	/**
447 	 * Set the description of how annotated tags should be treated on fetch.
448 	 *
449 	 * @param option
450 	 *            method to use when handling annotated tags.
451 	 */
452 	public void setTagOpt(TagOpt option) {
453 		tagopt = option != null ? option : TagOpt.AUTO_FOLLOW;
454 	}
455 
456 	/**
457 	 * Whether pushing to the remote automatically deletes remote refs which
458 	 * don't exist on the source side.
459 	 *
460 	 * @return true if pushing to the remote automatically deletes remote refs
461 	 *         which don't exist on the source side.
462 	 */
463 	public boolean isMirror() {
464 		return mirror;
465 	}
466 
467 	/**
468 	 * Set the mirror flag to automatically delete remote refs.
469 	 *
470 	 * @param m
471 	 *            true to automatically delete remote refs during push.
472 	 */
473 	public void setMirror(boolean m) {
474 		mirror = m;
475 	}
476 
477 	/**
478 	 * Get timeout (in seconds) before aborting an IO operation.
479 	 *
480 	 * @return timeout (in seconds) before aborting an IO operation.
481 	 */
482 	public int getTimeout() {
483 		return timeout;
484 	}
485 
486 	/**
487 	 * Set the timeout before willing to abort an IO call.
488 	 *
489 	 * @param seconds
490 	 *            number of seconds to wait (with no data transfer occurring)
491 	 *            before aborting an IO read or write operation with this
492 	 *            remote.  A timeout of 0 will block indefinitely.
493 	 */
494 	public void setTimeout(int seconds) {
495 		timeout = seconds;
496 	}
497 }