1
2
3
4
5
6
7
8
9
10
11
12 package org.eclipse.jgit.internal.storage.file;
13
14 import java.io.File;
15 import java.io.IOException;
16 import java.util.Collection;
17 import java.util.HashSet;
18 import java.util.Set;
19
20 import org.eclipse.jgit.internal.storage.file.ObjectDirectory.AlternateHandle;
21 import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
22 import org.eclipse.jgit.internal.storage.pack.PackWriter;
23 import org.eclipse.jgit.lib.AbbreviatedObjectId;
24 import org.eclipse.jgit.lib.AnyObjectId;
25 import org.eclipse.jgit.lib.Config;
26 import org.eclipse.jgit.lib.Constants;
27 import org.eclipse.jgit.lib.ObjectDatabase;
28 import org.eclipse.jgit.lib.ObjectId;
29 import org.eclipse.jgit.lib.ObjectIdOwnerMap;
30 import org.eclipse.jgit.lib.ObjectLoader;
31 import org.eclipse.jgit.util.FS;
32
33
34
35
36
37
38
39 class CachedObjectDirectory extends FileObjectDatabase {
40
41
42
43
44 private ObjectIdOwnerMap<UnpackedObjectId> unpackedObjects;
45
46 private final ObjectDirectory wrapped;
47
48 private CachedObjectDirectory[] alts;
49
50
51
52
53
54
55
56 CachedObjectDirectory(ObjectDirectory wrapped) {
57 this.wrapped = wrapped;
58 this.unpackedObjects = scanLoose();
59 }
60
61 private ObjectIdOwnerMap<UnpackedObjectId> scanLoose() {
62 ObjectIdOwnerMap<UnpackedObjectId> m = new ObjectIdOwnerMap<>();
63 File objects = wrapped.getDirectory();
64 String[] fanout = objects.list();
65 if (fanout == null)
66 return m;
67 for (String d : fanout) {
68 if (d.length() != 2)
69 continue;
70 String[] entries = new File(objects, d).list();
71 if (entries == null)
72 continue;
73 for (String e : entries) {
74 if (e.length() != Constants.OBJECT_ID_STRING_LENGTH - 2)
75 continue;
76 try {
77 ObjectId id = ObjectId.fromString(d + e);
78 m.add(new UnpackedObjectId(id));
79 } catch (IllegalArgumentException notAnObject) {
80
81 }
82 }
83 }
84 return m;
85 }
86
87
88 @Override
89 public void close() {
90
91 }
92
93
94 @Override
95 public ObjectDatabase newCachedDatabase() {
96 return this;
97 }
98
99 @Override
100 File getDirectory() {
101 return wrapped.getDirectory();
102 }
103
104 @Override
105 File fileFor(AnyObjectId id) {
106 return wrapped.fileFor(id);
107 }
108
109 @Override
110 Config getConfig() {
111 return wrapped.getConfig();
112 }
113
114 @Override
115 FS getFS() {
116 return wrapped.getFS();
117 }
118
119 @Override
120 public Set<ObjectId> getShallowCommits() throws IOException {
121 return wrapped.getShallowCommits();
122 }
123
124 @Override
125 public void setShallowCommits(Set<ObjectId> shallowCommits) throws IOException {
126 wrapped.setShallowCommits(shallowCommits);
127 }
128
129 private CachedObjectDirectory[] myAlternates() {
130 if (alts == null) {
131 ObjectDirectory.AlternateHandle[] src = wrapped.myAlternates();
132 alts = new CachedObjectDirectory[src.length];
133 for (int i = 0; i < alts.length; i++)
134 alts[i] = src[i].db.newCachedFileObjectDatabase();
135 }
136 return alts;
137 }
138
139 private Set<AlternateHandle.Id> skipMe(Set<AlternateHandle.Id> skips) {
140 Set<AlternateHandle.Id> withMe = new HashSet<>();
141 if (skips != null) {
142 withMe.addAll(skips);
143 }
144 withMe.add(getAlternateId());
145 return withMe;
146 }
147
148 @Override
149 void resolve(Set<ObjectId> matches, AbbreviatedObjectId id)
150 throws IOException {
151 wrapped.resolve(matches, id);
152 }
153
154
155 @Override
156 public boolean has(AnyObjectId objectId) throws IOException {
157 return has(objectId, null);
158 }
159
160 private boolean has(AnyObjectId objectId, Set<AlternateHandle.Id> skips)
161 throws IOException {
162 if (unpackedObjects.contains(objectId)) {
163 return true;
164 }
165 if (wrapped.hasPackedObject(objectId)) {
166 return true;
167 }
168 skips = skipMe(skips);
169 for (CachedObjectDirectory alt : myAlternates()) {
170 if (!skips.contains(alt.getAlternateId())) {
171 if (alt.has(objectId, skips)) {
172 return true;
173 }
174 }
175 }
176 return false;
177 }
178
179 @Override
180 ObjectLoader openObject(WindowCursor curs, AnyObjectId objectId)
181 throws IOException {
182 return openObject(curs, objectId, null);
183 }
184
185 private ObjectLoader openObject(final WindowCursor curs,
186 final AnyObjectId objectId, Set<AlternateHandle.Id> skips)
187 throws IOException {
188 ObjectLoader ldr = openLooseObject(curs, objectId);
189 if (ldr != null) {
190 return ldr;
191 }
192 ldr = wrapped.openPackedObject(curs, objectId);
193 if (ldr != null) {
194 return ldr;
195 }
196 skips = skipMe(skips);
197 for (CachedObjectDirectory alt : myAlternates()) {
198 if (!skips.contains(alt.getAlternateId())) {
199 ldr = alt.openObject(curs, objectId, skips);
200 if (ldr != null) {
201 return ldr;
202 }
203 }
204 }
205 return null;
206 }
207
208 @Override
209 long getObjectSize(WindowCursor curs, AnyObjectId objectId)
210 throws IOException {
211
212
213 return wrapped.getObjectSize(curs, objectId);
214 }
215
216 @Override
217 ObjectLoader openLooseObject(WindowCursor curs, AnyObjectId id)
218 throws IOException {
219 if (unpackedObjects.contains(id)) {
220 ObjectLoader ldr = wrapped.openLooseObject(curs, id);
221 if (ldr != null)
222 return ldr;
223 unpackedObjects = scanLoose();
224 }
225 return null;
226 }
227
228 @Override
229 InsertLooseObjectResult insertUnpackedObject(File tmp, ObjectId objectId,
230 boolean createDuplicate) throws IOException {
231 InsertLooseObjectResult result = wrapped.insertUnpackedObject(tmp,
232 objectId, createDuplicate);
233 switch (result) {
234 case INSERTED:
235 case EXISTS_LOOSE:
236 unpackedObjects.addIfAbsent(new UnpackedObjectId(objectId));
237 break;
238
239 case EXISTS_PACKED:
240 case FAILURE:
241 break;
242 }
243 return result;
244 }
245
246 @Override
247 Pack openPack(File pack) throws IOException {
248 return wrapped.openPack(pack);
249 }
250
251 @Override
252 void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
253 WindowCursor curs) throws IOException {
254 wrapped.selectObjectRepresentation(packer, otp, curs);
255 }
256
257 @Override
258 Collection<Pack> getPacks() {
259 return wrapped.getPacks();
260 }
261
262 private static class UnpackedObjectId extends ObjectIdOwnerMap.Entry {
263 UnpackedObjectId(AnyObjectId id) {
264 super(id);
265 }
266 }
267
268 private AlternateHandle.Id getAlternateId() {
269 return wrapped.getAlternateId();
270 }
271
272 @Override
273 public long getApproximateObjectCount() {
274 long count = 0;
275 for (Pack p : getPacks()) {
276 try {
277 count += p.getObjectCount();
278 } catch (IOException e) {
279 return -1;
280 }
281 }
282 return count;
283 }
284 }