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
172 MergedRefCursor() {
173 queue = new PriorityQueue<>(queueSize(), RefQueueEntry::compare);
174 }
175
176 void add(RefQueueEntry t) throws IOException {
177
178
179
180
181 if (!t.rc.next()) {
182 t.rc.close();
183 } else if (head == null) {
184 RefQueueEntry p = queue.peek();
185 if (p == null || RefQueueEntry.compare(t, p) < 0) {
186 head = t;
187 } else {
188 head = queue.poll();
189 queue.add(t);
190 }
191 } else if (RefQueueEntry.compare(t, head) > 0) {
192 queue.add(t);
193 } else {
194 queue.add(head);
195 head = t;
196 }
197 }
198
199 @Override
200 public boolean next() throws IOException {
201 for (;;) {
202 RefQueueEntry t = poll();
203 if (t == null) {
204 return false;
205 }
206
207 ref = t.rc.getRef();
208 boolean include = includeDeletes || !t.rc.wasDeleted();
209 add(t);
210 skipShadowedRefs(ref.getName());
211 if (include) {
212 return true;
213 }
214 }
215 }
216
217 private RefQueueEntry poll() {
218 RefQueueEntry e = head;
219 if (e != null) {
220 head = null;
221 return e;
222 }
223 return queue.poll();
224 }
225
226 private void skipShadowedRefs(String name) throws IOException {
227 for (;;) {
228 RefQueueEntry t = head != null ? head : queue.peek();
229 if (t != null && name.equals(t.name())) {
230 add(poll());
231 } else {
232 break;
233 }
234 }
235 }
236
237 @Override
238 public Ref getRef() {
239 return ref;
240 }
241
242 @Override
243 public void close() {
244 if (head != null) {
245 head.rc.close();
246 head = null;
247 }
248 while (!queue.isEmpty()) {
249 queue.remove().rc.close();
250 }
251 }
252 }
253
254 private static class RefQueueEntry {
255 static int compare(RefQueueEntry a, RefQueueEntry b) {
256 int cmp = a.name().compareTo(b.name());
257 if (cmp == 0) {
258
259 cmp = Long.signum(b.updateIndex() - a.updateIndex());
260 }
261 if (cmp == 0) {
262
263 cmp = b.stackIdx - a.stackIdx;
264 }
265 return cmp;
266 }
267
268 final RefCursor rc;
269 final int stackIdx;
270
271 RefQueueEntry(RefCursor rc, int stackIdx) {
272 this.rc = rc;
273 this.stackIdx = stackIdx;
274 }
275
276 String name() {
277 return rc.getRef().getName();
278 }
279
280 long updateIndex() {
281 return rc.getRef().getUpdateIndex();
282 }
283 }
284
285 private class MergedLogCursor extends LogCursor {
286 private final PriorityQueue<LogQueueEntry> queue;
287 private String refName;
288 private long updateIndex;
289 private ReflogEntry entry;
290
291 MergedLogCursor() {
292 queue = new PriorityQueue<>(queueSize(), LogQueueEntry::compare);
293 }
294
295 void add(LogQueueEntry t) throws IOException {
296 if (t.lc.next()) {
297 queue.add(t);
298 } else {
299 t.lc.close();
300 }
301 }
302
303 @Override
304 public boolean next() throws IOException {
305 for (;;) {
306 LogQueueEntry t = queue.poll();
307 if (t == null) {
308 return false;
309 }
310
311 refName = t.lc.getRefName();
312 updateIndex = t.lc.getUpdateIndex();
313 entry = t.lc.getReflogEntry();
314 boolean include = includeDeletes || entry != null;
315 skipShadowed(refName, updateIndex);
316 add(t);
317 if (include) {
318 return true;
319 }
320 }
321 }
322
323 private void skipShadowed(String name, long index) throws IOException {
324 for (;;) {
325 LogQueueEntry t = queue.peek();
326 if (t != null && name.equals(t.name()) && index == t.index()) {
327 add(queue.remove());
328 } else {
329 break;
330 }
331 }
332 }
333
334 @Override
335 public String getRefName() {
336 return refName;
337 }
338
339 @Override
340 public long getUpdateIndex() {
341 return updateIndex;
342 }
343
344 @Override
345 public ReflogEntry getReflogEntry() {
346 return entry;
347 }
348
349 @Override
350 public void close() {
351 while (!queue.isEmpty()) {
352 queue.remove().lc.close();
353 }
354 }
355 }
356
357 private static class LogQueueEntry {
358 static int compare(LogQueueEntry a, LogQueueEntry b) {
359 int cmp = a.name().compareTo(b.name());
360 if (cmp == 0) {
361
362 cmp = Long.signum(b.index() - a.index());
363 }
364 if (cmp == 0) {
365
366 cmp = b.stackIdx - a.stackIdx;
367 }
368 return cmp;
369 }
370
371 final LogCursor lc;
372 final int stackIdx;
373
374 LogQueueEntry(LogCursor lc, int stackIdx) {
375 this.lc = lc;
376 this.stackIdx = stackIdx;
377 }
378
379 String name() {
380 return lc.getRefName();
381 }
382
383 long index() {
384 return lc.getUpdateIndex();
385 }
386 }
387 }