View Javadoc
1   /*
2    * Copyright (C) 2012, Christian Halstrick <christian.halstrick@sap.com>
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  
44  package org.eclipse.jgit.internal.storage.file;
45  
46  import static java.lang.Integer.valueOf;
47  import static org.junit.Assert.assertEquals;
48  import static org.junit.Assert.assertFalse;
49  import static org.junit.Assert.assertNotNull;
50  import static org.junit.Assert.assertNull;
51  import static org.junit.Assert.assertSame;
52  
53  import java.io.File;
54  import java.io.IOException;
55  import java.nio.file.Files;
56  import java.nio.file.Path;
57  import java.util.concurrent.BrokenBarrierException;
58  import java.util.concurrent.Callable;
59  import java.util.concurrent.CyclicBarrier;
60  import java.util.concurrent.ExecutorService;
61  import java.util.concurrent.Executors;
62  import java.util.concurrent.Future;
63  import java.util.concurrent.TimeUnit;
64  
65  import org.eclipse.jgit.api.Git;
66  import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
67  import org.eclipse.jgit.lib.ConfigConstants;
68  import org.eclipse.jgit.lib.Constants;
69  import org.eclipse.jgit.lib.Ref.Storage;
70  import org.eclipse.jgit.lib.RefUpdate;
71  import org.eclipse.jgit.lib.RefUpdate.Result;
72  import org.eclipse.jgit.revwalk.RevBlob;
73  import org.eclipse.jgit.revwalk.RevCommit;
74  import org.eclipse.jgit.storage.file.FileBasedConfig;
75  import org.junit.Test;
76  
77  public class GcPackRefsTest extends GcTestCase {
78  	@Test
79  	public void looseRefPacked() throws Exception {
80  		RevBlob a = tr.blob("a");
81  		tr.lightweightTag("t", a);
82  
83  		gc.packRefs();
84  		assertSame(repo.exactRef("refs/tags/t").getStorage(), Storage.PACKED);
85  	}
86  
87  	@Test
88  	public void emptyRefDirectoryDeleted() throws Exception {
89  		String ref = "dir/ref";
90  		tr.branch(ref).commit().create();
91  		String name = repo.findRef(ref).getName();
92  		Path dir = repo.getDirectory().toPath().resolve(name).getParent();
93  		assertNotNull(dir);
94  		gc.packRefs();
95  		assertFalse(Files.exists(dir));
96  	}
97  
98  	@Test
99  	public void concurrentOnlyOneWritesPackedRefs() throws Exception {
100 		RevBlob a = tr.blob("a");
101 		tr.lightweightTag("t", a);
102 
103 		final CyclicBarrier syncPoint = new CyclicBarrier(2);
104 
105 		Callable<Integer> packRefs = new Callable<Integer>() {
106 
107 			/** @return 0 for success, 1 in case of error when writing pack */
108 			@Override
109 			public Integer call() throws Exception {
110 				syncPoint.await();
111 				try {
112 					gc.packRefs();
113 					return valueOf(0);
114 				} catch (IOException e) {
115 					return valueOf(1);
116 				}
117 			}
118 		};
119 		ExecutorService pool = Executors.newFixedThreadPool(2);
120 		try {
121 			Future<Integer> p1 = pool.submit(packRefs);
122 			Future<Integer> p2 = pool.submit(packRefs);
123 			assertEquals(1, p1.get().intValue() + p2.get().intValue());
124 		} finally {
125 			pool.shutdown();
126 			pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
127 		}
128 	}
129 
130 	@Test
131 	public void whileRefLockedRefNotPackedNoError()
132 			throws Exception {
133 		RevBlob a = tr.blob("a");
134 		tr.lightweightTag("t1", a);
135 		tr.lightweightTag("t2", a);
136 		LockFile refLock = new LockFile(new File(repo.getDirectory(),
137 				"refs/tags/t1"));
138 		try {
139 			refLock.lock();
140 			gc.packRefs();
141 		} finally {
142 			refLock.unlock();
143 		}
144 
145 		assertSame(repo.exactRef("refs/tags/t1").getStorage(), Storage.LOOSE);
146 		assertSame(repo.exactRef("refs/tags/t2").getStorage(), Storage.PACKED);
147 	}
148 
149 	@Test
150 	public void whileRefUpdatedRefUpdateSucceeds()
151 			throws Exception {
152 		RevBlob a = tr.blob("a");
153 		tr.lightweightTag("t", a);
154 		final RevBlob b = tr.blob("b");
155 
156 		final CyclicBarrier refUpdateLockedRef = new CyclicBarrier(2);
157 		final CyclicBarrier packRefsDone = new CyclicBarrier(2);
158 		ExecutorService pool = Executors.newFixedThreadPool(2);
159 		try {
160 			Future<Result> result = pool.submit(new Callable<Result>() {
161 
162 				@Override
163 				public Result call() throws Exception {
164 					RefUpdate update = new RefDirectoryUpdate(
165 							(RefDirectory) repo.getRefDatabase(),
166 							repo.exactRef("refs/tags/t")) {
167 						@Override
168 						public boolean isForceUpdate() {
169 							try {
170 								refUpdateLockedRef.await();
171 								packRefsDone.await();
172 							} catch (InterruptedException e) {
173 								Thread.currentThread().interrupt();
174 							} catch (BrokenBarrierException e) {
175 								Thread.currentThread().interrupt();
176 							}
177 							return super.isForceUpdate();
178 						}
179 					};
180 					update.setForceUpdate(true);
181 					update.setNewObjectId(b);
182 					return update.update();
183 				}
184 			});
185 
186 			pool.submit(new Callable<Void>() {
187 				@Override
188 				public Void call() throws Exception {
189 					refUpdateLockedRef.await();
190 					gc.packRefs();
191 					packRefsDone.await();
192 					return null;
193 				}
194 			});
195 
196 			assertSame(result.get(), Result.FORCED);
197 
198 		} finally {
199 			pool.shutdownNow();
200 			pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
201 		}
202 
203 		assertEquals(repo.exactRef("refs/tags/t").getObjectId(), b);
204 	}
205 
206 	@Test
207 	public void dontPackHEAD_nonBare() throws Exception {
208 		BranchBuilder bb = tr.branch("refs/heads/side");
209 		RevCommit first = bb.commit().add("A", "A").add("B", "B").create();
210 		bb.commit().add("A", "A2").add("B", "B2").create();
211 		Git git = Git.wrap(repo);
212 
213 		// check for the unborn branch master. HEAD should point to master and
214 		// master doesn't exist.
215 		assertEquals(repo.exactRef("HEAD").getTarget().getName(),
216 				"refs/heads/master");
217 		assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
218 		gc.packRefs();
219 		assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
220 		assertEquals(repo.exactRef("HEAD").getTarget().getName(),
221 				"refs/heads/master");
222 		assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
223 
224 		git.checkout().setName("refs/heads/side").call();
225 		gc.packRefs();
226 		assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
227 
228 		// check for detached HEAD
229 		git.checkout().setName(first.getName()).call();
230 		gc.packRefs();
231 		assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
232 	}
233 
234 	@Test
235 	public void dontPackHEAD_bare() throws Exception {
236 		BranchBuilder bb = tr.branch("refs/heads/side");
237 		bb.commit().add("A", "A").add("B", "B").create();
238 		RevCommit second = bb.commit().add("A", "A2").add("B", "B2").create();
239 
240 		// Convert the repo to be bare
241 		FileBasedConfig cfg = repo.getConfig();
242 		cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
243 				ConfigConstants.CONFIG_KEY_BARE, true);
244 		cfg.save();
245 		Git git = Git.open(repo.getDirectory());
246 		repo = (FileRepository) git.getRepository();
247 
248 		// check for the unborn branch master. HEAD should point to master and
249 		// master doesn't exist.
250 		assertEquals(repo.exactRef("HEAD").getTarget().getName(),
251 				"refs/heads/master");
252 		assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
253 		gc.packRefs();
254 		assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
255 		assertEquals(repo.exactRef("HEAD").getTarget().getName(),
256 				"refs/heads/master");
257 		assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
258 
259 		// check for non-detached HEAD
260 		repo.updateRef(Constants.HEAD).link("refs/heads/side");
261 		gc.packRefs();
262 		assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
263 		assertEquals(repo.exactRef("HEAD").getTarget().getObjectId(),
264 				second.getId());
265 	}
266 }