aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/Reftable.java
blob: 882e5377348a3cb341e885e446f55be3d6f3197b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
/*
 * Copyright (C) 2017, Google Inc. and others
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Distribution License v. 1.0 which is available at
 * https://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

package org.eclipse.jgit.internal.storage.reftable;

import static org.eclipse.jgit.lib.RefDatabase.MAX_SYMBOLIC_REF_DEPTH;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collection;

import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.internal.storage.io.BlockSource;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.SymbolicRef;

/**
 * Abstract table of references.
 */
public abstract class Reftable {
	/**
	 * References to convert into a reftable
	 *
	 * @param refs
	 *            references to convert into a reftable; may be empty.
	 * @return a reader for the supplied references.
	 */
	public static Reftable from(Collection<Ref> refs) {
		try {
			ReftableConfig cfg = new ReftableConfig();
			cfg.setIndexObjects(false);
			cfg.setAlignBlocks(false);
			ByteArrayOutputStream buf = new ByteArrayOutputStream();
			new ReftableWriter(buf)
				.setConfig(cfg)
				.begin()
				.sortAndWriteRefs(refs)
				.finish();
			return new ReftableReader(BlockSource.from(buf.toByteArray()));
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	/** {@code true} if deletions should be included in results. */
	protected boolean includeDeletes;

	/**
	 * Whether deleted references will be returned.
	 *
	 * @param deletes
	 *            if {@code true} deleted references will be returned. If
	 *            {@code false} (default behavior), deleted references will be
	 *            skipped, and not returned.
	 */
	public void setIncludeDeletes(boolean deletes) {
		includeDeletes = deletes;
	}

	/**
	 * Get the maximum update index for ref entries that appear in this
	 * reftable.
	 *
	 * @return the maximum update index for ref entries that appear in this
	 *         reftable.
	 * @throws java.io.IOException
	 *             file cannot be read.
	 */
	public abstract long maxUpdateIndex() throws IOException;

	/**
	 * Get the minimum update index for ref entries that appear in this
	 * reftable.
	 *
	 * @return the minimum update index for ref entries that appear in this
	 *         reftable.
	 * @throws java.io.IOException
	 *             file cannot be read.
	 */
	public abstract long minUpdateIndex() throws IOException;

	/**
	 * Seek to the first reference, to iterate in order.
	 *
	 * @return cursor to iterate.
	 * @throws java.io.IOException
	 *             if references cannot be read.
	 */
	public abstract RefCursor allRefs() throws IOException;

	/**
	 * Seek to a reference.
	 * <p>
	 * This method will seek to the reference {@code refName}. If present, the
	 * returned cursor will iterate exactly one entry. If not found, an empty
	 * cursor is returned.
	 *
	 * @param refName
	 *            reference name.
	 * @return cursor to iterate; empty cursor if no references match.
	 * @throws java.io.IOException
	 *             if references cannot be read.
	 */
	public abstract RefCursor seekRef(String refName) throws IOException;

	/**
	 * Seek references with prefix.
	 * <p>
	 * The method will seek all the references starting with {@code prefix} as a
	 * prefix. If no references start with this prefix, an empty cursor is
	 * returned.
	 *
	 * @param prefix
	 *            prefix to find.
	 * @return cursor to iterate; empty cursor if no references match.
	 * @throws java.io.IOException
	 *             if references cannot be read.
	 */
	public abstract RefCursor seekRefsWithPrefix(String prefix) throws IOException;

	/**
	 * Match references pointing to a specific object.
	 *
	 * @param id
	 *            object to find.
	 * @return cursor to iterate; empty cursor if no references match.
	 * @throws java.io.IOException
	 *             if references cannot be read.
	 */
	public abstract RefCursor byObjectId(AnyObjectId id) throws IOException;

	/**
	 * Whether this reftable can do a fast SHA1 =&gt; ref lookup
	 *
	 * @return whether this reftable can do a fast SHA1 =&gt; ref lookup.
	 * @throws IOException
	 *             on I/O problems.
	 */
	public abstract boolean hasObjectMap() throws IOException;

	/**
	 * Seek reader to read log records.
	 *
	 * @return cursor to iterate; empty cursor if no logs are present.
	 * @throws java.io.IOException
	 *             if logs cannot be read.
	 */
	public abstract LogCursor allLogs() throws IOException;

	/**
	 * Read a single reference's log.
	 *
	 * @param refName
	 *            exact name of the reference whose log to read.
	 * @return cursor to iterate; empty cursor if no logs match.
	 * @throws java.io.IOException
	 *             if logs cannot be read.
	 */
	public LogCursor seekLog(String refName) throws IOException {
		return seekLog(refName, Long.MAX_VALUE);
	}

	/**
	 * Seek to an update index in a reference's log.
	 *
	 * @param refName
	 *            exact name of the reference whose log to read.
	 * @param updateIndex
	 *            most recent index to return first in the log cursor. Log
	 *            records at or before {@code updateIndex} will be returned.
	 * @return cursor to iterate; empty cursor if no logs match.
	 * @throws java.io.IOException
	 *             if logs cannot be read.
	 */
	public abstract LogCursor seekLog(String refName, long updateIndex)
			throws IOException;

	/**
	 * Lookup a reference, or null if not found.
	 *
	 * @param refName
	 *            reference name to find.
	 * @return the reference, or {@code null} if not found.
	 * @throws java.io.IOException
	 *             if references cannot be read.
	 */
	@Nullable
	public Ref exactRef(String refName) throws IOException {
		try (RefCursor rc = seekRef(refName)) {
			return rc.next() ? rc.getRef() : null;
		}
	}

	/**
	 * Test if a reference exists.
	 *
	 * @param refName
	 *            reference name or subtree to find.
	 * @return {@code true} if the reference exists.
	 * @throws java.io.IOException
	 *             if references cannot be read.
	 */
	public boolean hasRef(String refName) throws IOException {
		try (RefCursor rc = seekRef(refName)) {
			return rc.next();
		}
	}

	/**
	 * Test if any reference starts with {@code prefix} as a prefix.
	 *
	 * @param prefix
	 *            prefix to find.
	 * @return {@code true} if at least one reference exists with prefix.
	 * @throws java.io.IOException
	 *             if references cannot be read.
	 */
	public boolean hasRefsWithPrefix(String prefix) throws IOException {
		try (RefCursor rc = seekRefsWithPrefix(prefix)) {
			return rc.next();
		}
	}

	/**
	 * Test if any reference directly refers to the object.
	 *
	 * @param id
	 *            ObjectId to find.
	 * @return {@code true} if any reference exists directly referencing
	 *         {@code id}, or a annotated tag that peels to {@code id}.
	 * @throws java.io.IOException
	 *             if references cannot be read.
	 */
	public boolean hasId(AnyObjectId id) throws IOException {
		try (RefCursor rc = byObjectId(id)) {
			return rc.next();
		}
	}

	/**
	 * Resolve a symbolic reference to populate its value.
	 *
	 * @param symref
	 *            reference to resolve.
	 * @return resolved {@code symref}, or {@code null}.
	 * @throws java.io.IOException
	 *             if references cannot be read.
	 */
	@Nullable
	public Ref resolve(Ref symref) throws IOException {
		return resolve(symref, 0);
	}

	private Ref resolve(Ref ref, int depth) throws IOException {
		if (!ref.isSymbolic()) {
			return ref;
		}

		Ref dst = ref.getTarget();
		if (MAX_SYMBOLIC_REF_DEPTH <= depth) {
			return null; // claim it doesn't exist
		}

		dst = exactRef(dst.getName());
		if (dst == null) {
			return ref;
		}

		dst = resolve(dst, depth + 1);
		if (dst == null) {
			return null; // claim it doesn't exist
		}
		return new SymbolicRef(ref.getName(), dst, ref.getUpdateIndex());
	}
}