1 /*
2 * Copyright (C) 2010, 2013 Google Inc.
3 * and other copyright owners as documented in the project's IP log.
4 *
5 * This program and the accompanying materials are made available
6 * under the terms of the Eclipse Distribution License v1.0 which
7 * accompanies this distribution, is reproduced below, and is
8 * available at http://www.eclipse.org/org/documents/edl-v10.php
9 *
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or
13 * without modification, are permitted provided that the following
14 * conditions are met:
15 *
16 * - Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 *
19 * - Redistributions in binary form must reproduce the above
20 * copyright notice, this list of conditions and the following
21 * disclaimer in the documentation and/or other materials provided
22 * with the distribution.
23 *
24 * - Neither the name of the Eclipse Foundation, Inc. nor the
25 * names of its contributors may be used to endorse or promote
26 * products derived from this software without specific prior
27 * written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 */
43
44 package org.eclipse.jgit.lib;
45
46 import static java.util.stream.Collectors.toList;
47
48 import java.io.IOException;
49 import java.util.ArrayList;
50 import java.util.Collection;
51 import java.util.Collections;
52 import java.util.HashMap;
53 import java.util.List;
54 import java.util.Map;
55
56 import org.eclipse.jgit.annotations.NonNull;
57 import org.eclipse.jgit.annotations.Nullable;
58
59 /**
60 * Abstraction of name to {@link org.eclipse.jgit.lib.ObjectId} mapping.
61 * <p>
62 * A reference database stores a mapping of reference names to
63 * {@link org.eclipse.jgit.lib.ObjectId}. Every
64 * {@link org.eclipse.jgit.lib.Repository} has a single reference database,
65 * mapping names to the tips of the object graph contained by the
66 * {@link org.eclipse.jgit.lib.ObjectDatabase}.
67 */
68 public abstract class RefDatabase {
69 /**
70 * Order of prefixes to search when using non-absolute references.
71 * <p>
72 * The implementation's {@link #getRef(String)} method must take this search
73 * space into consideration when locating a reference by name. The first
74 * entry in the path is always {@code ""}, ensuring that absolute references
75 * are resolved without further mangling.
76 */
77 protected static final String[] SEARCH_PATH = { "", //$NON-NLS-1$
78 Constants.R_REFS, //
79 Constants.R_TAGS, //
80 Constants.R_HEADS, //
81 Constants.R_REMOTES //
82 };
83
84 /**
85 * Maximum number of times a {@link SymbolicRef} can be traversed.
86 * <p>
87 * If the reference is nested deeper than this depth, the implementation
88 * should either fail, or at least claim the reference does not exist.
89 *
90 * @since 4.2
91 */
92 public static final int MAX_SYMBOLIC_REF_DEPTH = 5;
93
94 /**
95 * Magic value for {@link #getRefsByPrefix(String)} to return all
96 * references.
97 */
98 public static final String ALL = "";//$NON-NLS-1$
99
100 /**
101 * Initialize a new reference database at this location.
102 *
103 * @throws java.io.IOException
104 * the database could not be created.
105 */
106 public abstract void create() throws IOException;
107
108 /**
109 * Close any resources held by this database.
110 */
111 public abstract void close();
112
113 /**
114 * Determine if a proposed reference name overlaps with an existing one.
115 * <p>
116 * Reference names use '/' as a component separator, and may be stored in a
117 * hierarchical storage such as a directory on the local filesystem.
118 * <p>
119 * If the reference "refs/heads/foo" exists then "refs/heads/foo/bar" must
120 * not exist, as a reference cannot have a value and also be a container for
121 * other references at the same time.
122 * <p>
123 * If the reference "refs/heads/foo/bar" exists than the reference
124 * "refs/heads/foo" cannot exist, for the same reason.
125 *
126 * @param name
127 * proposed name.
128 * @return true if the name overlaps with an existing reference; false if
129 * using this name right now would be safe.
130 * @throws java.io.IOException
131 * the database could not be read to check for conflicts.
132 * @see #getConflictingNames(String)
133 */
134 public abstract boolean isNameConflicting(String name) throws IOException;
135
136 /**
137 * Determine if a proposed reference cannot coexist with existing ones. If
138 * the passed name already exists, it's not considered a conflict.
139 *
140 * @param name
141 * proposed name to check for conflicts against
142 * @return a collection of full names of existing refs which would conflict
143 * with the passed ref name; empty collection when there are no
144 * conflicts
145 * @throws java.io.IOException
146 * @since 2.3
147 * @see #isNameConflicting(String)
148 */
149 @NonNull
150 public Collection<String> getConflictingNames(String name)
151 throws IOException {
152 Map<String, Ref> allRefs = getRefs(ALL);
153 // Cannot be nested within an existing reference.
154 int lastSlash = name.lastIndexOf('/');
155 while (0 < lastSlash) {
156 String needle = name.substring(0, lastSlash);
157 if (allRefs.containsKey(needle))
158 return Collections.singletonList(needle);
159 lastSlash = name.lastIndexOf('/', lastSlash - 1);
160 }
161
162 List<String> conflicting = new ArrayList<>();
163 // Cannot be the container of an existing reference.
164 String prefix = name + '/';
165 for (String existing : allRefs.keySet())
166 if (existing.startsWith(prefix))
167 conflicting.add(existing);
168
169 return conflicting;
170 }
171
172 /**
173 * Create a new update command to create, modify or delete a reference.
174 *
175 * @param name
176 * the name of the reference.
177 * @param detach
178 * if {@code true} and {@code name} is currently a
179 * {@link org.eclipse.jgit.lib.SymbolicRef}, the update will
180 * replace it with an {@link org.eclipse.jgit.lib.ObjectIdRef}.
181 * Otherwise, the update will recursively traverse
182 * {@link org.eclipse.jgit.lib.SymbolicRef}s and operate on the
183 * leaf {@link org.eclipse.jgit.lib.ObjectIdRef}.
184 * @return a new update for the requested name; never null.
185 * @throws java.io.IOException
186 * the reference space cannot be accessed.
187 */
188 @NonNull
189 public abstract RefUpdate newUpdate(String name, boolean detach)
190 throws IOException;
191
192 /**
193 * Create a new update command to rename a reference.
194 *
195 * @param fromName
196 * name of reference to rename from
197 * @param toName
198 * name of reference to rename to
199 * @return an update command that knows how to rename a branch to another.
200 * @throws java.io.IOException
201 * the reference space cannot be accessed.
202 */
203 @NonNull
204 public abstract RefRename newRename(String fromName, String toName)
205 throws IOException;
206
207 /**
208 * Create a new batch update to attempt on this database.
209 * <p>
210 * The default implementation performs a sequential update of each command.
211 *
212 * @return a new batch update object.
213 */
214 @NonNull
215 public BatchRefUpdate newBatchUpdate() {
216 return new BatchRefUpdate(this);
217 }
218
219 /**
220 * Whether the database is capable of performing batch updates as atomic
221 * transactions.
222 * <p>
223 * If true, by default {@link org.eclipse.jgit.lib.BatchRefUpdate} instances
224 * will perform updates atomically, meaning either all updates will succeed,
225 * or all updates will fail. It is still possible to turn off this behavior
226 * on a per-batch basis by calling {@code update.setAtomic(false)}.
227 * <p>
228 * If false, {@link org.eclipse.jgit.lib.BatchRefUpdate} instances will
229 * never perform updates atomically, and calling
230 * {@code update.setAtomic(true)} will cause the entire batch to fail with
231 * {@code REJECTED_OTHER_REASON}.
232 * <p>
233 * This definition of atomicity is stronger than what is provided by
234 * {@link org.eclipse.jgit.transport.ReceivePack}. {@code ReceivePack} will
235 * attempt to reject all commands if it knows in advance some commands may
236 * fail, even if the storage layer does not support atomic transactions.
237 * Here, atomicity applies even in the case of unforeseeable errors.
238 *
239 * @return whether transactions are atomic by default.
240 * @since 3.6
241 */
242 public boolean performsAtomicTransactions() {
243 return false;
244 }
245
246 /**
247 * Read a single reference.
248 * <p>
249 * Aside from taking advantage of {@link #SEARCH_PATH}, this method may be
250 * able to more quickly resolve a single reference name than obtaining the
251 * complete namespace by {@code getRefs(ALL).get(name)}.
252 * <p>
253 * To read a specific reference without using @{link #SEARCH_PATH}, see
254 * {@link #exactRef(String)}.
255 *
256 * @param name
257 * the name of the reference. May be a short name which must be
258 * searched for using the standard {@link #SEARCH_PATH}.
259 * @return the reference (if it exists); else {@code null}.
260 * @throws java.io.IOException
261 * the reference space cannot be accessed.
262 */
263 @Nullable
264 public abstract Ref getRef(String name) throws IOException;
265
266 /**
267 * Read a single reference.
268 * <p>
269 * Unlike {@link #getRef}, this method expects an unshortened reference
270 * name and does not search using the standard {@link #SEARCH_PATH}.
271 *
272 * @param name
273 * the unabbreviated name of the reference.
274 * @return the reference (if it exists); else {@code null}.
275 * @throws java.io.IOException
276 * the reference space cannot be accessed.
277 * @since 4.1
278 */
279 @Nullable
280 public Ref exactRef(String name) throws IOException {
281 Ref ref = getRef(name);
282 if (ref == null || !name.equals(ref.getName())) {
283 return null;
284 }
285 return ref;
286 }
287
288 /**
289 * Read the specified references.
290 * <p>
291 * This method expects a list of unshortened reference names and returns
292 * a map from reference names to refs. Any named references that do not
293 * exist will not be included in the returned map.
294 *
295 * @param refs
296 * the unabbreviated names of references to look up.
297 * @return modifiable map describing any refs that exist among the ref
298 * ref names supplied. The map can be an unsorted map.
299 * @throws java.io.IOException
300 * the reference space cannot be accessed.
301 * @since 4.1
302 */
303 @NonNull
304 public Map<String, Ref> exactRef(String... refs) throws IOException {
305 Map<String, Ref> result = new HashMap<>(refs.length);
306 for (String name : refs) {
307 Ref ref = exactRef(name);
308 if (ref != null) {
309 result.put(name, ref);
310 }
311 }
312 return result;
313 }
314
315 /**
316 * Find the first named reference.
317 * <p>
318 * This method expects a list of unshortened reference names and returns
319 * the first that exists.
320 *
321 * @param refs
322 * the unabbreviated names of references to look up.
323 * @return the first named reference that exists (if any); else {@code null}.
324 * @throws java.io.IOException
325 * the reference space cannot be accessed.
326 * @since 4.1
327 */
328 @Nullable
329 public Ref firstExactRef(String... refs) throws IOException {
330 for (String name : refs) {
331 Ref ref = exactRef(name);
332 if (ref != null) {
333 return ref;
334 }
335 }
336 return null;
337 }
338
339 /**
340 * Returns all refs.
341 * <p>
342 * This includes {@code HEAD}, branches under {@code ref/heads/}, tags
343 * under {@code refs/tags/}, etc. It does not include pseudo-refs like
344 * {@code FETCH_HEAD}; for those, see {@link #getAdditionalRefs}.
345 * <p>
346 * Symbolic references to a non-existent ref (for example,
347 * {@code HEAD} pointing to a branch yet to be born) are not included.
348 * <p>
349 * Callers interested in only a portion of the ref hierarchy can call
350 * {@link #getRefsByPrefix} instead.
351 *
352 * @return immutable list of all refs.
353 * @throws java.io.IOException
354 * the reference space cannot be accessed.
355 * @since 5.0
356 */
357 @NonNull
358 public List<Ref> getRefs() throws IOException {
359 return getRefsByPrefix(ALL);
360 }
361
362 /**
363 * Get a section of the reference namespace.
364 *
365 * @param prefix
366 * prefix to search the namespace with; must end with {@code /}.
367 * If the empty string ({@link #ALL}), obtain a complete snapshot
368 * of all references.
369 * @return modifiable map that is a complete snapshot of the current
370 * reference namespace, with {@code prefix} removed from the start
371 * of each key. The map can be an unsorted map.
372 * @throws java.io.IOException
373 * the reference space cannot be accessed.
374 * @deprecated use {@link #getRefsByPrefix} instead
375 */
376 @NonNull
377 @Deprecated
378 public abstract Map<String, Ref> getRefs(String prefix) throws IOException;
379
380 /**
381 * Returns refs whose names start with a given prefix.
382 * <p>
383 * The default implementation uses {@link #getRefs(String)}. Implementors of
384 * {@link RefDatabase} should override this method directly if a better
385 * implementation is possible.
386 *
387 * @param prefix string that names of refs should start with; may be
388 * empty (to return all refs).
389 * @return immutable list of refs whose names start with {@code prefix}.
390 * @throws java.io.IOException
391 * the reference space cannot be accessed.
392 * @since 5.0
393 */
394 @NonNull
395 public List<Ref> getRefsByPrefix(String prefix) throws IOException {
396 Map<String, Ref> coarseRefs;
397 int lastSlash = prefix.lastIndexOf('/');
398 if (lastSlash == -1) {
399 coarseRefs = getRefs(ALL);
400 } else {
401 coarseRefs = getRefs(prefix.substring(0, lastSlash + 1));
402 }
403
404 List<Ref> result;
405 if (lastSlash + 1 == prefix.length()) {
406 result = coarseRefs.values().stream().collect(toList());
407 } else {
408 String p = prefix.substring(lastSlash + 1);
409 result = coarseRefs.entrySet().stream()
410 .filter(e -> e.getKey().startsWith(p))
411 .map(e -> e.getValue())
412 .collect(toList());
413 }
414 return Collections.unmodifiableList(result);
415 }
416
417 /**
418 * Check if any refs exist in the ref database.
419 * <p>
420 * This uses the same definition of refs as {@link #getRefs()}. In
421 * particular, returns {@code false} in a new repository with no refs
422 * under {@code refs/} and {@code HEAD} pointing to a branch yet to be
423 * born, and returns {@code true} in a repository with no refs under
424 * {@code refs/} and a detached {@code HEAD} pointing to history.
425 *
426 * @return true if the database has refs.
427 * @throws java.io.IOException
428 * the reference space cannot be accessed.
429 * @since 5.0
430 */
431 public boolean hasRefs() throws IOException {
432 return !getRefs().isEmpty();
433 }
434
435 /**
436 * Get the additional reference-like entities from the repository.
437 * <p>
438 * The result list includes non-ref items such as MERGE_HEAD and
439 * FETCH_RESULT cast to be refs. The names of these refs are not returned by
440 * <code>getRefs()</code> but are accepted by {@link #getRef(String)}
441 * and {@link #exactRef(String)}.
442 *
443 * @return a list of additional refs
444 * @throws java.io.IOException
445 * the reference space cannot be accessed.
446 */
447 @NonNull
448 public abstract List<Ref> getAdditionalRefs() throws IOException;
449
450 /**
451 * Peel a possibly unpeeled reference by traversing the annotated tags.
452 * <p>
453 * If the reference cannot be peeled (as it does not refer to an annotated
454 * tag) the peeled id stays null, but
455 * {@link org.eclipse.jgit.lib.Ref#isPeeled()} will be true.
456 * <p>
457 * Implementors should check {@link org.eclipse.jgit.lib.Ref#isPeeled()}
458 * before performing any additional work effort.
459 *
460 * @param ref
461 * The reference to peel
462 * @return {@code ref} if {@code ref.isPeeled()} is true; otherwise a new
463 * Ref object representing the same data as Ref, but isPeeled() will
464 * be true and getPeeledObjectId() will contain the peeled object
465 * (or {@code null}).
466 * @throws java.io.IOException
467 * the reference space or object space cannot be accessed.
468 */
469 @NonNull
470 public abstract Ref peel(Ref ref) throws IOException;
471
472 /**
473 * Triggers a refresh of all internal data structures.
474 * <p>
475 * In case the RefDatabase implementation has internal caches this method
476 * will trigger that all these caches are cleared.
477 * <p>
478 * Implementors should overwrite this method if they use any kind of caches.
479 */
480 public void refresh() {
481 // nothing
482 }
483
484 /**
485 * Try to find the specified name in the ref map using {@link #SEARCH_PATH}.
486 *
487 * @param map
488 * map of refs to search within. Names should be fully qualified,
489 * e.g. "refs/heads/master".
490 * @param name
491 * short name of ref to find, e.g. "master" to find
492 * "refs/heads/master" in map.
493 * @return The first ref matching the name, or {@code null} if not found.
494 * @since 3.4
495 */
496 @Nullable
497 public static Ref findRef(Map<String, Ref> map, String name) {
498 for (String prefix : SEARCH_PATH) {
499 String fullname = prefix + name;
500 Ref ref = map.get(fullname);
501 if (ref != null)
502 return ref;
503 }
504 return null;
505 }
506 }