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 }