View Javadoc
1   /*
2    * Copyright (C) 2012, Christian Halstrick <christian.halstrick@sap.com> and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  
11  package org.eclipse.jgit.internal.storage.file;
12  
13  import static org.hamcrest.MatcherAssert.assertThat;
14  import static org.hamcrest.Matchers.lessThanOrEqualTo;
15  import static org.junit.Assert.assertEquals;
16  import static org.junit.Assert.assertFalse;
17  import static org.junit.Assert.assertNotNull;
18  import static org.junit.Assert.assertNull;
19  import static org.junit.Assert.assertSame;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.nio.file.Files;
24  import java.nio.file.Path;
25  import java.util.concurrent.BrokenBarrierException;
26  import java.util.concurrent.Callable;
27  import java.util.concurrent.CyclicBarrier;
28  import java.util.concurrent.ExecutorService;
29  import java.util.concurrent.Executors;
30  import java.util.concurrent.Future;
31  import java.util.concurrent.TimeUnit;
32  
33  import org.eclipse.jgit.api.Git;
34  import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
35  import org.eclipse.jgit.lib.ConfigConstants;
36  import org.eclipse.jgit.lib.Constants;
37  import org.eclipse.jgit.lib.Ref.Storage;
38  import org.eclipse.jgit.lib.RefUpdate;
39  import org.eclipse.jgit.lib.RefUpdate.Result;
40  import org.eclipse.jgit.revwalk.RevBlob;
41  import org.eclipse.jgit.revwalk.RevCommit;
42  import org.eclipse.jgit.storage.file.FileBasedConfig;
43  import org.junit.Test;
44  
45  @SuppressWarnings("boxing")
46  public class GcPackRefsTest extends GcTestCase {
47  	@Test
48  	public void looseRefPacked() throws Exception {
49  		RevBlob a = tr.blob("a");
50  		tr.lightweightTag("t", a);
51  
52  		gc.packRefs();
53  		assertSame(repo.exactRef("refs/tags/t").getStorage(), Storage.PACKED);
54  	}
55  
56  	@Test
57  	public void emptyRefDirectoryDeleted() throws Exception {
58  		String ref = "dir/ref";
59  		tr.branch(ref).commit().create();
60  		String name = repo.findRef(ref).getName();
61  		Path dir = repo.getDirectory().toPath().resolve(name).getParent();
62  		assertNotNull(dir);
63  		gc.packRefs();
64  		assertFalse(Files.exists(dir));
65  	}
66  
67  	@Test
68  	public void concurrentOnlyOneWritesPackedRefs() throws Exception {
69  		RevBlob a = tr.blob("a");
70  		tr.lightweightTag("t", a);
71  
72  		CyclicBarrier syncPoint = new CyclicBarrier(2);
73  
74  		// Returns 0 for success, 1 in case of error when writing pack.
75  		Callable<Integer> packRefs = () -> {
76  			syncPoint.await();
77  			try {
78  				gc.packRefs();
79  				return 0;
80  			} catch (IOException e) {
81  				return 1;
82  			}
83  		};
84  		ExecutorService pool = Executors.newFixedThreadPool(2);
85  		try {
86  			Future<Integer> p1 = pool.submit(packRefs);
87  			Future<Integer> p2 = pool.submit(packRefs);
88  			assertThat(p1.get() + p2.get(), lessThanOrEqualTo(1));
89  		} finally {
90  			pool.shutdown();
91  			pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
92  		}
93  	}
94  
95  	@Test
96  	public void whileRefLockedRefNotPackedNoError()
97  			throws Exception {
98  		RevBlob a = tr.blob("a");
99  		tr.lightweightTag("t1", a);
100 		tr.lightweightTag("t2", a);
101 		LockFile refLock = new LockFile(new File(repo.getDirectory(),
102 				"refs/tags/t1"));
103 		try {
104 			refLock.lock();
105 			gc.packRefs();
106 		} finally {
107 			refLock.unlock();
108 		}
109 
110 		assertSame(repo.exactRef("refs/tags/t1").getStorage(), Storage.LOOSE);
111 		assertSame(repo.exactRef("refs/tags/t2").getStorage(), Storage.PACKED);
112 	}
113 
114 	@Test
115 	public void whileRefUpdatedRefUpdateSucceeds()
116 			throws Exception {
117 		RevBlob a = tr.blob("a");
118 		tr.lightweightTag("t", a);
119 		final RevBlob b = tr.blob("b");
120 
121 		final CyclicBarrier refUpdateLockedRef = new CyclicBarrier(2);
122 		final CyclicBarrier packRefsDone = new CyclicBarrier(2);
123 		ExecutorService pool = Executors.newFixedThreadPool(2);
124 		try {
125 			Future<Result> result = pool.submit(() -> {
126 				RefUpdate update = new RefDirectoryUpdate(
127 						(RefDirectory) repo.getRefDatabase(),
128 						repo.exactRef("refs/tags/t")) {
129 					@Override
130 					public boolean isForceUpdate() {
131 						try {
132 							refUpdateLockedRef.await();
133 							packRefsDone.await();
134 						} catch (InterruptedException
135 								| BrokenBarrierException e) {
136 							Thread.currentThread().interrupt();
137 						}
138 						return super.isForceUpdate();
139 					}
140 				};
141 				update.setForceUpdate(true);
142 				update.setNewObjectId(b);
143 				return update.update();
144 			});
145 
146 			Future<Result> result2 = pool.submit(() -> {
147 				refUpdateLockedRef.await();
148 				gc.packRefs();
149 				packRefsDone.await();
150 				return null;
151 			});
152 			assertNull(result2.get());
153 
154 			assertSame(result.get(), Result.FORCED);
155 
156 		} finally {
157 			pool.shutdownNow();
158 			pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
159 		}
160 
161 		assertEquals(repo.exactRef("refs/tags/t").getObjectId(), b);
162 	}
163 
164 	@Test
165 	public void dontPackHEAD_nonBare() throws Exception {
166 		BranchBuilder bb = tr.branch("refs/heads/side");
167 		RevCommit first = bb.commit().add("A", "A").add("B", "B").create();
168 		bb.commit().add("A", "A2").add("B", "B2").create();
169 		Git git = Git.wrap(repo);
170 
171 		// check for the unborn branch master. HEAD should point to master and
172 		// master doesn't exist.
173 		assertEquals(repo.exactRef("HEAD").getTarget().getName(),
174 				"refs/heads/master");
175 		assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
176 		gc.packRefs();
177 		assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
178 		assertEquals(repo.exactRef("HEAD").getTarget().getName(),
179 				"refs/heads/master");
180 		assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
181 
182 		git.checkout().setName("refs/heads/side").call();
183 		gc.packRefs();
184 		assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
185 
186 		// check for detached HEAD
187 		git.checkout().setName(first.getName()).call();
188 		gc.packRefs();
189 		assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
190 	}
191 
192 	@Test
193 	public void dontPackHEAD_bare() throws Exception {
194 		BranchBuilder bb = tr.branch("refs/heads/side");
195 		bb.commit().add("A", "A").add("B", "B").create();
196 		RevCommit second = bb.commit().add("A", "A2").add("B", "B2").create();
197 
198 		// Convert the repo to be bare
199 		FileBasedConfig cfg = repo.getConfig();
200 		cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
201 				ConfigConstants.CONFIG_KEY_BARE, true);
202 		cfg.save();
203 		Git git = Git.open(repo.getDirectory());
204 		repo = (FileRepository) git.getRepository();
205 
206 		// check for the unborn branch master. HEAD should point to master and
207 		// master doesn't exist.
208 		assertEquals(repo.exactRef("HEAD").getTarget().getName(),
209 				"refs/heads/master");
210 		assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
211 		gc.packRefs();
212 		assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
213 		assertEquals(repo.exactRef("HEAD").getTarget().getName(),
214 				"refs/heads/master");
215 		assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
216 
217 		// check for non-detached HEAD
218 		repo.updateRef(Constants.HEAD).link("refs/heads/side");
219 		gc.packRefs();
220 		assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
221 		assertEquals(repo.exactRef("HEAD").getTarget().getObjectId(),
222 				second.getId());
223 	}
224 }