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