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 either to a reference, or a reference subtree. 112 * <p> 113 * If {@code refName} ends with {@code "/"} the method will seek to the 114 * subtree of all references starting with {@code refName} as a prefix. If 115 * no references start with this prefix, an empty cursor is returned. 116 * <p> 117 * Otherwise exactly {@code refName} will be looked for. If present, the 118 * returned cursor will iterate exactly one entry. If not found, an empty 119 * cursor is returned. 120 * 121 * @param refName 122 * reference name or subtree 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 seekRef(String refName) 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 * Seek reader to read log records. 142 * 143 * @return cursor to iterate; empty cursor if no logs are present. 144 * @throws java.io.IOException 145 * if logs cannot be read. 146 */ 147 public abstract LogCursor allLogs() throws IOException; 148 149 /** 150 * Read a single reference's log. 151 * 152 * @param refName 153 * exact name of the reference whose log to read. 154 * @return cursor to iterate; empty cursor if no logs match. 155 * @throws java.io.IOException 156 * if logs cannot be read. 157 */ 158 public LogCursor seekLog(String refName) throws IOException { 159 return seekLog(refName, Long.MAX_VALUE); 160 } 161 162 /** 163 * Seek to an update index in a reference's log. 164 * 165 * @param refName 166 * exact name of the reference whose log to read. 167 * @param updateIndex 168 * most recent index to return first in the log cursor. Log 169 * records at or before {@code updateIndex} will be returned. 170 * @return cursor to iterate; empty cursor if no logs match. 171 * @throws java.io.IOException 172 * if logs cannot be read. 173 */ 174 public abstract LogCursor seekLog(String refName, long updateIndex) 175 throws IOException; 176 177 /** 178 * Lookup a reference, or null if not found. 179 * 180 * @param refName 181 * reference name to find. 182 * @return the reference, or {@code null} if not found. 183 * @throws java.io.IOException 184 * if references cannot be read. 185 */ 186 @Nullable 187 public Ref exactRef(String refName) throws IOException { 188 try (RefCursor rc = seekRef(refName)) { 189 return rc.next() ? rc.getRef() : null; 190 } 191 } 192 193 /** 194 * Test if a reference or reference subtree exists. 195 * <p> 196 * If {@code refName} ends with {@code "/"}, the method tests if any 197 * reference starts with {@code refName} as a prefix. 198 * <p> 199 * Otherwise, the method checks if {@code refName} exists. 200 * 201 * @param refName 202 * reference name or subtree to find. 203 * @return {@code true} if the reference exists, or at least one reference 204 * exists in the subtree. 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 directly refers to the object. 216 * 217 * @param id 218 * ObjectId to find. 219 * @return {@code true} if any reference exists directly referencing 220 * {@code id}, or a annotated tag that peels to {@code id}. 221 * @throws java.io.IOException 222 * if references cannot be read. 223 */ 224 public boolean hasId(AnyObjectId id) throws IOException { 225 try (RefCursor rc = byObjectId(id)) { 226 return rc.next(); 227 } 228 } 229 230 /** 231 * Resolve a symbolic reference to populate its value. 232 * 233 * @param symref 234 * reference to resolve. 235 * @return resolved {@code symref}, or {@code null}. 236 * @throws java.io.IOException 237 * if references cannot be read. 238 */ 239 @Nullable 240 public Ref resolve(Ref symref) throws IOException { 241 return resolve(symref, 0); 242 } 243 244 private Ref resolve(Ref ref, int depth) throws IOException { 245 if (!ref.isSymbolic()) { 246 return ref; 247 } 248 249 Ref dst = ref.getTarget(); 250 if (MAX_SYMBOLIC_REF_DEPTH <= depth) { 251 return null; // claim it doesn't exist 252 } 253 254 dst = exactRef(dst.getName()); 255 if (dst == null) { 256 return ref; 257 } 258 259 dst = resolve(dst, depth + 1); 260 if (dst == null) { 261 return null; // claim it doesn't exist 262 } 263 return new SymbolicRef(ref.getName(), dst); 264 } 265 266 /** {@inheritDoc} */ 267 @Override 268 public abstract void close() throws IOException; 269 }