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.reftree;
45
46 import static org.eclipse.jgit.lib.Constants.HEAD;
47 import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
48 import static org.eclipse.jgit.lib.Constants.R_REFS;
49 import static org.eclipse.jgit.lib.Constants.encode;
50 import static org.eclipse.jgit.lib.FileMode.GITLINK;
51 import static org.eclipse.jgit.lib.FileMode.SYMLINK;
52 import static org.eclipse.jgit.lib.FileMode.TYPE_GITLINK;
53 import static org.eclipse.jgit.lib.FileMode.TYPE_SYMLINK;
54 import static org.eclipse.jgit.lib.Ref.Storage.NEW;
55 import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
56 import static org.eclipse.jgit.lib.RefDatabase.MAX_SYMBOLIC_REF_DEPTH;
57 import static org.eclipse.jgit.transport.ReceiveCommand.Result.LOCK_FAILURE;
58 import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
59
60 import java.io.IOException;
61 import java.util.Collection;
62 import java.util.HashMap;
63 import java.util.Map;
64
65 import org.eclipse.jgit.annotations.Nullable;
66 import org.eclipse.jgit.dircache.DirCache;
67 import org.eclipse.jgit.dircache.DirCacheBuilder;
68 import org.eclipse.jgit.dircache.DirCacheEditor;
69 import org.eclipse.jgit.dircache.DirCacheEditor.DeletePath;
70 import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
71 import org.eclipse.jgit.dircache.DirCacheEntry;
72 import org.eclipse.jgit.errors.CorruptObjectException;
73 import org.eclipse.jgit.errors.DirCacheNameConflictException;
74 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
75 import org.eclipse.jgit.errors.MissingObjectException;
76 import org.eclipse.jgit.internal.JGitText;
77 import org.eclipse.jgit.lib.ObjectId;
78 import org.eclipse.jgit.lib.ObjectIdRef;
79 import org.eclipse.jgit.lib.ObjectInserter;
80 import org.eclipse.jgit.lib.ObjectReader;
81 import org.eclipse.jgit.lib.Ref;
82 import org.eclipse.jgit.lib.Repository;
83 import org.eclipse.jgit.lib.SymbolicRef;
84 import org.eclipse.jgit.revwalk.RevTree;
85 import org.eclipse.jgit.util.RawParseUtils;
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109 public class RefTree {
110
111 public static final String PEELED_SUFFIX = " ^";
112 static final String ROOT_DOTDOT = "..";
113
114
115
116
117
118
119 public static RefTree newEmptyTree() {
120 return new RefTree(DirCache.newInCore());
121 }
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140 public static RefTree read(ObjectReader reader, RevTree tree)
141 throws MissingObjectException, IncorrectObjectTypeException,
142 CorruptObjectException, IOException {
143 return new RefTree(DirCache.read(reader, tree));
144 }
145
146 private DirCache contents;
147 private Map<ObjectId, String> pendingBlobs;
148
149 private RefTree(DirCache dc) {
150 this.contents = dc;
151 }
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174 @Nullable
175 public Ref exactRef(ObjectReader reader, String name) throws IOException {
176 Ref r = readRef(reader, name);
177 if (r == null) {
178 return null;
179 } else if (r.isSymbolic()) {
180 return resolve(reader, r, 0);
181 }
182
183 DirCacheEntry p = contents.getEntry(peeledPath(name));
184 if (p != null && p.getRawMode() == TYPE_GITLINK) {
185 return new ObjectIdRef.PeeledTag(PACKED, r.getName(),
186 r.getObjectId(), p.getObjectId());
187 }
188 return r;
189 }
190
191 private Ref readRef(ObjectReader reader, String name) throws IOException {
192 DirCacheEntry e = contents.getEntry(refPath(name));
193 return e != null ? toRef(reader, e, name) : null;
194 }
195
196 private Ref toRef(ObjectReader reader, DirCacheEntry e, String name)
197 throws IOException {
198 int mode = e.getRawMode();
199 if (mode == TYPE_GITLINK) {
200 ObjectId id = e.getObjectId();
201 return new ObjectIdRef.PeeledNonTag(PACKED, name, id);
202 }
203
204 if (mode == TYPE_SYMLINK) {
205 ObjectId id = e.getObjectId();
206 String n = pendingBlobs != null ? pendingBlobs.get(id) : null;
207 if (n == null) {
208 byte[] bin = reader.open(id, OBJ_BLOB).getCachedBytes();
209 n = RawParseUtils.decode(bin);
210 }
211 Ref dst = new ObjectIdRef.Unpeeled(NEW, n, null);
212 return new SymbolicRef(name, dst);
213 }
214
215 return null;
216 }
217
218 private Ref./org/eclipse/jgit/lib/Ref.html#Ref">Ref resolve(ObjectReader reader, Ref ref, int depth)
219 throws IOException {
220 if (ref.isSymbolic() && depth < MAX_SYMBOLIC_REF_DEPTH) {
221 Ref r = readRef(reader, ref.getTarget().getName());
222 if (r == null) {
223 return ref;
224 }
225 Ref dst = resolve(reader, r, depth + 1);
226 return new SymbolicRef(ref.getName(), dst);
227 }
228 return ref;
229 }
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245 public boolean apply(Collection<Command> cmdList) {
246 try {
247 DirCacheEditor ed = contents.editor();
248 for (Command cmd : cmdList) {
249 if (!isValidRef(cmd)) {
250 cmd.setResult(REJECTED_OTHER_REASON,
251 JGitText.get().funnyRefname);
252 Command.abort(cmdList, null);
253 return false;
254 }
255 apply(ed, cmd);
256 }
257 ed.finish();
258 return true;
259 } catch (DirCacheNameConflictException e) {
260 String r1 = refName(e.getPath1());
261 String r2 = refName(e.getPath2());
262 for (Command cmd : cmdList) {
263 if (r1.equals(cmd.getRefName())
264 || r2.equals(cmd.getRefName())) {
265 cmd.setResult(LOCK_FAILURE);
266 break;
267 }
268 }
269 Command.abort(cmdList, null);
270 return false;
271 } catch (LockFailureException e) {
272 Command.abort(cmdList, null);
273 return false;
274 }
275 }
276
277 private static boolean isValidRef(Command cmd) {
278 String n = cmd.getRefName();
279 return HEAD.equals(n) || Repository.isValidRefName(n);
280 }
281
282 private void apply(DirCacheEditor ed, Command cmd) {
283 String path = refPath(cmd.getRefName());
284 Ref oldRef = cmd.getOldRef();
285 final Ref newRef = cmd.getNewRef();
286
287 if (newRef == null) {
288 checkRef(contents.getEntry(path), cmd);
289 ed.add(new DeletePath(path));
290 cleanupPeeledRef(ed, oldRef);
291 return;
292 }
293
294 if (newRef.isSymbolic()) {
295 final String dst = newRef.getTarget().getName();
296 ed.add(new PathEdit(path) {
297 @Override
298 public void apply(DirCacheEntry ent) {
299 checkRef(ent, cmd);
300 ObjectId id = Command.symref(dst);
301 ent.setFileMode(SYMLINK);
302 ent.setObjectId(id);
303 if (pendingBlobs == null) {
304 pendingBlobs = new HashMap<>(4);
305 }
306 pendingBlobs.put(id, dst);
307 }
308 }.setReplace(false));
309 cleanupPeeledRef(ed, oldRef);
310 return;
311 }
312
313 ed.add(new PathEdit(path) {
314 @Override
315 public void apply(DirCacheEntry ent) {
316 checkRef(ent, cmd);
317 ent.setFileMode(GITLINK);
318 ent.setObjectId(newRef.getObjectId());
319 }
320 }.setReplace(false));
321
322 if (newRef.getPeeledObjectId() != null) {
323 ed.add(new PathEdit(peeledPath(newRef.getName())) {
324 @Override
325 public void apply(DirCacheEntry ent) {
326 ent.setFileMode(GITLINK);
327 ent.setObjectId(newRef.getPeeledObjectId());
328 }
329 }.setReplace(false));
330 } else {
331 cleanupPeeledRef(ed, oldRef);
332 }
333 }
334
335 private static void checkRef(@Nullable DirCacheEntry ent, Command cmd) {
336 if (!cmd.checkRef(ent)) {
337 cmd.setResult(LOCK_FAILURE);
338 throw new LockFailureException();
339 }
340 }
341
342 private static void cleanupPeeledRef(DirCacheEditor ed, Ref ref) {
343 if (ref != null && !ref.isSymbolic()
344 && (!ref.isPeeled() || ref.getPeeledObjectId() != null)) {
345 ed.add(new DeletePath(peeledPath(ref.getName())));
346 }
347 }
348
349
350
351
352
353
354
355
356
357 public static String refName(String path) {
358 if (path.startsWith(ROOT_DOTDOT)) {
359 return path.substring(2);
360 }
361 return R_REFS + path;
362 }
363
364 static String refPath(String name) {
365 if (name.startsWith(R_REFS)) {
366 return name.substring(R_REFS.length());
367 }
368 return ROOT_DOTDOT + name;
369 }
370
371 private static String peeledPath(String name) {
372 return refPath(name) + PEELED_SUFFIX;
373 }
374
375
376
377
378
379
380
381
382
383
384
385
386 public ObjectId writeTree(ObjectInserter inserter) throws IOException {
387 if (pendingBlobs != null) {
388 for (String s : pendingBlobs.values()) {
389 inserter.insert(OBJ_BLOB, encode(s));
390 }
391 pendingBlobs = null;
392 }
393 return contents.writeTree(inserter);
394 }
395
396
397
398
399
400
401 public RefTree copy() {
402 RefTree r = new RefTree(DirCache.newInCore());
403 DirCacheBuilder b = r.contents.builder();
404 for (int i = 0; i < contents.getEntryCount(); i++) {
405 b.add(new DirCacheEntry(contents.getEntry(i)));
406 }
407 b.finish();
408 if (pendingBlobs != null) {
409 r.pendingBlobs = new HashMap<>(pendingBlobs);
410 }
411 return r;
412 }
413
414 private static class LockFailureException extends RuntimeException {
415 private static final long serialVersionUID = 1L;
416 }
417 }