| /* |
| * Copyright (C) 2011, Kevin Sawicki <kevin@github.com> 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.api; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertTrue; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.nio.file.Path; |
| |
| import org.eclipse.jgit.api.CheckoutCommand.Stage; |
| import org.eclipse.jgit.api.errors.JGitInternalException; |
| import org.eclipse.jgit.dircache.DirCache; |
| import org.eclipse.jgit.dircache.DirCacheEntry; |
| import org.eclipse.jgit.errors.NoWorkTreeException; |
| import org.eclipse.jgit.junit.RepositoryTestCase; |
| import org.eclipse.jgit.lib.ConfigConstants; |
| import org.eclipse.jgit.lib.ObjectReader; |
| import org.eclipse.jgit.lib.RepositoryState; |
| import org.eclipse.jgit.lib.StoredConfig; |
| import org.eclipse.jgit.revwalk.RevCommit; |
| import org.eclipse.jgit.util.FS; |
| import org.eclipse.jgit.util.FileUtils; |
| import org.junit.Assume; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| /** |
| * Unit tests of path-based uses of {@link CheckoutCommand} |
| */ |
| public class PathCheckoutCommandTest extends RepositoryTestCase { |
| |
| private static final String FILE1 = "f/Test.txt"; |
| |
| private static final String FILE2 = "Test2.txt"; |
| |
| private static final String FILE3 = "Test3.txt"; |
| |
| private static final String LINK = "link"; |
| |
| Git git; |
| |
| RevCommit initialCommit; |
| |
| RevCommit secondCommit; |
| |
| @Override |
| @Before |
| public void setUp() throws Exception { |
| super.setUp(); |
| git = new Git(db); |
| writeTrashFile(FILE1, "1"); |
| writeTrashFile(FILE2, "a"); |
| git.add().addFilepattern(FILE1).addFilepattern(FILE2).call(); |
| initialCommit = git.commit().setMessage("Initial commit").call(); |
| writeTrashFile(FILE1, "2"); |
| writeTrashFile(FILE2, "b"); |
| git.add().addFilepattern(FILE1).addFilepattern(FILE2).call(); |
| secondCommit = git.commit().setMessage("Second commit").call(); |
| writeTrashFile(FILE1, "3"); |
| writeTrashFile(FILE2, "c"); |
| git.add().addFilepattern(FILE1).addFilepattern(FILE2).call(); |
| git.commit().setMessage("Third commit").call(); |
| } |
| |
| @Test |
| public void testUpdateSymLink() throws Exception { |
| Assume.assumeTrue(FS.DETECTED.supportsSymlinks()); |
| |
| Path path = writeLink(LINK, FILE1); |
| git.add().addFilepattern(LINK).call(); |
| git.commit().setMessage("Added link").call(); |
| assertEquals("3", read(path.toFile())); |
| |
| writeLink(LINK, FILE2); |
| assertEquals("c", read(path.toFile())); |
| |
| CheckoutCommand co = git.checkout(); |
| co.addPath(LINK).call(); |
| |
| assertEquals("3", read(path.toFile())); |
| } |
| |
| @Test |
| public void testUpdateBrokenSymLinkToDirectory() throws Exception { |
| Assume.assumeTrue(FS.DETECTED.supportsSymlinks()); |
| |
| Path path = writeLink(LINK, "f"); |
| git.add().addFilepattern(LINK).call(); |
| git.commit().setMessage("Added link").call(); |
| assertEquals("f", FileUtils.readSymLink(path.toFile())); |
| assertTrue(path.toFile().exists()); |
| |
| writeLink(LINK, "link_to_nowhere"); |
| assertFalse(path.toFile().exists()); |
| assertEquals("link_to_nowhere", FileUtils.readSymLink(path.toFile())); |
| |
| CheckoutCommand co = git.checkout(); |
| co.addPath(LINK).call(); |
| |
| assertEquals("f", FileUtils.readSymLink(path.toFile())); |
| } |
| |
| @Test |
| public void testUpdateBrokenSymLink() throws Exception { |
| Assume.assumeTrue(FS.DETECTED.supportsSymlinks()); |
| |
| Path path = writeLink(LINK, FILE1); |
| git.add().addFilepattern(LINK).call(); |
| git.commit().setMessage("Added link").call(); |
| assertEquals("3", read(path.toFile())); |
| assertEquals(FILE1, FileUtils.readSymLink(path.toFile())); |
| |
| writeLink(LINK, "link_to_nowhere"); |
| assertFalse(path.toFile().exists()); |
| assertEquals("link_to_nowhere", FileUtils.readSymLink(path.toFile())); |
| |
| CheckoutCommand co = git.checkout(); |
| co.addPath(LINK).call(); |
| |
| assertEquals("3", read(path.toFile())); |
| } |
| |
| @Test |
| public void testUpdateWorkingDirectory() throws Exception { |
| CheckoutCommand co = git.checkout(); |
| File written = writeTrashFile(FILE1, ""); |
| assertEquals("", read(written)); |
| co.addPath(FILE1).call(); |
| assertEquals("3", read(written)); |
| assertEquals("c", read(new File(db.getWorkTree(), FILE2))); |
| } |
| |
| @Test |
| public void testCheckoutFirst() throws Exception { |
| CheckoutCommand co = git.checkout(); |
| File written = writeTrashFile(FILE1, ""); |
| co.setStartPoint(initialCommit).addPath(FILE1).call(); |
| assertEquals("1", read(written)); |
| assertEquals("c", read(new File(db.getWorkTree(), FILE2))); |
| } |
| |
| @Test |
| public void testCheckoutSecond() throws Exception { |
| CheckoutCommand co = git.checkout(); |
| File written = writeTrashFile(FILE1, ""); |
| co.setStartPoint("HEAD~1").addPath(FILE1).call(); |
| assertEquals("2", read(written)); |
| assertEquals("c", read(new File(db.getWorkTree(), FILE2))); |
| } |
| |
| @Test |
| public void testCheckoutMultiple() throws Exception { |
| CheckoutCommand co = git.checkout(); |
| File test = writeTrashFile(FILE1, ""); |
| File test2 = writeTrashFile(FILE2, ""); |
| co.setStartPoint("HEAD~2").addPath(FILE1).addPath(FILE2).call(); |
| assertEquals("1", read(test)); |
| assertEquals("a", read(test2)); |
| } |
| |
| @Test |
| public void testUpdateWorkingDirectoryFromIndex() throws Exception { |
| CheckoutCommand co = git.checkout(); |
| File written = writeTrashFile(FILE1, "3a"); |
| git.add().addFilepattern(FILE1).call(); |
| written = writeTrashFile(FILE1, ""); |
| assertEquals("", read(written)); |
| co.addPath(FILE1).call(); |
| assertEquals("3a", read(written)); |
| assertEquals("c", read(new File(db.getWorkTree(), FILE2))); |
| } |
| |
| @Test |
| public void testUpdateWorkingDirectoryFromHeadWithIndexChange() |
| throws Exception { |
| CheckoutCommand co = git.checkout(); |
| File written = writeTrashFile(FILE1, "3a"); |
| git.add().addFilepattern(FILE1).call(); |
| written = writeTrashFile(FILE1, ""); |
| assertEquals("", read(written)); |
| co.addPath(FILE1).setStartPoint("HEAD").call(); |
| assertEquals("3", read(written)); |
| assertEquals("c", read(new File(db.getWorkTree(), FILE2))); |
| } |
| |
| @Test |
| public void testUpdateWorkingDirectoryFromIndex2() throws Exception { |
| CheckoutCommand co = git.checkout(); |
| fsTick(git.getRepository().getIndexFile()); |
| |
| File written1 = writeTrashFile(FILE1, "3(modified)"); |
| File written2 = writeTrashFile(FILE2, "a(modified)"); |
| fsTick(written2); |
| |
| // make sure that we get unsmudged entries for FILE1 and FILE2 |
| writeTrashFile(FILE3, "foo"); |
| git.add().addFilepattern(FILE3).call(); |
| fsTick(git.getRepository().getIndexFile()); |
| |
| git.add().addFilepattern(FILE1).addFilepattern(FILE2).call(); |
| fsTick(git.getRepository().getIndexFile()); |
| |
| writeTrashFile(FILE1, "3(modified again)"); |
| writeTrashFile(FILE2, "a(modified again)"); |
| fsTick(written2); |
| |
| co.addPath(FILE1).setStartPoint(secondCommit).call(); |
| |
| assertEquals("2", read(written1)); |
| assertEquals("a(modified again)", read(written2)); |
| |
| validateIndex(git); |
| } |
| |
| public static void validateIndex(Git git) throws NoWorkTreeException, |
| IOException { |
| DirCache dc = git.getRepository().lockDirCache(); |
| try (ObjectReader r = git.getRepository().getObjectDatabase() |
| .newReader()) { |
| for (int i = 0; i < dc.getEntryCount(); ++i) { |
| DirCacheEntry entry = dc.getEntry(i); |
| if (entry.getLength() > 0) |
| assertEquals(entry.getLength(), r.getObjectSize( |
| entry.getObjectId(), ObjectReader.OBJ_ANY)); |
| } |
| } finally { |
| dc.unlock(); |
| } |
| } |
| |
| @Test |
| public void testCheckoutMixedNewlines() throws Exception { |
| // "git config core.autocrlf true" |
| StoredConfig config = git.getRepository().getConfig(); |
| config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null, |
| ConfigConstants.CONFIG_KEY_AUTOCRLF, true); |
| config.save(); |
| // edit <FILE1> |
| File written = writeTrashFile(FILE1, "4\r\n4"); |
| assertEquals("4\r\n4", read(written)); |
| // "git add <FILE1>" |
| git.add().addFilepattern(FILE1).call(); |
| // "git commit -m 'CRLF'" |
| git.commit().setMessage("CRLF").call(); |
| // edit <FILE1> |
| written = writeTrashFile(FILE1, "4\n4"); |
| assertEquals("4\n4", read(written)); |
| // "git add <FILE1>" |
| git.add().addFilepattern(FILE1).call(); |
| // "git checkout -- <FILE1> |
| git.checkout().addPath(FILE1).call(); |
| // "git status" => clean |
| Status status = git.status().call(); |
| assertEquals(0, status.getAdded().size()); |
| assertEquals(0, status.getChanged().size()); |
| assertEquals(0, status.getConflicting().size()); |
| assertEquals(0, status.getMissing().size()); |
| assertEquals(0, status.getModified().size()); |
| assertEquals(0, status.getRemoved().size()); |
| assertEquals(0, status.getUntracked().size()); |
| } |
| |
| @Test |
| public void testCheckoutRepository() throws Exception { |
| CheckoutCommand co = git.checkout(); |
| File test = writeTrashFile(FILE1, ""); |
| File test2 = writeTrashFile(FILE2, ""); |
| co.setStartPoint("HEAD~2").setAllPaths(true).call(); |
| assertEquals("1", read(test)); |
| assertEquals("a", read(test2)); |
| } |
| |
| |
| @Test(expected = JGitInternalException.class) |
| public void testCheckoutOfConflictingFileShouldThrow() |
| throws Exception { |
| setupConflictingState(); |
| |
| git.checkout().addPath(FILE1).call(); |
| } |
| |
| @Test |
| public void testCheckoutOurs() throws Exception { |
| setupConflictingState(); |
| |
| git.checkout().setStage(Stage.OURS).addPath(FILE1).call(); |
| |
| assertEquals("3", read(FILE1)); |
| assertStageOneToThree(FILE1); |
| } |
| |
| @Test |
| public void testCheckoutTheirs() throws Exception { |
| setupConflictingState(); |
| |
| git.checkout().setStage(Stage.THEIRS).addPath(FILE1).call(); |
| |
| assertEquals("Conflicting", read(FILE1)); |
| assertStageOneToThree(FILE1); |
| } |
| |
| @Test |
| public void testCheckoutOursWhenNoBase() throws Exception { |
| String file = "added.txt"; |
| |
| git.checkout().setCreateBranch(true).setName("side") |
| .setStartPoint(initialCommit).call(); |
| writeTrashFile(file, "Added on side"); |
| git.add().addFilepattern(file).call(); |
| RevCommit side = git.commit().setMessage("Commit on side").call(); |
| |
| git.checkout().setName("master").call(); |
| writeTrashFile(file, "Added on master"); |
| git.add().addFilepattern(file).call(); |
| git.commit().setMessage("Commit on master").call(); |
| |
| git.merge().include(side).call(); |
| assertEquals(RepositoryState.MERGING, db.getRepositoryState()); |
| |
| DirCache cache = DirCache.read(db.getIndexFile(), db.getFS()); |
| assertEquals("Expected add/add file to not have base stage", |
| DirCacheEntry.STAGE_2, cache.getEntry(file).getStage()); |
| |
| assertTrue(read(file).startsWith("<<<<<<< HEAD")); |
| |
| git.checkout().setStage(Stage.OURS).addPath(file).call(); |
| |
| assertEquals("Added on master", read(file)); |
| |
| cache = DirCache.read(db.getIndexFile(), db.getFS()); |
| assertEquals("Expected conflict stages to still exist after checkout", |
| DirCacheEntry.STAGE_2, cache.getEntry(file).getStage()); |
| } |
| |
| @Test(expected = IllegalStateException.class) |
| public void testStageNotPossibleWithBranch() throws Exception { |
| git.checkout().setStage(Stage.OURS).setStartPoint("master").call(); |
| } |
| |
| private void setupConflictingState() throws Exception { |
| git.checkout().setCreateBranch(true).setName("conflict") |
| .setStartPoint(initialCommit).call(); |
| writeTrashFile(FILE1, "Conflicting"); |
| RevCommit conflict = git.commit().setAll(true) |
| .setMessage("Conflicting change").call(); |
| |
| git.checkout().setName("master").call(); |
| |
| git.merge().include(conflict).call(); |
| assertEquals(RepositoryState.MERGING, db.getRepositoryState()); |
| assertStageOneToThree(FILE1); |
| } |
| |
| private void assertStageOneToThree(String name) throws Exception { |
| DirCache cache = DirCache.read(db.getIndexFile(), db.getFS()); |
| int i = cache.findEntry(name); |
| DirCacheEntry stage1 = cache.getEntry(i); |
| DirCacheEntry stage2 = cache.getEntry(i + 1); |
| DirCacheEntry stage3 = cache.getEntry(i + 2); |
| |
| assertEquals(DirCacheEntry.STAGE_1, stage1.getStage()); |
| assertEquals(DirCacheEntry.STAGE_2, stage2.getStage()); |
| assertEquals(DirCacheEntry.STAGE_3, stage3.getStage()); |
| } |
| } |