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