1 /* 2 * Copyright (C) 2017, 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.internal.storage.reftable; 45 46 import static org.eclipse.jgit.lib.RefDatabase.MAX_SYMBOLIC_REF_DEPTH; 47 48 import java.io.ByteArrayOutputStream; 49 import java.io.IOException; 50 import java.util.Collection; 51 52 import org.eclipse.jgit.annotations.Nullable; 53 import org.eclipse.jgit.internal.storage.io.BlockSource; 54 import org.eclipse.jgit.lib.AnyObjectId; 55 import org.eclipse.jgit.lib.Ref; 56 import org.eclipse.jgit.lib.SymbolicRef; 57 58 /** 59 * Abstract table of references. 60 */ 61 public abstract class Reftable implements AutoCloseable { 62 /** 63 * References to convert into a reftable 64 * 65 * @param refs 66 * references to convert into a reftable; may be empty. 67 * @return a reader for the supplied references. 68 */ 69 public static Reftable from(Collection<Ref> refs) { 70 try { 71 ReftableConfig cfg = new ReftableConfig(); 72 cfg.setIndexObjects(false); 73 cfg.setAlignBlocks(false); 74 ByteArrayOutputStream buf = new ByteArrayOutputStream(); 75 new ReftableWriter() 76 .setConfig(cfg) 77 .begin(buf) 78 .sortAndWriteRefs(refs) 79 .finish(); 80 return new ReftableReader(BlockSource.from(buf.toByteArray())); 81 } catch (IOException e) { 82 throw new RuntimeException(e); 83 } 84 } 85 86 /** {@code true} if deletions should be included in results. */ 87 protected boolean includeDeletes; 88 89 /** 90 * Whether deleted references will be returned. 91 * 92 * @param deletes 93 * if {@code true} deleted references will be returned. If 94 * {@code false} (default behavior), deleted references will be 95 * skipped, and not returned. 96 */ 97 public void setIncludeDeletes(boolean deletes) { 98 includeDeletes = deletes; 99 } 100 101 /** 102 * Seek to the first reference, to iterate in order. 103 * 104 * @return cursor to iterate. 105 * @throws java.io.IOException 106 * if references cannot be read. 107 */ 108 public abstract RefCursor allRefs() throws IOException; 109 110 /** 111 * Seek to a reference. 112 * <p> 113 * This method will seek to the reference {@code refName}. If present, the 114 * returned cursor will iterate exactly one entry. If not found, an empty 115 * cursor is returned. 116 * 117 * @param refName 118 * reference name. 119 * @return cursor to iterate; empty cursor if no references match. 120 * @throws java.io.IOException 121 * if references cannot be read. 122 */ 123 public abstract RefCursor seekRef(String refName) throws IOException; 124 125 /** 126 * Seek references with prefix. 127 * <p> 128 * The method will seek all the references starting with {@code prefix} as a 129 * prefix. If no references start with this prefix, an empty cursor is 130 * returned. 131 * 132 * @param prefix 133 * prefix to find. 134 * @return cursor to iterate; empty cursor if no references match. 135 * @throws java.io.IOException 136 * if references cannot be read. 137 */ 138 public abstract RefCursor seekRefsWithPrefix(String prefix) throws IOException; 139 140 /** 141 * Match references pointing to a specific object. 142 * 143 * @param id 144 * object to find. 145 * @return cursor to iterate; empty cursor if no references match. 146 * @throws java.io.IOException 147 * if references cannot be read. 148 */ 149 public abstract RefCursor byObjectId(AnyObjectId id) throws IOException; 150 151 /** 152 * Seek reader to read log records. 153 * 154 * @return cursor to iterate; empty cursor if no logs are present. 155 * @throws java.io.IOException 156 * if logs cannot be read. 157 */ 158 public abstract LogCursor allLogs() throws IOException; 159 160 /** 161 * Read a single reference's log. 162 * 163 * @param refName 164 * exact name of the reference whose log to read. 165 * @return cursor to iterate; empty cursor if no logs match. 166 * @throws java.io.IOException 167 * if logs cannot be read. 168 */ 169 public LogCursor seekLog(String refName) throws IOException { 170 return seekLog(refName, Long.MAX_VALUE); 171 } 172 173 /** 174 * Seek to an update index in a reference's log. 175 * 176 * @param refName 177 * exact name of the reference whose log to read. 178 * @param updateIndex 179 * most recent index to return first in the log cursor. Log 180 * records at or before {@code updateIndex} will be returned. 181 * @return cursor to iterate; empty cursor if no logs match. 182 * @throws java.io.IOException 183 * if logs cannot be read. 184 */ 185 public abstract LogCursor seekLog(String refName, long updateIndex) 186 throws IOException; 187 188 /** 189 * Lookup a reference, or null if not found. 190 * 191 * @param refName 192 * reference name to find. 193 * @return the reference, or {@code null} if not found. 194 * @throws java.io.IOException 195 * if references cannot be read. 196 */ 197 @Nullable 198 public Ref exactRef(String refName) throws IOException { 199 try (RefCursor rc = seekRef(refName)) { 200 return rc.next() ? rc.getRef() : null; 201 } 202 } 203 204 /** 205 * Test if a reference exists. 206 * 207 * @param refName 208 * reference name or subtree to find. 209 * @return {@code true} if the reference exists. 210 * @throws java.io.IOException 211 * if references cannot be read. 212 */ 213 public boolean hasRef(String refName) throws IOException { 214 try (RefCursor rc = seekRef(refName)) { 215 return rc.next(); 216 } 217 } 218 219 /** 220 * Test if any reference starts with {@code prefix} as a prefix. 221 * 222 * @param prefix 223 * prefix to find. 224 * @return {@code true} if at least one reference exists with prefix. 225 * @throws java.io.IOException 226 * if references cannot be read. 227 */ 228 public boolean hasRefsWithPrefix(String prefix) throws IOException { 229 try (RefCursor rc = seekRefsWithPrefix(prefix)) { 230 return rc.next(); 231 } 232 } 233 234 /** 235 * Test if any reference directly refers to the object. 236 * 237 * @param id 238 * ObjectId to find. 239 * @return {@code true} if any reference exists directly referencing 240 * {@code id}, or a annotated tag that peels to {@code id}. 241 * @throws java.io.IOException 242 * if references cannot be read. 243 */ 244 public boolean hasId(AnyObjectId id) throws IOException { 245 try (RefCursor rc = byObjectId(id)) { 246 return rc.next(); 247 } 248 } 249 250 /** 251 * Resolve a symbolic reference to populate its value. 252 * 253 * @param symref 254 * reference to resolve. 255 * @return resolved {@code symref}, or {@code null}. 256 * @throws java.io.IOException 257 * if references cannot be read. 258 */ 259 @Nullable 260 public Ref resolve(Ref symref) throws IOException { 261 return resolve(symref, 0); 262 } 263 264 private Ref resolve(Ref ref, int depth) throws IOException { 265 if (!ref.isSymbolic()) { 266 return ref; 267 } 268 269 Ref dst = ref.getTarget(); 270 if (MAX_SYMBOLIC_REF_DEPTH <= depth) { 271 return null; // claim it doesn't exist 272 } 273 274 dst = exactRef(dst.getName()); 275 if (dst == null) { 276 return ref; 277 } 278 279 dst = resolve(dst, depth + 1); 280 if (dst == null) { 281 return null; // claim it doesn't exist 282 } 283 return new SymbolicRef(ref.getName(), dst); 284 } 285 286 /** {@inheritDoc} */ 287 @Override 288 public abstract void close() throws IOException; 289 }