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 int slash = name.lastIndexOf('/'); 243 String prefix = name.substring(0, slash + 1); 244 String rest = name.substring(slash + 1); 245 Ref result = getRefs(prefix).get(rest); 246 if (result != null || slash != -1) { 247 return result; 248 } 249 250 for (Ref ref : getAdditionalRefs()) { 251 if (name.equals(ref.getName())) { 252 return ref; 253 } 254 } 255 return null; 256 } 257 258 /** 259 * Read the specified references. 260 * <p> 261 * This method expects a list of unshortened reference names and returns 262 * a map from reference names to refs. Any named references that do not 263 * exist will not be included in the returned map. 264 * 265 * @param refs 266 * the unabbreviated names of references to look up. 267 * @return modifiable map describing any refs that exist among the ref 268 * ref names supplied. The map can be an unsorted map. 269 * @throws IOException 270 * the reference space cannot be accessed. 271 * @since 4.1 272 */ 273 public Map<String, Ref> exactRef(String... refs) throws IOException { 274 Map<String, Ref> result = new HashMap<>(refs.length); 275 for (String name : refs) { 276 Ref ref = exactRef(name); 277 if (ref != null) { 278 result.put(name, ref); 279 } 280 } 281 return result; 282 } 283 284 /** 285 * Find the first named reference. 286 * <p> 287 * This method expects a list of unshortened reference names and returns 288 * the first that exists. 289 * 290 * @param refs 291 * the unabbreviated names of references to look up. 292 * @return the first named reference that exists (if any); else {@code null}. 293 * @throws IOException 294 * the reference space cannot be accessed. 295 * @since 4.1 296 */ 297 public Ref firstExactRef(String... refs) throws IOException { 298 for (String name : refs) { 299 Ref ref = exactRef(name); 300 if (ref != null) { 301 return ref; 302 } 303 } 304 return null; 305 } 306 307 /** 308 * Get a section of the reference namespace. 309 * 310 * @param prefix 311 * prefix to search the namespace with; must end with {@code /}. 312 * If the empty string ({@link #ALL}), obtain a complete snapshot 313 * of all references. 314 * @return modifiable map that is a complete snapshot of the current 315 * reference namespace, with {@code prefix} removed from the start 316 * of each key. The map can be an unsorted map. 317 * @throws IOException 318 * the reference space cannot be accessed. 319 */ 320 public abstract Map<String, Ref> getRefs(String prefix) throws IOException; 321 322 /** 323 * Get the additional reference-like entities from the repository. 324 * <p> 325 * The result list includes non-ref items such as MERGE_HEAD and 326 * FETCH_RESULT cast to be refs. The names of these refs are not returned by 327 * <code>getRefs(ALL)</code> but are accepted by {@link #getRef(String)} 328 * and {@link #exactRef(String)}. 329 * 330 * @return a list of additional refs 331 * @throws IOException 332 * the reference space cannot be accessed. 333 */ 334 public abstract List<Ref> getAdditionalRefs() throws IOException; 335 336 /** 337 * Peel a possibly unpeeled reference by traversing the annotated tags. 338 * <p> 339 * If the reference cannot be peeled (as it does not refer to an annotated 340 * tag) the peeled id stays null, but {@link Ref#isPeeled()} will be true. 341 * <p> 342 * Implementors should check {@link Ref#isPeeled()} before performing any 343 * additional work effort. 344 * 345 * @param ref 346 * The reference to peel 347 * @return {@code ref} if {@code ref.isPeeled()} is true; otherwise a new 348 * Ref object representing the same data as Ref, but isPeeled() will 349 * be true and getPeeledObjectId() will contain the peeled object 350 * (or null). 351 * @throws IOException 352 * the reference space or object space cannot be accessed. 353 */ 354 public abstract Ref peel(Ref ref) throws IOException; 355 356 /** 357 * Triggers a refresh of all internal data structures. 358 * <p> 359 * In case the RefDatabase implementation has internal caches this method 360 * will trigger that all these caches are cleared. 361 * <p> 362 * Implementors should overwrite this method if they use any kind of caches. 363 */ 364 public void refresh() { 365 // nothing 366 } 367 368 /** 369 * Try to find the specified name in the ref map using {@link #SEARCH_PATH}. 370 * 371 * @param map 372 * map of refs to search within. Names should be fully qualified, 373 * e.g. "refs/heads/master". 374 * @param name 375 * short name of ref to find, e.g. "master" to find 376 * "refs/heads/master" in map. 377 * @return The first ref matching the name, or null if not found. 378 * @since 3.4 379 */ 380 public static Ref findRef(Map<String, Ref> map, String name) { 381 for (String prefix : SEARCH_PATH) { 382 String fullname = prefix + name; 383 Ref ref = map.get(fullname); 384 if (ref != null) 385 return ref; 386 } 387 return null; 388 } 389 }