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.pack;
45
46 import static org.junit.Assert.assertEquals;
47 import static org.junit.Assert.assertTrue;
48
49 import java.io.IOException;
50 import java.util.ArrayList;
51 import java.util.Arrays;
52 import java.util.Collections;
53 import java.util.HashSet;
54 import java.util.List;
55 import java.util.Set;
56
57 import org.eclipse.jgit.internal.storage.file.GcTestCase;
58 import org.eclipse.jgit.internal.storage.file.PackBitmapIndexBuilder;
59 import org.eclipse.jgit.internal.storage.pack.PackWriterBitmapPreparer.BitmapCommit;
60 import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
61 import org.eclipse.jgit.junit.TestRepository.CommitBuilder;
62 import org.eclipse.jgit.lib.Constants;
63 import org.eclipse.jgit.lib.NullProgressMonitor;
64 import org.eclipse.jgit.lib.ObjectId;
65 import org.eclipse.jgit.revwalk.RevCommit;
66 import org.eclipse.jgit.storage.pack.PackConfig;
67 import org.junit.Test;
68
69 public class GcCommitSelectionTest extends GcTestCase {
70
71 @Test
72 public void testBitmapSpansNoMerges() throws Exception {
73 testBitmapSpansNoMerges(false);
74 }
75
76 @Test
77 public void testBitmapSpansNoMergesWithTags() throws Exception {
78 testBitmapSpansNoMerges(true);
79 }
80
81 private void testBitmapSpansNoMerges(boolean withTags) throws Exception {
82
83
84
85
86
87
88
89 int[][] bitmapCounts = {
90 { 1, 1 }, { 50, 50 }, { 99, 99 }, { 100, 100 }, { 101, 100 },
91 { 200, 100 }, { 201, 100 }, { 299, 100 }, { 300, 101 },
92 { 301, 101 }, { 401, 101 }, { 499, 101 }, { 500, 102 }, };
93 int currentCommits = 0;
94 BranchBuilder bb = tr.branch("refs/heads/main");
95
96 for (int[] counts : bitmapCounts) {
97 int nextCommitCount = counts[0];
98 int expectedBitmapCount = counts[1];
99 assertTrue(nextCommitCount > currentCommits);
100 for (int i = currentCommits; i < nextCommitCount; i++) {
101 String str = "A" + i;
102 RevCommit rc = bb.commit().message(str).add(str, str).create();
103 if (withTags) {
104 tr.lightweightTag(str, rc);
105 }
106 }
107 currentCommits = nextCommitCount;
108
109 gc.setPackExpireAgeMillis(0);
110 gc.setExpireAgeMillis(0);
111 gc.gc();
112 assertEquals(currentCommits * 3,
113 gc.getStatistics().numberOfPackedObjects);
114 assertEquals(currentCommits + " commits: ", expectedBitmapCount,
115 gc.getStatistics().numberOfBitmaps);
116 }
117 }
118
119 @Test
120 public void testBitmapSpansWithMerges() throws Exception {
121
122
123
124
125
126
127 List<Integer> merges = Arrays.asList(Integer.valueOf(55),
128 Integer.valueOf(115), Integer.valueOf(175),
129 Integer.valueOf(235));
130
131
132
133
134
135
136
137 int[][] bitmapCounts = {
138 { 1, 1 }, { 55, 55 }, { 56, 57 },
139 { 99, 100 },
140 { 100, 100 },
141 { 116, 100 },
142 { 176, 101 },
143 { 213, 101 },
144 { 214, 102 },
145 { 236, 102 },
146 { 273, 102 },
147 { 274, 103 },
148 { 334, 103 },
149 { 335, 104 },
150 { 435, 104 },
151 { 436, 104 },
152 };
153
154 int currentCommits = 0;
155 BranchBuilder bb = tr.branch("refs/heads/main");
156
157 for (int[] counts : bitmapCounts) {
158 int nextCommitCount = counts[0];
159 int expectedBitmapCount = counts[1];
160 assertTrue(nextCommitCount > currentCommits);
161 for (int i = currentCommits; i < nextCommitCount; i++) {
162 String str = "A" + i;
163 if (!merges.contains(Integer.valueOf(i))) {
164 bb.commit().message(str).add(str, str).create();
165 } else {
166 BranchBuilder bbN = tr.branch("refs/heads/A" + i);
167 bb.commit().message(str).add(str, str)
168 .parent(bbN.commit().create()).create();
169 }
170 }
171 currentCommits = nextCommitCount;
172
173 gc.setPackExpireAgeMillis(0);
174 gc.setExpireAgeMillis(0);
175 gc.gc();
176 assertEquals(currentCommits + " commits: ", expectedBitmapCount,
177 gc.getStatistics().numberOfBitmaps);
178 }
179 }
180
181 @Test
182 public void testBitmapsForExcessiveBranches() throws Exception {
183 int oneDayInSeconds = 60 * 60 * 24;
184
185
186 BranchBuilder bbA = tr.branch("refs/heads/A");
187 for (int i = 0; i < 1001; i++) {
188 String msg = "A" + i;
189 bbA.commit().message(msg).add(msg, msg).create();
190 }
191
192 tr.tick(oneDayInSeconds * 90);
193 BranchBuilder bbB = tr.branch("refs/heads/B");
194 for (int i = 0; i < 1001; i++) {
195 String msg = "B" + i;
196 bbB.commit().message(msg).add(msg, msg).create();
197 }
198
199 for (int i = 0; i < 100; i++) {
200 BranchBuilder bb = tr.branch("refs/heads/N" + i);
201 String msg = "singlecommit" + i;
202 bb.commit().message(msg).add(msg, msg).create();
203 }
204
205 tr.tick(oneDayInSeconds);
206
207
208
209 final int commitsForSparseBranch = 1 + (1001 / 200);
210 final int commitsForFullBranch = 100 + (901 / 200);
211 final int commitsForShallowBranches = 100;
212
213
214 gc.setPackExpireAgeMillis(0);
215 gc.setExpireAgeMillis(0);
216 gc.gc();
217 assertEquals(
218 commitsForSparseBranch + commitsForFullBranch
219 + commitsForShallowBranches,
220 gc.getStatistics().numberOfBitmaps);
221 }
222
223 @Test
224 public void testSelectionOrderingWithChains() throws Exception {
225
226
227
228
229
230
231
232
233 BranchBuilder bb = tr.branch("refs/heads/main");
234 RevCommit m0 = addCommit(bb, "m0");
235 RevCommit m1 = addCommit(bb, "m1", m0);
236 RevCommit m2 = addCommit(bb, "m2", m1);
237 RevCommit b3 = addCommit(bb, "b3", m1);
238 RevCommit m4 = addCommit(bb, "m4", m2);
239 RevCommit b5 = addCommit(bb, "m5", b3);
240 RevCommit m6 = addCommit(bb, "m6", m4);
241 RevCommit b7 = addCommit(bb, "m7", b5);
242 RevCommit m8 = addCommit(bb, "m8", m6, b7);
243 RevCommit m9 = addCommit(bb, "m9", m8);
244
245 List<RevCommit> commits = Arrays.asList(m0, m1, m2, b3, m4, b5, m6, b7,
246 m8, m9);
247 PackWriterBitmapPreparer preparer = newPreparer(
248 Collections.singleton(m9), commits, new PackConfig());
249 List<BitmapCommit> selection = new ArrayList<>(
250 preparer.selectCommits(commits.size(), PackWriter.NONE));
251
252
253 String[] expected = { m0.name(), m1.name(), m2.name(), m4.name(),
254 m6.name(), m8.name(), m9.name(), b3.name(), b5.name(),
255 b7.name() };
256 assertEquals(expected.length, selection.size());
257 for (int i = 0; i < expected.length; i++) {
258 assertEquals("Entry " + i, expected[i], selection.get(i).getName());
259 }
260 }
261
262 private RevCommit addCommit(BranchBuilder bb, String msg,
263 RevCommit... parents) throws Exception {
264 CommitBuilder commit = bb.commit().message(msg).add(msg, msg).tick(1)
265 .noParents();
266 for (RevCommit parent : parents) {
267 commit.parent(parent);
268 }
269 return commit.create();
270 }
271
272 @Test
273 public void testDistributionOnMultipleBranches() throws Exception {
274 BranchBuilder[] branches = { tr.branch("refs/heads/main"),
275 tr.branch("refs/heads/a"), tr.branch("refs/heads/b"),
276 tr.branch("refs/heads/c") };
277 RevCommit[] tips = new RevCommit[branches.length];
278 List<RevCommit> commits = createHistory(branches, tips);
279 PackConfig config = new PackConfig();
280 config.setBitmapContiguousCommitCount(1);
281 config.setBitmapRecentCommitSpan(5);
282 config.setBitmapDistantCommitSpan(20);
283 config.setBitmapRecentCommitCount(100);
284 Set<RevCommit> wants = new HashSet<>(Arrays.asList(tips));
285 PackWriterBitmapPreparer preparer = newPreparer(wants, commits, config);
286 List<BitmapCommit> selection = new ArrayList<>(
287 preparer.selectCommits(commits.size(), PackWriter.NONE));
288 Set<ObjectId> selected = new HashSet<>();
289 for (BitmapCommit c : selection) {
290 selected.add(c.toObjectId());
291 }
292
293
294 for (RevCommit c : wants) {
295 assertTrue(selected.contains(c.toObjectId()));
296 int count = 1;
297 int selectedCount = 1;
298 RevCommit parent = c;
299 while (parent.getParentCount() != 0) {
300 parent = parent.getParent(0);
301 count++;
302 if (selected.contains(parent.toObjectId())) {
303 selectedCount++;
304 }
305 }
306
307
308
309
310
311 int expectedCount = 10 + (count - 100 - 24) / 25;
312 assertTrue(expectedCount <= selectedCount);
313 }
314 }
315
316 private List<RevCommit> createHistory(BranchBuilder[] branches,
317 RevCommit[] tips) throws Exception {
318
319
320
321
322
323
324
325
326
327
328
329
330
331 List<RevCommit> commits = new ArrayList<>();
332 String[] prefixes = { "m", "a", "b", "c" };
333 int branchCount = branches.length;
334 tips[0] = addCommit(commits, branches[0], "root");
335 int counter = 0;
336
337 for (int b = 0; b < branchCount; b++) {
338 for (int i = 0; i < 100; i++, counter++) {
339 for (int j = 0; j <= b; j++) {
340 tips[j] = addCommit(commits, branches[j],
341 prefixes[j] + counter);
342 }
343 }
344
345 if (b < branchCount - 1) {
346 tips[b + 1] = addCommit(branches[b + 1],
347 "branch_" + prefixes[b + 1], tips[0]);
348 }
349 }
350 return commits;
351 }
352
353 private RevCommit addCommit(List<RevCommit> commits, BranchBuilder bb,
354 String msg, RevCommit... parents) throws Exception {
355 CommitBuilder commit = bb.commit().message(msg).add(msg, msg).tick(1);
356 if (parents.length > 0) {
357 commit.noParents();
358 for (RevCommit parent : parents) {
359 commit.parent(parent);
360 }
361 }
362 RevCommit c = commit.create();
363 commits.add(c);
364 return c;
365 }
366
367 private PackWriterBitmapPreparer newPreparer(Set<RevCommit> wants,
368 List<RevCommit> commits, PackConfig config) throws IOException {
369 List<ObjectToPack> objects = new ArrayList<>(commits.size());
370 for (RevCommit commit : commits) {
371 objects.add(new ObjectToPack(commit, Constants.OBJ_COMMIT));
372 }
373 PackBitmapIndexBuilder builder = new PackBitmapIndexBuilder(objects);
374 return new PackWriterBitmapPreparer(
375 tr.getRepository().newObjectReader(), builder,
376 NullProgressMonitor.INSTANCE, wants, config);
377 }
378 }