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.transport;
45
46 import static java.nio.charset.StandardCharsets.UTF_8;
47 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
48 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SYMREF;
49
50 import java.io.IOException;
51 import java.nio.ByteBuffer;
52 import java.nio.CharBuffer;
53 import java.nio.charset.CharacterCodingException;
54 import java.nio.charset.CharsetEncoder;
55 import java.nio.charset.CoderResult;
56 import java.util.Collection;
57 import java.util.HashMap;
58 import java.util.HashSet;
59 import java.util.LinkedHashSet;
60 import java.util.Map;
61 import java.util.Set;
62
63 import org.eclipse.jgit.lib.AnyObjectId;
64 import org.eclipse.jgit.lib.Constants;
65 import org.eclipse.jgit.lib.ObjectId;
66 import org.eclipse.jgit.lib.Ref;
67 import org.eclipse.jgit.lib.RefComparator;
68 import org.eclipse.jgit.lib.Repository;
69
70
71
72
73
74 public abstract class RefAdvertiser {
75
76 public static class PacketLineOutRefAdvertiser extends RefAdvertiser {
77 private final CharsetEncoder utf8 = UTF_8.newEncoder();
78 private final PacketLineOut pckOut;
79
80 private byte[] binArr = new byte[256];
81 private ByteBuffer binBuf = ByteBuffer.wrap(binArr);
82
83 private char[] chArr = new char[256];
84 private CharBuffer chBuf = CharBuffer.wrap(chArr);
85
86
87
88
89
90
91
92 public PacketLineOutRefAdvertiser(PacketLineOut out) {
93 pckOut = out;
94 }
95
96 @Override
97 public void advertiseId(AnyObjectId id, String refName)
98 throws IOException {
99 id.copyTo(binArr, 0);
100 binArr[OBJECT_ID_STRING_LENGTH] = ' ';
101 binBuf.position(OBJECT_ID_STRING_LENGTH + 1);
102 append(refName);
103 if (first) {
104 first = false;
105 if (!capablities.isEmpty()) {
106 append('\0');
107 for (String cap : capablities) {
108 append(' ');
109 append(cap);
110 }
111 }
112 }
113 append('\n');
114 pckOut.writePacket(binArr, 0, binBuf.position());
115 }
116
117 private void append(String str) throws CharacterCodingException {
118 int n = str.length();
119 if (n > chArr.length) {
120 chArr = new char[n + 256];
121 chBuf = CharBuffer.wrap(chArr);
122 }
123 str.getChars(0, n, chArr, 0);
124 chBuf.position(0).limit(n);
125 utf8.reset();
126 for (;;) {
127 CoderResult cr = utf8.encode(chBuf, binBuf, true);
128 if (cr.isOverflow()) {
129 grow();
130 } else if (cr.isUnderflow()) {
131 break;
132 } else {
133 cr.throwException();
134 }
135 }
136 }
137
138 private void append(int b) {
139 if (!binBuf.hasRemaining()) {
140 grow();
141 }
142 binBuf.put((byte) b);
143 }
144
145 private void grow() {
146 int cnt = binBuf.position();
147 byte[] tmp = new byte[binArr.length << 1];
148 System.arraycopy(binArr, 0, tmp, 0, cnt);
149 binArr = tmp;
150 binBuf = ByteBuffer.wrap(binArr);
151 binBuf.position(cnt);
152 }
153
154 @Override
155 protected void writeOne(CharSequence line) throws IOException {
156 pckOut.writeString(line.toString());
157 }
158
159 @Override
160 protected void end() throws IOException {
161 pckOut.end();
162 }
163 }
164
165 private final StringBuilder tmpLine = new StringBuilder(100);
166
167 private final char[] tmpId = new char[Constants.OBJECT_ID_STRING_LENGTH];
168
169 final Set<String> capablities = new LinkedHashSet<>();
170
171 private final Set<ObjectId> sent = new HashSet<>();
172
173 private Repository repository;
174
175 private boolean derefTags;
176
177 boolean first = true;
178
179 private boolean useProtocolV2;
180
181
182 private final Map<String, String> symrefs = new HashMap<>();
183
184
185
186
187
188
189
190 public void init(Repository src) {
191 repository = src;
192 }
193
194
195
196
197
198
199
200 public void setUseProtocolV2(boolean b) {
201 useProtocolV2 = b;
202 }
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217 public void setDerefTags(boolean deref) {
218 derefTags = deref;
219 }
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236 public void advertiseCapability(String name) {
237 capablities.add(name);
238 }
239
240
241
242
243
244
245
246
247
248
249 public void advertiseCapability(String name, String value) {
250 if (value != null) {
251 capablities.add(name + '=' + value);
252 }
253 }
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270 public void addSymref(String from, String to) {
271 if (useProtocolV2) {
272 symrefs.put(from, to);
273 } else {
274 advertiseCapability(OPTION_SYMREF, from + ':' + to);
275 }
276 }
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291 @Deprecated
292 public Set<ObjectId> send(Map<String, Ref> refs) throws IOException {
293 return send(refs.values());
294 }
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309 public Set<ObjectId> send(Collection<Ref> refs) throws IOException {
310 for (Ref ref : RefComparator.sort(refs)) {
311
312
313 ObjectId objectId = ref.getObjectId();
314 if (objectId == null) {
315 continue;
316 }
317
318 if (useProtocolV2) {
319 String symrefPart = symrefs.containsKey(ref.getName())
320 ? (" symref-target:" + symrefs.get(ref.getName()))
321 : "";
322 String peelPart = "";
323 if (derefTags) {
324 if (!ref.isPeeled() && repository != null) {
325 ref = repository.getRefDatabase().peel(ref);
326 }
327 ObjectId peeledObjectId = ref.getPeeledObjectId();
328 if (peeledObjectId != null) {
329 peelPart = " peeled:" + peeledObjectId.getName();
330 }
331 }
332 writeOne(objectId.getName() + " " + ref.getName() + symrefPart
333 + peelPart + "\n");
334 continue;
335 }
336
337 advertiseAny(objectId, ref.getName());
338
339 if (!derefTags)
340 continue;
341
342 if (!ref.isPeeled()) {
343 if (repository == null)
344 continue;
345 ref = repository.getRefDatabase().peel(ref);
346 }
347
348 if (ref.getPeeledObjectId() != null)
349 advertiseAny(ref.getPeeledObjectId(), ref.getName() + "^{}");
350 }
351 return sent;
352 }
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368 public void advertiseHave(AnyObjectId id) throws IOException {
369 advertiseAnyOnce(id, ".have");
370 }
371
372
373
374
375
376
377 public boolean isEmpty() {
378 return first;
379 }
380
381 private void advertiseAnyOnce(AnyObjectId obj, String refName)
382 throws IOException {
383 if (!sent.contains(obj))
384 advertiseAny(obj, refName);
385 }
386
387 private void advertiseAny(AnyObjectId obj, String refName)
388 throws IOException {
389 sent.add(obj.toObjectId());
390 advertiseId(obj, refName);
391 }
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408 public void advertiseId(AnyObjectId id, String refName)
409 throws IOException {
410 tmpLine.setLength(0);
411 id.copyTo(tmpId, tmpLine);
412 tmpLine.append(' ');
413 tmpLine.append(refName);
414 if (first) {
415 first = false;
416 if (!capablities.isEmpty()) {
417 tmpLine.append('\0');
418 for (String capName : capablities) {
419 tmpLine.append(' ');
420 tmpLine.append(capName);
421 }
422 tmpLine.append(' ');
423 }
424 }
425 tmpLine.append('\n');
426 writeOne(tmpLine);
427 }
428
429
430
431
432
433
434
435
436
437
438
439 protected abstract void writeOne(CharSequence line) throws IOException;
440
441
442
443
444
445
446
447
448 protected abstract void end() throws IOException;
449 }