View Javadoc
1   /*
2    * Copyright (C) 2009-2010, Google Inc.
3    * Copyright (C) 2009, Robin Rosenberg
4    * Copyright (C) 2009, Robin Rosenberg <robin.rosenberg@dewire.com> and others
5    *
6    * This program and the accompanying materials are made available under the
7    * terms of the Eclipse Distribution License v. 1.0 which is available at
8    * https://www.eclipse.org/org/documents/edl-v10.php.
9    *
10   * SPDX-License-Identifier: BSD-3-Clause
11   */
12  
13  package org.eclipse.jgit.lib;
14  
15  import static java.nio.charset.StandardCharsets.UTF_8;
16  import static org.eclipse.jgit.junit.Assert.assertEquals;
17  import static org.junit.Assert.assertEquals;
18  import static org.junit.Assert.assertFalse;
19  import static org.junit.Assert.assertNotNull;
20  import static org.junit.Assert.assertNull;
21  import static org.junit.Assert.assertSame;
22  import static org.junit.Assert.assertTrue;
23  import static org.junit.Assert.fail;
24  
25  import java.io.File;
26  import java.io.FileOutputStream;
27  import java.io.IOException;
28  import java.util.Collection;
29  import java.util.HashSet;
30  import java.util.List;
31  import java.util.Optional;
32  import java.util.Set;
33  import java.util.TreeSet;
34  
35  import org.eclipse.jgit.lib.Ref.Storage;
36  import org.eclipse.jgit.lib.RefUpdate.Result;
37  import org.eclipse.jgit.storage.file.FileBasedConfig;
38  import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
39  import org.junit.Test;
40  
41  /**
42   * Misc tests for refs. A lot of things are tested elsewhere so not having a
43   * test for a ref related method, does not mean it is untested.
44   */
45  public class RefTest extends SampleDataRepositoryTestCase {
46  
47  	private void writeSymref(String src, String dst) throws IOException {
48  		RefUpdate u = db.updateRef(src);
49  		switch (u.link(dst)) {
50  		case NEW:
51  		case FORCED:
52  		case NO_CHANGE:
53  			break;
54  		default:
55  			fail("link " + src + " to " + dst);
56  		}
57  	}
58  
59  	private void writeNewRef(String name, ObjectId value) throws IOException {
60  		RefUpdate updateRef = db.updateRef(name);
61  		updateRef.setNewObjectId(value);
62  		assertEquals(RefUpdate.Result.NEW, updateRef.update());
63  	}
64  
65  	@Test
66  	public void testRemoteNames() throws Exception {
67  		FileBasedConfig config = db.getConfig();
68  		config.setBoolean(ConfigConstants.CONFIG_REMOTE_SECTION,
69  				"origin", "dummy", true);
70  		config.setBoolean(ConfigConstants.CONFIG_REMOTE_SECTION,
71  				"ab/c", "dummy", true);
72  		config.save();
73  		assertEquals("[ab/c, origin]",
74  				new TreeSet<>(db.getRemoteNames()).toString());
75  
76  		// one-level deep remote branch
77  		assertEquals("master",
78  				db.shortenRemoteBranchName("refs/remotes/origin/master"));
79  		assertEquals("origin", db.getRemoteName("refs/remotes/origin/master"));
80  
81  		// two-level deep remote branch
82  		assertEquals("masta/r",
83  				db.shortenRemoteBranchName("refs/remotes/origin/masta/r"));
84  		assertEquals("origin", db.getRemoteName("refs/remotes/origin/masta/r"));
85  
86  		// Remote with slash and one-level deep branch name
87  		assertEquals("xmaster",
88  				db.shortenRemoteBranchName("refs/remotes/ab/c/xmaster"));
89  		assertEquals("ab/c", db.getRemoteName("refs/remotes/ab/c/xmaster"));
90  
91  		// Remote with slash and two-level deep branch name
92  		assertEquals("xmasta/r",
93  				db.shortenRemoteBranchName("refs/remotes/ab/c/xmasta/r"));
94  		assertEquals("ab/c", db.getRemoteName("refs/remotes/ab/c/xmasta/r"));
95  
96  		// no such remote
97  		assertNull(db.getRemoteName("refs/remotes/nosuchremote/x"));
98  		assertNull(db.shortenRemoteBranchName("refs/remotes/nosuchremote/x"));
99  
100 		// no such remote too, no branch name either
101 		assertNull(db.getRemoteName("refs/remotes/abranch"));
102 		assertNull(db.shortenRemoteBranchName("refs/remotes/abranch"));
103 
104 		// // local branch
105 		assertNull(db.getRemoteName("refs/heads/abranch"));
106 		assertNull(db.shortenRemoteBranchName("refs/heads/abranch"));
107 	}
108 
109 	@Test
110 	public void testReadAllIncludingSymrefs() throws Exception {
111 		ObjectId masterId = db.resolve("refs/heads/master");
112 		RefUpdate updateRef = db.updateRef("refs/remotes/origin/master");
113 		updateRef.setNewObjectId(masterId);
114 		updateRef.setForceUpdate(true);
115 		updateRef.update();
116 		writeSymref("refs/remotes/origin/HEAD",
117 					"refs/remotes/origin/master");
118 
119 		ObjectId r = db.resolve("refs/remotes/origin/HEAD");
120 		assertEquals(masterId, r);
121 
122 		List<Ref> allRefs = db.getRefDatabase().getRefs();
123 		Optional<Ref> refHEAD = allRefs.stream()
124 				.filter(ref -> ref.getName().equals("refs/remotes/origin/HEAD"))
125 				.findAny();
126 		assertTrue(refHEAD.isPresent());
127 		assertEquals(masterId, refHEAD.get().getObjectId());
128 		assertFalse(refHEAD.get().isPeeled());
129 		assertNull(refHEAD.get().getPeeledObjectId());
130 
131 		Optional<Ref> refmaster = allRefs.stream().filter(
132 				ref -> ref.getName().equals("refs/remotes/origin/master"))
133 				.findAny();
134 		assertTrue(refmaster.isPresent());
135 		assertEquals(masterId, refmaster.get().getObjectId());
136 		assertFalse(refmaster.get().isPeeled());
137 		assertNull(refmaster.get().getPeeledObjectId());
138 	}
139 
140 	@Test
141 	public void testReadSymRefToPacked() throws IOException {
142 		writeSymref("HEAD", "refs/heads/b");
143 		Ref ref = db.exactRef("HEAD");
144 		assertEquals(Ref.Storage.LOOSE, ref.getStorage());
145 		assertTrue("is symref", ref.isSymbolic());
146 		ref = ref.getTarget();
147 		assertEquals("refs/heads/b", ref.getName());
148 		assertEquals(Ref.Storage.PACKED, ref.getStorage());
149 	}
150 
151 	@Test
152 	public void testReadSymRefToLoosePacked() throws IOException {
153 		ObjectId pid = db.resolve("refs/heads/master^");
154 		RefUpdate updateRef = db.updateRef("refs/heads/master");
155 		updateRef.setNewObjectId(pid);
156 		updateRef.setForceUpdate(true);
157 		Result update = updateRef.update();
158 		assertEquals(Result.FORCED, update); // internal
159 
160 		writeSymref("HEAD", "refs/heads/master");
161 		Ref ref = db.exactRef("HEAD");
162 		assertEquals(Ref.Storage.LOOSE, ref.getStorage());
163 		ref = ref.getTarget();
164 		assertEquals("refs/heads/master", ref.getName());
165 		assertEquals(Ref.Storage.LOOSE, ref.getStorage());
166 	}
167 
168 	@Test
169 	public void testReadLooseRef() throws IOException {
170 		RefUpdate updateRef = db.updateRef("ref/heads/new");
171 		updateRef.setNewObjectId(db.resolve("refs/heads/master"));
172 		Result update = updateRef.update();
173 		assertEquals(Result.NEW, update);
174 		Ref ref = db.exactRef("ref/heads/new");
175 		assertEquals(Storage.LOOSE, ref.getStorage());
176 	}
177 
178 	@Test
179 	public void testGetShortRef() throws IOException {
180 		Ref ref = db.exactRef("refs/heads/master");
181 		assertEquals("refs/heads/master", ref.getName());
182 		assertEquals(db.resolve("refs/heads/master"), ref.getObjectId());
183 	}
184 
185 	@Test
186 	public void testGetShortExactRef() throws IOException {
187 		assertNull(db.getRefDatabase().exactRef("master"));
188 
189 		Ref ref = db.getRefDatabase().exactRef("HEAD");
190 		assertEquals("HEAD", ref.getName());
191 		assertEquals("refs/heads/master", ref.getTarget().getName());
192 		assertEquals(db.resolve("refs/heads/master"), ref.getObjectId());
193 	}
194 
195 	@Test
196 	public void testRefsUnderRefs() throws IOException {
197 		ObjectId masterId = db.resolve("refs/heads/master");
198 		writeNewRef("refs/heads/refs/foo/bar", masterId);
199 
200 		assertNull(db.getRefDatabase().exactRef("refs/foo/bar"));
201 
202 		Ref ref = db.findRef("refs/foo/bar");
203 		assertEquals("refs/heads/refs/foo/bar", ref.getName());
204 		assertEquals(db.resolve("refs/heads/master"), ref.getObjectId());
205 	}
206 
207 	@Test
208 	public void testAmbiguousRefsUnderRefs() throws IOException {
209 		ObjectId masterId = db.resolve("refs/heads/master");
210 		writeNewRef("refs/foo/bar", masterId);
211 		writeNewRef("refs/heads/refs/foo/bar", masterId);
212 
213 		Ref exactRef = db.getRefDatabase().exactRef("refs/foo/bar");
214 		assertEquals("refs/foo/bar", exactRef.getName());
215 		assertEquals(masterId, exactRef.getObjectId());
216 
217 		Ref ref = db.findRef("refs/foo/bar");
218 		assertEquals("refs/foo/bar", ref.getName());
219 		assertEquals(masterId, ref.getObjectId());
220 	}
221 
222 	/**
223 	 * Let an "outsider" create a loose ref with the same name as a packed one
224 	 *
225 	 * @throws IOException
226 	 * @throws InterruptedException
227 	 */
228 	@Test
229 	public void testReadLoosePackedRef() throws IOException,
230 			InterruptedException {
231 		Ref ref = db.exactRef("refs/heads/master");
232 		assertEquals(Storage.PACKED, ref.getStorage());
233 		try (FileOutputStream os = new FileOutputStream(
234 				new File(db.getDirectory(), "refs/heads/master"))) {
235 			os.write(ref.getObjectId().name().getBytes(UTF_8));
236 			os.write('\n');
237 		}
238 
239 		ref = db.exactRef("refs/heads/master");
240 		assertEquals(Storage.LOOSE, ref.getStorage());
241 	}
242 
243 	/**
244 	 * Modify a packed ref using the API. This creates a loose ref too, ie.
245 	 * LOOSE_PACKED
246 	 *
247 	 * @throws IOException
248 	 */
249 	@Test
250 	public void testReadSimplePackedRefSameRepo() throws IOException {
251 		Ref ref = db.exactRef("refs/heads/master");
252 		ObjectId pid = db.resolve("refs/heads/master^");
253 		assertEquals(Storage.PACKED, ref.getStorage());
254 		RefUpdate updateRef = db.updateRef("refs/heads/master");
255 		updateRef.setNewObjectId(pid);
256 		updateRef.setForceUpdate(true);
257 		Result update = updateRef.update();
258 		assertEquals(Result.FORCED, update);
259 
260 		ref = db.exactRef("refs/heads/master");
261 		assertEquals(Storage.LOOSE, ref.getStorage());
262 	}
263 
264 	@Test
265 	public void testResolvedNamesBranch() throws IOException {
266 		Ref ref = db.findRef("a");
267 		assertEquals("refs/heads/a", ref.getName());
268 	}
269 
270 	@Test
271 	public void testResolvedSymRef() throws IOException {
272 		Ref ref = db.exactRef(Constants.HEAD);
273 		assertEquals(Constants.HEAD, ref.getName());
274 		assertTrue("is symbolic ref", ref.isSymbolic());
275 		assertSame(Ref.Storage.LOOSE, ref.getStorage());
276 
277 		Ref dst = ref.getTarget();
278 		assertNotNull("has target", dst);
279 		assertEquals("refs/heads/master", dst.getName());
280 
281 		assertSame(dst.getObjectId(), ref.getObjectId());
282 		assertSame(dst.getPeeledObjectId(), ref.getPeeledObjectId());
283 		assertEquals(dst.isPeeled(), ref.isPeeled());
284 	}
285 
286 	private static void checkContainsRef(Collection<Ref> haystack, Ref needle) {
287 		for (Ref ref : haystack) {
288 			if (ref.getName().equals(needle.getName()) &&
289 					ref.getObjectId().equals(needle.getObjectId())) {
290 				return;
291 			}
292 		}
293 		fail("list " + haystack + " does not contain ref " + needle);
294 	}
295 
296 	@Test
297 	public void testGetRefsByPrefix() throws IOException {
298 		List<Ref> refs = db.getRefDatabase().getRefsByPrefix("refs/heads/g");
299 		assertEquals(2, refs.size());
300 		checkContainsRef(refs, db.exactRef("refs/heads/g"));
301 		checkContainsRef(refs, db.exactRef("refs/heads/gitlink"));
302 
303 		refs = db.getRefDatabase().getRefsByPrefix("refs/heads/prefix/");
304 		assertEquals(1, refs.size());
305 		checkContainsRef(refs, db.exactRef("refs/heads/prefix/a"));
306 	}
307 
308 	@Test
309 	public void testGetRefsByPrefixes() throws IOException {
310 		List<Ref> refs = db.getRefDatabase().getRefsByPrefix();
311 		assertEquals(0, refs.size());
312 
313 		refs = db.getRefDatabase().getRefsByPrefix("refs/heads/p",
314 				"refs/tags/A");
315 		assertEquals(3, refs.size());
316 		checkContainsRef(refs, db.exactRef("refs/heads/pa"));
317 		checkContainsRef(refs, db.exactRef("refs/heads/prefix/a"));
318 		checkContainsRef(refs, db.exactRef("refs/tags/A"));
319 	}
320 
321 	@Test
322 	public void testGetRefsExcludingPrefix() throws IOException {
323 		Set<String> exclude = new HashSet<>();
324 		exclude.add("refs/tags");
325 		// HEAD + 12 refs/heads are present here.
326 		List<Ref> refs =
327 				db.getRefDatabase().getRefsByPrefixWithExclusions(RefDatabase.ALL, exclude);
328 		assertEquals(13, refs.size());
329 		checkContainsRef(refs, db.exactRef("HEAD"));
330 		checkContainsRef(refs, db.exactRef("refs/heads/a"));
331 		for (Ref notInResult : db.getRefDatabase().getRefsByPrefix("refs/tags")) {
332 			assertFalse(refs.contains(notInResult));
333 		}
334 	}
335 
336 	@Test
337 	public void testGetRefsExcludingPrefixes() throws IOException {
338 		Set<String> exclude = new HashSet<>();
339 		exclude.add("refs/tags/");
340 		exclude.add("refs/heads/");
341 		List<Ref> refs = db.getRefDatabase().getRefsByPrefixWithExclusions(RefDatabase.ALL, exclude);
342 		assertEquals(1, refs.size());
343 		checkContainsRef(refs, db.exactRef("HEAD"));
344 	}
345 
346 	@Test
347 	public void testGetRefsExcludingNonExistingPrefixes() throws IOException {
348 		Set<String> prefixes = new HashSet<>();
349 		prefixes.add("refs/tags/");
350 		prefixes.add("refs/heads/");
351 		prefixes.add("refs/nonexistent/");
352 		List<Ref> refs = db.getRefDatabase().getRefsByPrefixWithExclusions(RefDatabase.ALL, prefixes);
353 		assertEquals(1, refs.size());
354 		checkContainsRef(refs, db.exactRef("HEAD"));
355 	}
356 
357 	@Test
358 	public void testGetRefsWithPrefixExcludingPrefixes() throws IOException {
359 		Set<String> exclude = new HashSet<>();
360 		exclude.add("refs/heads/pa");
361 		String include = "refs/heads/p";
362 		List<Ref> refs = db.getRefDatabase().getRefsByPrefixWithExclusions(include, exclude);
363 		assertEquals(1, refs.size());
364 		checkContainsRef(refs, db.exactRef("refs/heads/prefix/a"));
365 	}
366 
367 	@Test
368 	public void testGetRefsWithPrefixExcludingOverlappingPrefixes() throws IOException {
369 		Set<String> exclude = new HashSet<>();
370 		exclude.add("refs/heads/pa");
371 		exclude.add("refs/heads/");
372 		exclude.add("refs/heads/p");
373 		exclude.add("refs/tags/");
374 		List<Ref> refs = db.getRefDatabase().getRefsByPrefixWithExclusions(RefDatabase.ALL, exclude);
375 		assertEquals(1, refs.size());
376 		checkContainsRef(refs, db.exactRef("HEAD"));
377 	}
378 
379 	@Test
380 	public void testResolveTipSha1() throws IOException {
381 		ObjectId masterId = db.resolve("refs/heads/master");
382 		Set<Ref> resolved = db.getRefDatabase().getTipsWithSha1(masterId);
383 
384 		assertEquals(2, resolved.size());
385 		checkContainsRef(resolved, db.exactRef("refs/heads/master"));
386 		checkContainsRef(resolved, db.exactRef("HEAD"));
387 
388 		assertEquals(db.getRefDatabase()
389 				.getTipsWithSha1(ObjectId.zeroId()).size(), 0);
390 	}
391 }