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 package org.eclipse.jgit.internal.storage.reftable;
45
46 import java.io.IOException;
47 import java.util.List;
48 import java.util.PriorityQueue;
49
50 import org.eclipse.jgit.lib.AnyObjectId;
51 import org.eclipse.jgit.lib.Ref;
52 import org.eclipse.jgit.lib.ReflogEntry;
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 public class MergedReftable extends Reftable {
70 private final Reftable[] tables;
71
72
73
74
75
76
77
78
79
80
81
82
83
84 public MergedReftable(List<Reftable> tableStack) {
85 tables = tableStack.toArray(new Reftable[0]);
86
87
88
89 for (Reftable t : tables) {
90 t.setIncludeDeletes(true);
91 }
92 }
93
94
95 @Override
96 public RefCursor allRefs() throws IOException {
97 MergedRefCursor m = new MergedRefCursor();
98 for (int i = 0; i < tables.length; i++) {
99 m.add(new RefQueueEntry(tables[i].allRefs(), i));
100 }
101 return m;
102 }
103
104
105 @Override
106 public RefCursor seekRef(String name) throws IOException {
107 MergedRefCursor m = new MergedRefCursor();
108 for (int i = 0; i < tables.length; i++) {
109 m.add(new RefQueueEntry(tables[i].seekRef(name), i));
110 }
111 return m;
112 }
113
114
115 @Override
116 public RefCursor seekRefsWithPrefix(String prefix) throws IOException {
117 MergedRefCursor m = new MergedRefCursor();
118 for (int i = 0; i < tables.length; i++) {
119 m.add(new RefQueueEntry(tables[i].seekRefsWithPrefix(prefix), i));
120 }
121 return m;
122 }
123
124
125 @Override
126 public RefCursor byObjectId(AnyObjectId name) throws IOException {
127 MergedRefCursor m = new MergedRefCursor();
128 for (int i = 0; i < tables.length; i++) {
129 m.add(new RefQueueEntry(tables[i].byObjectId(name), i));
130 }
131 return m;
132 }
133
134
135 @Override
136 public LogCursor allLogs() throws IOException {
137 MergedLogCursor m = new MergedLogCursor();
138 for (int i = 0; i < tables.length; i++) {
139 m.add(new LogQueueEntry(tables[i].allLogs(), i));
140 }
141 return m;
142 }
143
144
145 @Override
146 public LogCursor seekLog(String refName, long updateIdx)
147 throws IOException {
148 MergedLogCursor m = new MergedLogCursor();
149 for (int i = 0; i < tables.length; i++) {
150 m.add(new LogQueueEntry(tables[i].seekLog(refName, updateIdx), i));
151 }
152 return m;
153 }
154
155
156 @Override
157 public void close() throws IOException {
158 for (Reftable t : tables) {
159 t.close();
160 }
161 }
162
163 int queueSize() {
164 return Math.max(1, tables.length);
165 }
166
167 private class MergedRefCursor extends RefCursor {
168 private final PriorityQueue<RefQueueEntry> queue;
169 private RefQueueEntry head;
170 private Ref ref;
171 private long updateIndex;
172
173 MergedRefCursor() {
174 queue = new PriorityQueue<>(queueSize(), RefQueueEntry::compare);
175 }
176
177 void add(RefQueueEntry t) throws IOException {
178
179
180
181
182 if (!t.rc.next()) {
183 t.rc.close();
184 } else if (head == null) {
185 RefQueueEntry p = queue.peek();
186 if (p == null || RefQueueEntry.compare(t, p) < 0) {
187 head = t;
188 } else {
189 head = queue.poll();
190 queue.add(t);
191 }
192 } else if (RefQueueEntry.compare(t, head) > 0) {
193 queue.add(t);
194 } else {
195 queue.add(head);
196 head = t;
197 }
198 }
199
200 @Override
201 public boolean next() throws IOException {
202 for (;;) {
203 RefQueueEntry t = poll();
204 if (t == null) {
205 return false;
206 }
207
208 ref = t.rc.getRef();
209 updateIndex = t.rc.getUpdateIndex();
210 boolean include = includeDeletes || !t.rc.wasDeleted();
211 add(t);
212 skipShadowedRefs(ref.getName());
213 if (include) {
214 return true;
215 }
216 }
217 }
218
219 private RefQueueEntry poll() {
220 RefQueueEntry e = head;
221 if (e != null) {
222 head = null;
223 return e;
224 }
225 return queue.poll();
226 }
227
228 private void skipShadowedRefs(String name) throws IOException {
229 for (;;) {
230 RefQueueEntry t = head != null ? head : queue.peek();
231 if (t != null && name.equals(t.name())) {
232 add(poll());
233 } else {
234 break;
235 }
236 }
237 }
238
239 @Override
240 public Ref getRef() {
241 return ref;
242 }
243
244 @Override
245 public long getUpdateIndex() {
246 return updateIndex;
247 }
248
249 @Override
250 public void close() {
251 if (head != null) {
252 head.rc.close();
253 head = null;
254 }
255 while (!queue.isEmpty()) {
256 queue.remove().rc.close();
257 }
258 }
259 }
260
261 private static class RefQueueEntry {
262 static int compare(RefQueueEntry a, RefQueueEntry b) {
263 int cmp = a.name().compareTo(b.name());
264 if (cmp == 0) {
265
266 cmp = Long.signum(b.updateIndex() - a.updateIndex());
267 }
268 if (cmp == 0) {
269
270 cmp = b.stackIdx - a.stackIdx;
271 }
272 return cmp;
273 }
274
275 final RefCursor rc;
276 final int stackIdx;
277
278 RefQueueEntry(RefCursor rc, int stackIdx) {
279 this.rc = rc;
280 this.stackIdx = stackIdx;
281 }
282
283 String name() {
284 return rc.getRef().getName();
285 }
286
287 long updateIndex() {
288 return rc.getUpdateIndex();
289 }
290 }
291
292 private class MergedLogCursor extends LogCursor {
293 private final PriorityQueue<LogQueueEntry> queue;
294 private String refName;
295 private long updateIndex;
296 private ReflogEntry entry;
297
298 MergedLogCursor() {
299 queue = new PriorityQueue<>(queueSize(), LogQueueEntry::compare);
300 }
301
302 void add(LogQueueEntry t) throws IOException {
303 if (t.lc.next()) {
304 queue.add(t);
305 } else {
306 t.lc.close();
307 }
308 }
309
310 @Override
311 public boolean next() throws IOException {
312 for (;;) {
313 LogQueueEntry t = queue.poll();
314 if (t == null) {
315 return false;
316 }
317
318 refName = t.lc.getRefName();
319 updateIndex = t.lc.getUpdateIndex();
320 entry = t.lc.getReflogEntry();
321 boolean include = includeDeletes || entry != null;
322 skipShadowed(refName, updateIndex);
323 add(t);
324 if (include) {
325 return true;
326 }
327 }
328 }
329
330 private void skipShadowed(String name, long index) throws IOException {
331 for (;;) {
332 LogQueueEntry t = queue.peek();
333 if (t != null && name.equals(t.name()) && index == t.index()) {
334 add(queue.remove());
335 } else {
336 break;
337 }
338 }
339 }
340
341 @Override
342 public String getRefName() {
343 return refName;
344 }
345
346 @Override
347 public long getUpdateIndex() {
348 return updateIndex;
349 }
350
351 @Override
352 public ReflogEntry getReflogEntry() {
353 return entry;
354 }
355
356 @Override
357 public void close() {
358 while (!queue.isEmpty()) {
359 queue.remove().lc.close();
360 }
361 }
362 }
363
364 private static class LogQueueEntry {
365 static int compare(LogQueueEntry a, LogQueueEntry b) {
366 int cmp = a.name().compareTo(b.name());
367 if (cmp == 0) {
368
369 cmp = Long.signum(b.index() - a.index());
370 }
371 if (cmp == 0) {
372
373 cmp = b.stackIdx - a.stackIdx;
374 }
375 return cmp;
376 }
377
378 final LogCursor lc;
379 final int stackIdx;
380
381 LogQueueEntry(LogCursor lc, int stackIdx) {
382 this.lc = lc;
383 this.stackIdx = stackIdx;
384 }
385
386 String name() {
387 return lc.getRefName();
388 }
389
390 long index() {
391 return lc.getUpdateIndex();
392 }
393 }
394 }