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 java.io.IOException; 47 import java.util.ArrayList; 48 import java.util.Collection; 49 import java.util.Collections; 50 import java.util.HashMap; 51 import java.util.List; 52 import java.util.Map; 53 54 /** 55 * Abstraction of name to {@link ObjectId} mapping. 56 * <p> 57 * A reference database stores a mapping of reference names to {@link ObjectId}. 58 * Every {@link Repository} has a single reference database, mapping names to 59 * the tips of the object graph contained by the {@link ObjectDatabase}. 60 */ 61 public abstract class RefDatabase { 62 /** 63 * Order of prefixes to search when using non-absolute references. 64 * <p> 65 * The implementation's {@link #getRef(String)} method must take this search 66 * space into consideration when locating a reference by name. The first 67 * entry in the path is always {@code ""}, ensuring that absolute references 68 * are resolved without further mangling. 69 */ 70 protected static final String[] SEARCH_PATH = { "", //$NON-NLS-1$ 71 Constants.R_REFS, // 72 Constants.R_TAGS, // 73 Constants.R_HEADS, // 74 Constants.R_REMOTES // 75 }; 76 77 /** 78 * Maximum number of times a {@link SymbolicRef} can be traversed. 79 * <p> 80 * If the reference is nested deeper than this depth, the implementation 81 * should either fail, or at least claim the reference does not exist. 82 */ 83 protected static final int MAX_SYMBOLIC_REF_DEPTH = 5; 84 85 /** Magic value for {@link #getRefs(String)} to return all references. */ 86 public static final String ALL = "";//$NON-NLS-1$ 87 88 /** 89 * Initialize a new reference database at this location. 90 * 91 * @throws IOException 92 * the database could not be created. 93 */ 94 public abstract void create() throws IOException; 95 96 /** Close any resources held by this database. */ 97 public abstract void close(); 98 99 /** 100 * Determine if a proposed reference name overlaps with an existing one. 101 * <p> 102 * Reference names use '/' as a component separator, and may be stored in a 103 * hierarchical storage such as a directory on the local filesystem. 104 * <p> 105 * If the reference "refs/heads/foo" exists then "refs/heads/foo/bar" must 106 * not exist, as a reference cannot have a value and also be a container for 107 * other references at the same time. 108 * <p> 109 * If the reference "refs/heads/foo/bar" exists than the reference 110 * "refs/heads/foo" cannot exist, for the same reason. 111 * 112 * @param name 113 * proposed name. 114 * @return true if the name overlaps with an existing reference; false if 115 * using this name right now would be safe. 116 * @throws IOException 117 * the database could not be read to check for conflicts. 118 * @see #getConflictingNames(String) 119 */ 120 public abstract boolean isNameConflicting(String name) throws IOException; 121 122 /** 123 * Determine if a proposed reference cannot coexist with existing ones. If 124 * the passed name already exists, it's not considered a conflict. 125 * 126 * @param name 127 * proposed name to check for conflicts against 128 * @return a collection of full names of existing refs which would conflict 129 * with the passed ref name; empty collection when there are no 130 * conflicts 131 * @throws IOException 132 * @since 2.3 133 * @see #isNameConflicting(String) 134 */ 135 public Collection<String> getConflictingNames(String name) 136 throws IOException { 137 Map<String, Ref> allRefs = getRefs(ALL); 138 // Cannot be nested within an existing reference. 139 int lastSlash = name.lastIndexOf('/'); 140 while (0 < lastSlash) { 141 String needle = name.substring(0, lastSlash); 142 if (allRefs.containsKey(needle)) 143 return Collections.singletonList(needle); 144 lastSlash = name.lastIndexOf('/', lastSlash - 1); 145 } 146 147 List<String> conflicting = new ArrayList<String>(); 148 // Cannot be the container of an existing reference. 149 String prefix = name + '/'; 150 for (String existing : allRefs.keySet()) 151 if (existing.startsWith(prefix)) 152 conflicting.add(existing); 153 154 return conflicting; 155 } 156 157 /** 158 * Create a new update command to create, modify or delete a reference. 159 * 160 * @param name 161 * the name of the reference. 162 * @param detach 163 * if {@code true} and {@code name} is currently a 164 * {@link SymbolicRef}, the update will replace it with an 165 * {@link ObjectIdRef}. Otherwise, the update will recursively 166 * traverse {@link SymbolicRef}s and operate on the leaf 167 * {@link ObjectIdRef}. 168 * @return a new update for the requested name; never null. 169 * @throws IOException 170 * the reference space cannot be accessed. 171 */ 172 public abstract RefUpdate newUpdate(String name, boolean detach) 173 throws IOException; 174 175 /** 176 * Create a new update command to rename a reference. 177 * 178 * @param fromName 179 * name of reference to rename from 180 * @param toName 181 * name of reference to rename to 182 * @return an update command that knows how to rename a branch to another. 183 * @throws IOException 184 * the reference space cannot be accessed. 185 */ 186 public abstract RefRename newRename(String fromName, String toName) 187 throws IOException; 188 189 /** 190 * Create a new batch update to attempt on this database. 191 * <p> 192 * The default implementation performs a sequential update of each command. 193 * 194 * @return a new batch update object. 195 */ 196 public BatchRefUpdate newBatchUpdate() { 197 return new BatchRefUpdate(this); 198 } 199 200 /** 201 * @return if the database performs {@code newBatchUpdate()} as an atomic 202 * transaction. 203 * @since 3.6 204 */ 205 public boolean performsAtomicTransactions() { 206 return false; 207 } 208 209 /** 210 * Read a single reference. 211 * <p> 212 * Aside from taking advantage of {@link #SEARCH_PATH}, this method may be 213 * able to more quickly resolve a single reference name than obtaining the 214 * complete namespace by {@code getRefs(ALL).get(name)}. 215 * <p> 216 * To read a specific reference without using @{link #SEARCH_PATH}, see 217 * {@link #exactRef(String)}. 218 * 219 * @param name 220 * the name of the reference. May be a short name which must be 221 * searched for using the standard {@link #SEARCH_PATH}. 222 * @return the reference (if it exists); else {@code null}. 223 * @throws IOException 224 * the reference space cannot be accessed. 225 */ 226 public abstract Ref getRef(String name) throws IOException; 227 228 /** 229 * Read a single reference. 230 * <p> 231 * Unlike {@link #getRef}, this method expects an unshortened reference 232 * name and does not search using the standard {@link #SEARCH_PATH}. 233 * 234 * @param name 235 * the unabbreviated name of the reference. 236 * @return the reference (if it exists); else {@code null}. 237 * @throws IOException 238 * the reference space cannot be accessed. 239 * @since 4.1 240 */ 241 public Ref exactRef(String name) throws IOException { 242 Ref ref = getRef(name); 243 if (ref == null || !name.equals(ref.getName())) { 244 return null; 245 } 246 return ref; 247 } 248 249 /** 250 * Read the specified references. 251 * <p> 252 * This method expects a list of unshortened reference names and returns 253 * a map from reference names to refs. Any named references that do not 254 * exist will not be included in the returned map. 255 * 256 * @param refs 257 * the unabbreviated names of references to look up. 258 * @return modifiable map describing any refs that exist among the ref 259 * ref names supplied. The map can be an unsorted map. 260 * @throws IOException 261 * the reference space cannot be accessed. 262 * @since 4.1 263 */ 264 public Map<String, Ref> exactRef(String... refs) throws IOException { 265 Map<String, Ref> result = new HashMap<>(refs.length); 266 for (String name : refs) { 267 Ref ref = exactRef(name); 268 if (ref != null) { 269 result.put(name, ref); 270 } 271 } 272 return result; 273 } 274 275 /** 276 * Find the first named reference. 277 * <p> 278 * This method expects a list of unshortened reference names and returns 279 * the first that exists. 280 * 281 * @param refs 282 * the unabbreviated names of references to look up. 283 * @return the first named reference that exists (if any); else {@code null}. 284 * @throws IOException 285 * the reference space cannot be accessed. 286 * @since 4.1 287 */ 288 public Ref firstExactRef(String... refs) throws IOException { 289 for (String name : refs) { 290 Ref ref = exactRef(name); 291 if (ref != null) { 292 return ref; 293 } 294 } 295 return null; 296 } 297 298 /** 299 * Get a section of the reference namespace. 300 * 301 * @param prefix 302 * prefix to search the namespace with; must end with {@code /}. 303 * If the empty string ({@link #ALL}), obtain a complete snapshot 304 * of all references. 305 * @return modifiable map that is a complete snapshot of the current 306 * reference namespace, with {@code prefix} removed from the start 307 * of each key. The map can be an unsorted map. 308 * @throws IOException 309 * the reference space cannot be accessed. 310 */ 311 public abstract Map<String, Ref> getRefs(String prefix) throws IOException; 312 313 /** 314 * Get the additional reference-like entities from the repository. 315 * <p> 316 * The result list includes non-ref items such as MERGE_HEAD and 317 * FETCH_RESULT cast to be refs. The names of these refs are not returned by 318 * <code>getRefs(ALL)</code> but are accepted by {@link #getRef(String)} 319 * and {@link #exactRef(String)}. 320 * 321 * @return a list of additional refs 322 * @throws IOException 323 * the reference space cannot be accessed. 324 */ 325 public abstract List<Ref> getAdditionalRefs() throws IOException; 326 327 /** 328 * Peel a possibly unpeeled reference by traversing the annotated tags. 329 * <p> 330 * If the reference cannot be peeled (as it does not refer to an annotated 331 * tag) the peeled id stays null, but {@link Ref#isPeeled()} will be true. 332 * <p> 333 * Implementors should check {@link Ref#isPeeled()} before performing any 334 * additional work effort. 335 * 336 * @param ref 337 * The reference to peel 338 * @return {@code ref} if {@code ref.isPeeled()} is true; otherwise a new 339 * Ref object representing the same data as Ref, but isPeeled() will 340 * be true and getPeeledObjectId() will contain the peeled object 341 * (or null). 342 * @throws IOException 343 * the reference space or object space cannot be accessed. 344 */ 345 public abstract Ref peel(Ref ref) throws IOException; 346 347 /** 348 * Triggers a refresh of all internal data structures. 349 * <p> 350 * In case the RefDatabase implementation has internal caches this method 351 * will trigger that all these caches are cleared. 352 * <p> 353 * Implementors should overwrite this method if they use any kind of caches. 354 */ 355 public void refresh() { 356 // nothing 357 } 358 359 /** 360 * Try to find the specified name in the ref map using {@link #SEARCH_PATH}. 361 * 362 * @param map 363 * map of refs to search within. Names should be fully qualified, 364 * e.g. "refs/heads/master". 365 * @param name 366 * short name of ref to find, e.g. "master" to find 367 * "refs/heads/master" in map. 368 * @return The first ref matching the name, or null if not found. 369 * @since 3.4 370 */ 371 public static Ref findRef(Map<String, Ref> map, String name) { 372 for (String prefix : SEARCH_PATH) { 373 String fullname = prefix + name; 374 Ref ref = map.get(fullname); 375 if (ref != null) 376 return ref; 377 } 378 return null; 379 } 380 }