/* * Copyright (C) 2015, Google Inc. and others * * This program and the accompanying materials are made available under the * terms of the Eclipse Distribution License v. 1.0 which is available at * https://www.eclipse.org/org/documents/edl-v10.php. * * SPDX-License-Identifier: BSD-3-Clause */ package org.eclipse.jgit.junit; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.util.Date; import java.util.regex.Pattern; import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription; import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.revwalk.RevBlob; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevObject; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.treewalk.TreeWalk; import org.junit.After; import org.junit.Before; import org.junit.Test; public class TestRepositoryTest { private TestRepository tr; private InMemoryRepository repo; private RevWalk rw; @Before public void setUp() throws Exception { tr = new TestRepository<>(new InMemoryRepository( new DfsRepositoryDescription("test"))); repo = tr.getRepository(); rw = tr.getRevWalk(); } @After public void tearDown() { rw.close(); repo.close(); } @Test public void insertChangeId() throws Exception { RevCommit c1 = tr.commit().message("message").insertChangeId().create(); rw.parseBody(c1); assertTrue(Pattern.matches( "^message\n\nChange-Id: I[0-9a-f]{40}\n$", c1.getFullMessage())); RevCommit c2 = tr.commit().message("").insertChangeId().create(); rw.parseBody(c2); assertEquals("\n\nChange-Id: I0000000000000000000000000000000000000000\n", c2.getFullMessage()); } @Test public void insertChangeIdIgnoresExisting() throws Exception { String msg = "message\n" + "\n" + "Change-Id: Ideadbeefdeadbeefdeadbeefdeadbeefdeadbeef\n"; RevCommit c = tr.commit().message(msg).insertChangeId().create(); rw.parseBody(c); assertEquals(msg, c.getFullMessage()); } @Test public void insertExplicitChangeId() throws Exception { RevCommit c = tr.commit().message("message") .insertChangeId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef") .create(); rw.parseBody(c); assertEquals("message\n\n" + "Change-Id: Ideadbeefdeadbeefdeadbeefdeadbeefdeadbeef\n" , c.getFullMessage()); } @Test public void resetFromSymref() throws Exception { repo.updateRef("HEAD").link("refs/heads/master"); Ref head = repo.exactRef("HEAD"); RevCommit master = tr.branch("master").commit().create(); RevCommit branch = tr.branch("branch").commit().create(); RevCommit detached = tr.commit().create(); assertTrue(head.isSymbolic()); assertEquals("refs/heads/master", head.getTarget().getName()); assertEquals(master, repo.exactRef("refs/heads/master").getObjectId()); assertEquals(branch, repo.exactRef("refs/heads/branch").getObjectId()); // Reset to branches preserves symref. tr.reset("master"); head = repo.exactRef("HEAD"); assertEquals(master, head.getObjectId()); assertTrue(head.isSymbolic()); assertEquals("refs/heads/master", head.getTarget().getName()); tr.reset("branch"); head = repo.exactRef("HEAD"); assertEquals(branch, head.getObjectId()); assertTrue(head.isSymbolic()); assertEquals("refs/heads/master", head.getTarget().getName()); ObjectId lastHeadBeforeDetach = head.getObjectId().copy(); // Reset to a SHA-1 detaches. tr.reset(detached); head = repo.exactRef("HEAD"); assertEquals(detached, head.getObjectId()); assertFalse(head.isSymbolic()); tr.reset(detached.name()); head = repo.exactRef("HEAD"); assertEquals(detached, head.getObjectId()); assertFalse(head.isSymbolic()); // Reset back to a branch remains detached. tr.reset("master"); head = repo.exactRef("HEAD"); assertEquals(lastHeadBeforeDetach, head.getObjectId()); assertFalse(head.isSymbolic()); } @Test public void resetFromDetachedHead() throws Exception { Ref head = repo.exactRef("HEAD"); RevCommit master = tr.branch("master").commit().create(); RevCommit branch = tr.branch("branch").commit().create(); RevCommit detached = tr.commit().create(); assertNull(head); assertEquals(master, repo.exactRef("refs/heads/master").getObjectId()); assertEquals(branch, repo.exactRef("refs/heads/branch").getObjectId()); tr.reset("master"); head = repo.exactRef("HEAD"); assertEquals(master, head.getObjectId()); assertFalse(head.isSymbolic()); tr.reset("branch"); head = repo.exactRef("HEAD"); assertEquals(branch, head.getObjectId()); assertFalse(head.isSymbolic()); tr.reset(detached); head = repo.exactRef("HEAD"); assertEquals(detached, head.getObjectId()); assertFalse(head.isSymbolic()); tr.reset(detached.name()); head = repo.exactRef("HEAD"); assertEquals(detached, head.getObjectId()); assertFalse(head.isSymbolic()); } @Test public void amendRef() throws Exception { RevCommit root = tr.commit() .add("todelete", "to be deleted") .create(); RevCommit orig = tr.commit().parent(root) .rm("todelete") .add("foo", "foo contents") .add("bar", "bar contents") .add("dir/baz", "baz contents") .create(); rw.parseBody(orig); tr.branch("master").update(orig); assertEquals("foo contents", blobAsString(orig, "foo")); assertEquals("bar contents", blobAsString(orig, "bar")); assertEquals("baz contents", blobAsString(orig, "dir/baz")); RevCommit amended = tr.amendRef("master") .tick(3) .add("bar", "fixed bar contents") .create(); assertEquals(amended, repo.exactRef("refs/heads/master").getObjectId()); rw.parseBody(amended); assertEquals(1, amended.getParentCount()); assertEquals(root, amended.getParent(0)); assertEquals(orig.getFullMessage(), amended.getFullMessage()); assertEquals(orig.getAuthorIdent(), amended.getAuthorIdent()); // Committer name/email is the same, but time was incremented. assertEquals(new PersonIdent(orig.getCommitterIdent(), new Date(0)), new PersonIdent(amended.getCommitterIdent(), new Date(0))); assertTrue(orig.getCommitTime() < amended.getCommitTime()); assertEquals("foo contents", blobAsString(amended, "foo")); assertEquals("fixed bar contents", blobAsString(amended, "bar")); assertEquals("baz contents", blobAsString(amended, "dir/baz")); assertNull(TreeWalk.forPath(repo, "todelete", amended.getTree())); } @Test public void amendHead() throws Exception { repo.updateRef("HEAD").link("refs/heads/master"); RevCommit root = tr.commit() .add("foo", "foo contents") .create(); RevCommit orig = tr.commit().parent(root) .message("original message") .add("bar", "bar contents") .create(); tr.branch("master").update(orig); RevCommit amended = tr.amendRef("HEAD") .add("foo", "fixed foo contents") .create(); Ref head = repo.exactRef(Constants.HEAD); assertEquals(amended, head.getObjectId()); assertTrue(head.isSymbolic()); assertEquals("refs/heads/master", head.getTarget().getName()); rw.parseBody(amended); assertEquals("original message", amended.getFullMessage()); assertEquals("fixed foo contents", blobAsString(amended, "foo")); assertEquals("bar contents", blobAsString(amended, "bar")); } @Test public void amendCommit() throws Exception { RevCommit root = tr.commit() .add("foo", "foo contents") .create(); RevCommit orig = tr.commit().parent(root) .message("original message") .add("bar", "bar contents") .create(); RevCommit amended = tr.amend(orig.copy()) .add("foo", "fixed foo contents") .create(); rw.parseBody(amended); assertEquals("original message", amended.getFullMessage()); assertEquals("fixed foo contents", blobAsString(amended, "foo")); assertEquals("bar contents", blobAsString(amended, "bar")); } @Test public void commitToUnbornHead() throws Exception { repo.updateRef("HEAD").link("refs/heads/master"); RevCommit root = tr.branch("HEAD").commit().create(); Ref ref = repo.exactRef(Constants.HEAD); assertEquals(root, ref.getObjectId()); assertTrue(ref.isSymbolic()); assertEquals("refs/heads/master", ref.getTarget().getName()); } @Test public void cherryPick() throws Exception { repo.updateRef("HEAD").link("refs/heads/master"); RevCommit head = tr.branch("master").commit() .add("foo", "foo contents\n") .create(); rw.parseBody(head); RevCommit toPick = tr.commit() .parent(tr.commit().create()) // Can't cherry-pick root. .author(new PersonIdent("Cherrypick Author", "cpa@example.com", tr.getDate(), tr.getTimeZone())) .author(new PersonIdent("Cherrypick Committer", "cpc@example.com", tr.getDate(), tr.getTimeZone())) .message("message to cherry-pick") .add("bar", "bar contents\n") .create(); RevCommit result = tr.cherryPick(toPick); rw.parseBody(result); Ref headRef = tr.getRepository().exactRef("HEAD"); assertEquals(result, headRef.getObjectId()); assertTrue(headRef.isSymbolic()); assertEquals("refs/heads/master", headRef.getLeaf().getName()); assertEquals(1, result.getParentCount()); assertEquals(head, result.getParent(0)); assertEquals(toPick.getAuthorIdent(), result.getAuthorIdent()); // Committer name/email matches default, and time was incremented. assertEquals(new PersonIdent(head.getCommitterIdent(), new Date(0)), new PersonIdent(result.getCommitterIdent(), new Date(0))); assertTrue(toPick.getCommitTime() < result.getCommitTime()); assertEquals("message to cherry-pick", result.getFullMessage()); assertEquals("foo contents\n", blobAsString(result, "foo")); assertEquals("bar contents\n", blobAsString(result, "bar")); } @Test public void cherryPickWithContentMerge() throws Exception { RevCommit base = tr.branch("HEAD").commit() .add("foo", "foo contents\n\n") .create(); tr.branch("HEAD").commit() .add("foo", "foo contents\n\nlast line\n") .create(); RevCommit toPick = tr.commit() .message("message to cherry-pick") .parent(base) .add("foo", "changed foo contents\n\n") .create(); RevCommit result = tr.cherryPick(toPick); rw.parseBody(result); assertEquals("message to cherry-pick", result.getFullMessage()); assertEquals("changed foo contents\n\nlast line\n", blobAsString(result, "foo")); } @Test public void cherryPickWithIdenticalContents() throws Exception { RevCommit base = tr.branch("HEAD").commit().add("foo", "foo contents\n") .create(); RevCommit head = tr.branch("HEAD").commit() .parent(base) .add("bar", "bar contents\n") .create(); RevCommit toPick = tr.commit() .parent(base) .message("message to cherry-pick") .add("bar", "bar contents\n") .create(); assertNotEquals(head, toPick); assertNull(tr.cherryPick(toPick)); assertEquals(head, repo.exactRef("HEAD").getObjectId()); } @Test public void reattachToMaster_Race() throws Exception { RevCommit commit = tr.branch("master").commit().create(); tr.branch("master").update(commit); tr.branch("other").update(commit); repo.updateRef("HEAD").link("refs/heads/master"); // Create a detached HEAD that is not an . tr.reset(commit); Ref head = repo.exactRef("HEAD"); assertEquals(commit, head.getObjectId()); assertFalse(head.isSymbolic()); // Try to reattach to master. RefUpdate refUpdate = repo.updateRef("HEAD"); // Make a change during reattachment. repo.updateRef("HEAD").link("refs/heads/other"); assertEquals( RefUpdate.Result.LOCK_FAILURE, refUpdate.link("refs/heads/master")); } @Test public void nonRacingChange() throws Exception { tr.branch("master").update(tr.branch("master").commit().create()); tr.branch("other").update(tr.branch("other").commit().create()); repo.updateRef("HEAD").link("refs/heads/master"); // Try to update HEAD. RefUpdate refUpdate = repo.updateRef("HEAD"); // Proceed a master. This should not affect changing HEAD. tr.branch("master").update(tr.branch("master").commit().create()); assertEquals(RefUpdate.Result.FORCED, refUpdate.link("refs/heads/other")); } private String blobAsString(AnyObjectId treeish, String path) throws Exception { RevObject obj = tr.get(rw.parseTree(treeish), path); assertSame(RevBlob.class, obj.getClass()); ObjectLoader loader = rw.getObjectReader().open(obj); return new String(loader.getCachedBytes(), UTF_8); } }