View Javadoc
1   /*
2    * Copyright (C) 2015, Google Inc.
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.junit;
45  
46  import static java.nio.charset.StandardCharsets.UTF_8;
47  import static org.junit.Assert.assertEquals;
48  import static org.junit.Assert.assertFalse;
49  import static org.junit.Assert.assertNotEquals;
50  import static org.junit.Assert.assertNull;
51  import static org.junit.Assert.assertSame;
52  import static org.junit.Assert.assertTrue;
53  
54  import java.util.Date;
55  import java.util.regex.Pattern;
56  
57  import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
58  import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
59  import org.eclipse.jgit.lib.AnyObjectId;
60  import org.eclipse.jgit.lib.Constants;
61  import org.eclipse.jgit.lib.ObjectId;
62  import org.eclipse.jgit.lib.ObjectLoader;
63  import org.eclipse.jgit.lib.PersonIdent;
64  import org.eclipse.jgit.lib.Ref;
65  import org.eclipse.jgit.lib.RefUpdate;
66  import org.eclipse.jgit.revwalk.RevBlob;
67  import org.eclipse.jgit.revwalk.RevCommit;
68  import org.eclipse.jgit.revwalk.RevObject;
69  import org.eclipse.jgit.revwalk.RevWalk;
70  import org.eclipse.jgit.treewalk.TreeWalk;
71  import org.junit.After;
72  import org.junit.Before;
73  import org.junit.Test;
74  
75  public class TestRepositoryTest {
76  	private TestRepository<InMemoryRepository> tr;
77  	private InMemoryRepository repo;
78  	private RevWalk rw;
79  
80  	@Before
81  	public void setUp() throws Exception {
82  		tr = new TestRepository<>(new InMemoryRepository(
83  				new DfsRepositoryDescription("test")));
84  		repo = tr.getRepository();
85  		rw = tr.getRevWalk();
86  	}
87  
88  	@After
89  	public void tearDown() {
90  		rw.close();
91  		repo.close();
92  	}
93  
94  	@Test
95  	public void insertChangeId() throws Exception {
96  		RevCommit c1 = tr.commit().message("message").insertChangeId().create();
97  		rw.parseBody(c1);
98  		assertTrue(Pattern.matches(
99  				"^message\n\nChange-Id: I[0-9a-f]{40}\n$", c1.getFullMessage()));
100 
101 		RevCommit c2 = tr.commit().message("").insertChangeId().create();
102 		rw.parseBody(c2);
103 		assertEquals("\n\nChange-Id: I0000000000000000000000000000000000000000\n",
104 				c2.getFullMessage());
105 	}
106 
107 	@Test
108 	public void insertChangeIdIgnoresExisting() throws Exception {
109 		String msg = "message\n"
110 				+ "\n"
111 				+	"Change-Id: Ideadbeefdeadbeefdeadbeefdeadbeefdeadbeef\n";
112 		RevCommit c = tr.commit().message(msg).insertChangeId().create();
113 		rw.parseBody(c);
114 		assertEquals(msg, c.getFullMessage());
115 	}
116 
117 	@Test
118 	public void insertExplicitChangeId() throws Exception {
119 		RevCommit c = tr.commit().message("message")
120 				.insertChangeId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")
121 				.create();
122 		rw.parseBody(c);
123 		assertEquals("message\n\n"
124 				+ "Change-Id: Ideadbeefdeadbeefdeadbeefdeadbeefdeadbeef\n"
125 				, c.getFullMessage());
126 	}
127 
128 	@Test
129 	public void resetFromSymref() throws Exception {
130 		repo.updateRef("HEAD").link("refs/heads/master");
131 		Ref head = repo.exactRef("HEAD");
132 		RevCommit master = tr.branch("master").commit().create();
133 		RevCommit branch = tr.branch("branch").commit().create();
134 		RevCommit detached = tr.commit().create();
135 
136 		assertTrue(head.isSymbolic());
137 		assertEquals("refs/heads/master", head.getTarget().getName());
138 		assertEquals(master, repo.exactRef("refs/heads/master").getObjectId());
139 		assertEquals(branch, repo.exactRef("refs/heads/branch").getObjectId());
140 
141 		// Reset to branches preserves symref.
142 		tr.reset("master");
143 		head = repo.exactRef("HEAD");
144 		assertEquals(master, head.getObjectId());
145 		assertTrue(head.isSymbolic());
146 		assertEquals("refs/heads/master", head.getTarget().getName());
147 
148 		tr.reset("branch");
149 		head = repo.exactRef("HEAD");
150 		assertEquals(branch, head.getObjectId());
151 		assertTrue(head.isSymbolic());
152 		assertEquals("refs/heads/master", head.getTarget().getName());
153 		ObjectId lastHeadBeforeDetach = head.getObjectId().copy();
154 
155 		// Reset to a SHA-1 detaches.
156 		tr.reset(detached);
157 		head = repo.exactRef("HEAD");
158 		assertEquals(detached, head.getObjectId());
159 		assertFalse(head.isSymbolic());
160 
161 		tr.reset(detached.name());
162 		head = repo.exactRef("HEAD");
163 		assertEquals(detached, head.getObjectId());
164 		assertFalse(head.isSymbolic());
165 
166 		// Reset back to a branch remains detached.
167 		tr.reset("master");
168 		head = repo.exactRef("HEAD");
169 		assertEquals(lastHeadBeforeDetach, head.getObjectId());
170 		assertFalse(head.isSymbolic());
171 	}
172 
173 	@Test
174 	public void resetFromDetachedHead() throws Exception {
175 		Ref head = repo.exactRef("HEAD");
176 		RevCommit master = tr.branch("master").commit().create();
177 		RevCommit branch = tr.branch("branch").commit().create();
178 		RevCommit detached = tr.commit().create();
179 
180 		assertNull(head);
181 		assertEquals(master, repo.exactRef("refs/heads/master").getObjectId());
182 		assertEquals(branch, repo.exactRef("refs/heads/branch").getObjectId());
183 
184 		tr.reset("master");
185 		head = repo.exactRef("HEAD");
186 		assertEquals(master, head.getObjectId());
187 		assertFalse(head.isSymbolic());
188 
189 		tr.reset("branch");
190 		head = repo.exactRef("HEAD");
191 		assertEquals(branch, head.getObjectId());
192 		assertFalse(head.isSymbolic());
193 
194 		tr.reset(detached);
195 		head = repo.exactRef("HEAD");
196 		assertEquals(detached, head.getObjectId());
197 		assertFalse(head.isSymbolic());
198 
199 		tr.reset(detached.name());
200 		head = repo.exactRef("HEAD");
201 		assertEquals(detached, head.getObjectId());
202 		assertFalse(head.isSymbolic());
203 	}
204 
205 	@Test
206 	public void amendRef() throws Exception {
207 		RevCommit root = tr.commit()
208 				.add("todelete", "to be deleted")
209 				.create();
210 		RevCommit orig = tr.commit().parent(root)
211 				.rm("todelete")
212 				.add("foo", "foo contents")
213 				.add("bar", "bar contents")
214 				.add("dir/baz", "baz contents")
215 				.create();
216 		rw.parseBody(orig);
217 		tr.branch("master").update(orig);
218 		assertEquals("foo contents", blobAsString(orig, "foo"));
219 		assertEquals("bar contents", blobAsString(orig, "bar"));
220 		assertEquals("baz contents", blobAsString(orig, "dir/baz"));
221 
222 		RevCommit amended = tr.amendRef("master")
223 				.tick(3)
224 				.add("bar", "fixed bar contents")
225 				.create();
226 		assertEquals(amended, repo.exactRef("refs/heads/master").getObjectId());
227 		rw.parseBody(amended);
228 
229 		assertEquals(1, amended.getParentCount());
230 		assertEquals(root, amended.getParent(0));
231 		assertEquals(orig.getFullMessage(), amended.getFullMessage());
232 		assertEquals(orig.getAuthorIdent(), amended.getAuthorIdent());
233 
234 		// Committer name/email is the same, but time was incremented.
235 		assertEquals(new PersonIdent(orig.getCommitterIdent(), new Date(0)),
236 				new PersonIdent(amended.getCommitterIdent(), new Date(0)));
237 		assertTrue(orig.getCommitTime() < amended.getCommitTime());
238 
239 		assertEquals("foo contents", blobAsString(amended, "foo"));
240 		assertEquals("fixed bar contents", blobAsString(amended, "bar"));
241 		assertEquals("baz contents", blobAsString(amended, "dir/baz"));
242 		assertNull(TreeWalk.forPath(repo, "todelete", amended.getTree()));
243 	}
244 
245 	@Test
246 	public void amendHead() throws Exception {
247 		repo.updateRef("HEAD").link("refs/heads/master");
248 		RevCommit root = tr.commit()
249 				.add("foo", "foo contents")
250 				.create();
251 		RevCommit orig = tr.commit().parent(root)
252 				.message("original message")
253 				.add("bar", "bar contents")
254 				.create();
255 		tr.branch("master").update(orig);
256 
257 		RevCommit amended = tr.amendRef("HEAD")
258 				.add("foo", "fixed foo contents")
259 				.create();
260 
261 		Ref head = repo.exactRef(Constants.HEAD);
262 		assertEquals(amended, head.getObjectId());
263 		assertTrue(head.isSymbolic());
264 		assertEquals("refs/heads/master", head.getTarget().getName());
265 
266 		rw.parseBody(amended);
267 		assertEquals("original message", amended.getFullMessage());
268 		assertEquals("fixed foo contents", blobAsString(amended, "foo"));
269 		assertEquals("bar contents", blobAsString(amended, "bar"));
270 	}
271 
272 	@Test
273 	public void amendCommit() throws Exception {
274 		RevCommit root = tr.commit()
275 				.add("foo", "foo contents")
276 				.create();
277 		RevCommit orig = tr.commit().parent(root)
278 				.message("original message")
279 				.add("bar", "bar contents")
280 				.create();
281 		RevCommit amended = tr.amend(orig.copy())
282 				.add("foo", "fixed foo contents")
283 				.create();
284 
285 		rw.parseBody(amended);
286 		assertEquals("original message", amended.getFullMessage());
287 		assertEquals("fixed foo contents", blobAsString(amended, "foo"));
288 		assertEquals("bar contents", blobAsString(amended, "bar"));
289 	}
290 
291 	@Test
292 	public void commitToUnbornHead() throws Exception {
293 		repo.updateRef("HEAD").link("refs/heads/master");
294 		RevCommit root = tr.branch("HEAD").commit().create();
295 		Ref ref = repo.exactRef(Constants.HEAD);
296 		assertEquals(root, ref.getObjectId());
297 		assertTrue(ref.isSymbolic());
298 		assertEquals("refs/heads/master", ref.getTarget().getName());
299 	}
300 
301 	@Test
302 	public void cherryPick() throws Exception {
303 		repo.updateRef("HEAD").link("refs/heads/master");
304 		RevCommit head = tr.branch("master").commit()
305 				.add("foo", "foo contents\n")
306 				.create();
307 		rw.parseBody(head);
308 		RevCommit toPick = tr.commit()
309 				.parent(tr.commit().create()) // Can't cherry-pick root.
310 				.author(new PersonIdent("Cherrypick Author", "cpa@example.com",
311 						tr.getDate(), tr.getTimeZone()))
312 				.author(new PersonIdent("Cherrypick Committer", "cpc@example.com",
313 						tr.getDate(), tr.getTimeZone()))
314 				.message("message to cherry-pick")
315 				.add("bar", "bar contents\n")
316 				.create();
317 		RevCommit result = tr.cherryPick(toPick);
318 		rw.parseBody(result);
319 
320 		Ref headRef = tr.getRepository().exactRef("HEAD");
321 		assertEquals(result, headRef.getObjectId());
322 		assertTrue(headRef.isSymbolic());
323 		assertEquals("refs/heads/master", headRef.getLeaf().getName());
324 
325 		assertEquals(1, result.getParentCount());
326 		assertEquals(head, result.getParent(0));
327 		assertEquals(toPick.getAuthorIdent(), result.getAuthorIdent());
328 
329 		// Committer name/email matches default, and time was incremented.
330 		assertEquals(new PersonIdent(head.getCommitterIdent(), new Date(0)),
331 				new PersonIdent(result.getCommitterIdent(), new Date(0)));
332 		assertTrue(toPick.getCommitTime() < result.getCommitTime());
333 
334 		assertEquals("message to cherry-pick", result.getFullMessage());
335 		assertEquals("foo contents\n", blobAsString(result, "foo"));
336 		assertEquals("bar contents\n", blobAsString(result, "bar"));
337 	}
338 
339 	@Test
340 	public void cherryPickWithContentMerge() throws Exception {
341 		RevCommit base = tr.branch("HEAD").commit()
342 				.add("foo", "foo contents\n\n")
343 				.create();
344 		tr.branch("HEAD").commit()
345 				.add("foo", "foo contents\n\nlast line\n")
346 				.create();
347 		RevCommit toPick = tr.commit()
348 				.message("message to cherry-pick")
349 				.parent(base)
350 				.add("foo", "changed foo contents\n\n")
351 				.create();
352 		RevCommit result = tr.cherryPick(toPick);
353 		rw.parseBody(result);
354 
355 		assertEquals("message to cherry-pick", result.getFullMessage());
356 		assertEquals("changed foo contents\n\nlast line\n",
357 				blobAsString(result, "foo"));
358 	}
359 
360 	@Test
361 	public void cherryPickWithIdenticalContents() throws Exception {
362 		RevCommit base = tr.branch("HEAD").commit().add("foo", "foo contents\n")
363 				.create();
364 		RevCommit head = tr.branch("HEAD").commit()
365 				.parent(base)
366 				.add("bar", "bar contents\n")
367 				.create();
368 		RevCommit toPick = tr.commit()
369 				.parent(base)
370 				.message("message to cherry-pick")
371 				.add("bar", "bar contents\n")
372 				.create();
373 		assertNotEquals(head, toPick);
374 		assertNull(tr.cherryPick(toPick));
375 		assertEquals(head, repo.exactRef("HEAD").getObjectId());
376 	}
377 
378 	@Test
379 	public void reattachToMaster_Race() throws Exception {
380 		RevCommit commit = tr.branch("master").commit().create();
381 		tr.branch("master").update(commit);
382 		tr.branch("other").update(commit);
383 		repo.updateRef("HEAD").link("refs/heads/master");
384 
385 		// Create a detached HEAD that is not an .
386 		tr.reset(commit);
387 		Ref head = repo.exactRef("HEAD");
388 		assertEquals(commit, head.getObjectId());
389 		assertFalse(head.isSymbolic());
390 
391 		// Try to reattach to master.
392 		RefUpdate refUpdate = repo.updateRef("HEAD");
393 
394 		// Make a change during reattachment.
395 		repo.updateRef("HEAD").link("refs/heads/other");
396 
397 		assertEquals(
398 				RefUpdate.Result.LOCK_FAILURE, refUpdate.link("refs/heads/master"));
399 	}
400 
401 	@Test
402 	public void nonRacingChange() throws Exception {
403 		tr.branch("master").update(tr.branch("master").commit().create());
404 		tr.branch("other").update(tr.branch("other").commit().create());
405 		repo.updateRef("HEAD").link("refs/heads/master");
406 
407 		// Try to update HEAD.
408 		RefUpdate refUpdate = repo.updateRef("HEAD");
409 
410 		// Proceed a master. This should not affect changing HEAD.
411 		tr.branch("master").update(tr.branch("master").commit().create());
412 
413 		assertEquals(RefUpdate.Result.FORCED, refUpdate.link("refs/heads/other"));
414 	}
415 
416 	private String blobAsString(AnyObjectId treeish, String path)
417 			throws Exception {
418 		RevObject obj = tr.get(rw.parseTree(treeish), path);
419 		assertSame(RevBlob.class, obj.getClass());
420 		ObjectLoader loader = rw.getObjectReader().open(obj);
421 		return new String(loader.getCachedBytes(), UTF_8);
422 	}
423 }