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
45 package org.eclipse.jgit.internal.storage.file;
46
47 import static org.junit.Assert.assertArrayEquals;
48 import static org.junit.Assert.assertEquals;
49 import static org.junit.Assert.assertFalse;
50 import static org.junit.Assert.assertNotNull;
51 import static org.junit.Assert.assertNotSame;
52 import static org.junit.Assert.fail;
53
54 import java.io.BufferedOutputStream;
55 import java.io.File;
56 import java.io.FileOutputStream;
57 import java.io.IOException;
58 import java.io.OutputStream;
59 import java.time.Instant;
60
61 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
62 import org.eclipse.jgit.errors.MissingObjectException;
63 import org.eclipse.jgit.internal.storage.pack.PackWriter;
64 import org.eclipse.jgit.junit.RepositoryTestCase;
65 import org.eclipse.jgit.lib.AnyObjectId;
66 import org.eclipse.jgit.lib.Constants;
67 import org.eclipse.jgit.lib.NullProgressMonitor;
68 import org.eclipse.jgit.lib.ObjectId;
69 import org.eclipse.jgit.lib.ObjectInserter;
70 import org.eclipse.jgit.lib.ObjectLoader;
71 import org.eclipse.jgit.lib.Repository;
72 import org.eclipse.jgit.revwalk.RevObject;
73 import org.eclipse.jgit.revwalk.RevWalk;
74 import org.eclipse.jgit.storage.file.WindowCacheConfig;
75 import org.eclipse.jgit.util.FS;
76 import org.eclipse.jgit.util.FileUtils;
77 import org.junit.After;
78 import org.junit.Before;
79 import org.junit.Test;
80
81 public class ConcurrentRepackTest extends RepositoryTestCase {
82 @Override
83 @Before
84 public void setUp() throws Exception {
85 WindowCacheConfig windowCacheConfig = new WindowCacheConfig();
86 windowCacheConfig.setPackedGitOpenFiles(1);
87 windowCacheConfig.install();
88 super.setUp();
89 }
90
91 @Override
92 @After
93 public void tearDown() throws Exception {
94 super.tearDown();
95 new WindowCacheConfig().install();
96 }
97
98 @Test
99 public void testObjectInNewPack() throws IncorrectObjectTypeException,
100 IOException {
101
102
103 final Repository eden = createBareRepository();
104 final RevObject o1 = writeBlob(eden, "o1");
105 pack(eden, o1);
106 assertEquals(o1.name(), parse(o1).name());
107 }
108
109 @Test
110 public void testObjectMovedToNewPack1()
111 throws IncorrectObjectTypeException, IOException {
112
113
114
115
116 final Repository eden = createBareRepository();
117 final RevObject o1 = writeBlob(eden, "o1");
118 final File[] out1 = pack(eden, o1);
119 assertEquals(o1.name(), parse(o1).name());
120
121 final RevObject o2 = writeBlob(eden, "o2");
122 pack(eden, o2, o1);
123
124
125
126 whackCache();
127 delete(out1);
128
129
130
131
132 assertEquals(o2.name(), parse(o2).name());
133 assertEquals(o1.name(), parse(o1).name());
134 }
135
136 @Test
137 public void testObjectMovedWithinPack()
138 throws IncorrectObjectTypeException, IOException {
139
140
141 final Repository eden = createBareRepository();
142 final RevObject o1 = writeBlob(eden, "o1");
143 final File[] out1 = pack(eden, o1);
144 assertEquals(o1.name(), parse(o1).name());
145
146
147
148 whackCache();
149
150
151
152
153
154
155 final RevObject o2 = writeBlob(eden, "o2");
156 try (PackWriter pw = new PackWriter(eden)) {
157 pw.addObject(o2);
158 pw.addObject(o1);
159 write(out1, pw);
160 }
161
162
163
164
165 assertEquals(o1.name(), parse(o1).name());
166 assertEquals(o2.name(), parse(o2).name());
167 }
168
169 @Test
170 public void testObjectMovedToNewPack2()
171 throws IncorrectObjectTypeException, IOException {
172
173
174
175
176 final Repository eden = createBareRepository();
177 final RevObject o1 = writeBlob(eden, "o1");
178 final File[] out1 = pack(eden, o1);
179 assertEquals(o1.name(), parse(o1).name());
180
181 final ObjectLoader load1 = db.open(o1, Constants.OBJ_BLOB);
182 assertNotNull(load1);
183
184 final RevObject o2 = writeBlob(eden, "o2");
185 pack(eden, o2, o1);
186
187
188
189 whackCache();
190 delete(out1);
191
192
193
194
195
196 final ObjectLoader load2 = db.open(o1, Constants.OBJ_BLOB);
197 assertNotNull(load2);
198 assertNotSame(load1, load2);
199
200 final byte[] data2 = load2.getCachedBytes();
201 final byte[] data1 = load1.getCachedBytes();
202 assertNotNull(data2);
203 assertNotNull(data1);
204 assertNotSame(data1, data2);
205 assertArrayEquals(data1, data2);
206 assertEquals(load2.getType(), load1.getType());
207 }
208
209 private static void whackCache() {
210 final WindowCacheConfig config = new WindowCacheConfig();
211 config.setPackedGitOpenFiles(1);
212 config.install();
213 }
214
215 private RevObject parse(AnyObjectId id)
216 throws MissingObjectException, IOException {
217 try (RevWalk rw = new RevWalk(db)) {
218 return rw.parseAny(id);
219 }
220 }
221
222 private File[] pack(Repository src, RevObject... list)
223 throws IOException {
224 try (PackWriter pw = new PackWriter(src)) {
225 for (RevObject o : list) {
226 pw.addObject(o);
227 }
228
229 final ObjectId name = pw.computeName();
230 final File packFile = fullPackFileName(name, ".pack");
231 final File idxFile = fullPackFileName(name, ".idx");
232 final File[] files = new File[] { packFile, idxFile };
233 write(files, pw);
234 return files;
235 }
236 }
237
238 private static void write(File[] files, PackWriter pw)
239 throws IOException {
240 final Instant begin = FS.DETECTED
241 .lastModifiedInstant(files[0].getParentFile());
242 NullProgressMonitor m = NullProgressMonitor.INSTANCE;
243
244 try (OutputStream out = new BufferedOutputStream(
245 new FileOutputStream(files[0]))) {
246 pw.writePack(m, m, out);
247 }
248
249 try (OutputStream out = new BufferedOutputStream(
250 new FileOutputStream(files[1]))) {
251 pw.writeIndex(out);
252 }
253
254 touch(begin, files[0].getParentFile());
255 }
256
257 private static void delete(File[] list) throws IOException {
258 final Instant begin = FS.DETECTED
259 .lastModifiedInstant(list[0].getParentFile());
260 for (File f : list) {
261 FileUtils.delete(f);
262 assertFalse(f + " was removed", f.exists());
263 }
264 touch(begin, list[0].getParentFile());
265 }
266
267 private static void touch(Instant begin, File dir) throws IOException {
268 while (begin.compareTo(FS.DETECTED.lastModifiedInstant(dir)) >= 0) {
269 try {
270 Thread.sleep(25);
271 } catch (InterruptedException ie) {
272
273 }
274 FS.DETECTED.setLastModified(dir.toPath(), Instant.now());
275 }
276 }
277
278 private File fullPackFileName(ObjectId name, String suffix) {
279 final File packdir = db.getObjectDatabase().getPackDirectory();
280 return new File(packdir, "pack-" + name.name() + suffix);
281 }
282
283 private RevObject writeBlob(Repository repo, String data)
284 throws IOException {
285 final byte[] bytes = Constants.encode(data);
286 final ObjectId id;
287 try (ObjectInserter inserter = repo.newObjectInserter()) {
288 id = inserter.insert(Constants.OBJ_BLOB, bytes);
289 inserter.flush();
290 }
291 try {
292 parse(id);
293 fail("Object " + id.name() + " should not exist in test repository");
294 } catch (MissingObjectException e) {
295
296 }
297 try (RevWalk revWalk = new RevWalk(repo)) {
298 return revWalk.lookupBlob(id);
299 }
300 }
301 }