| /* |
| * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> |
| * Copyright (C) 2011, Matthias Sohn <matthias.sohn@sap.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 java.time.Instant.EPOCH; |
| import static org.eclipse.jgit.lib.Constants.MASTER; |
| import static org.eclipse.jgit.lib.Constants.R_HEADS; |
| import static org.hamcrest.CoreMatchers.is; |
| import static org.hamcrest.MatcherAssert.assertThat; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertSame; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.net.MalformedURLException; |
| import java.net.URISyntaxException; |
| import java.nio.file.Files; |
| import java.nio.file.attribute.FileTime; |
| import java.time.Instant; |
| |
| import org.eclipse.jgit.api.CheckoutResult.Status; |
| import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode; |
| import org.eclipse.jgit.api.errors.CheckoutConflictException; |
| import org.eclipse.jgit.api.errors.GitAPIException; |
| import org.eclipse.jgit.api.errors.InvalidRefNameException; |
| import org.eclipse.jgit.api.errors.InvalidRemoteException; |
| import org.eclipse.jgit.api.errors.JGitInternalException; |
| import org.eclipse.jgit.api.errors.RefAlreadyExistsException; |
| import org.eclipse.jgit.api.errors.RefNotFoundException; |
| import org.eclipse.jgit.api.errors.TransportException; |
| import org.eclipse.jgit.dircache.DirCache; |
| import org.eclipse.jgit.dircache.DirCacheEntry; |
| import org.eclipse.jgit.junit.JGitTestUtil; |
| import org.eclipse.jgit.junit.RepositoryTestCase; |
| import org.eclipse.jgit.junit.time.TimeUtil; |
| import org.eclipse.jgit.lfs.BuiltinLFS; |
| import org.eclipse.jgit.lib.ConfigConstants; |
| import org.eclipse.jgit.lib.Constants; |
| import org.eclipse.jgit.lib.Ref; |
| import org.eclipse.jgit.lib.RefUpdate; |
| import org.eclipse.jgit.lib.Repository; |
| import org.eclipse.jgit.lib.Sets; |
| import org.eclipse.jgit.lib.StoredConfig; |
| import org.eclipse.jgit.revwalk.RevCommit; |
| import org.eclipse.jgit.storage.file.FileBasedConfig; |
| import org.eclipse.jgit.transport.RemoteConfig; |
| import org.eclipse.jgit.transport.URIish; |
| import org.eclipse.jgit.util.FS; |
| import org.eclipse.jgit.util.FileUtils; |
| import org.eclipse.jgit.util.SystemReader; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| public class CheckoutCommandTest extends RepositoryTestCase { |
| private Git git; |
| |
| RevCommit initialCommit; |
| |
| RevCommit secondCommit; |
| |
| @Override |
| @Before |
| public void setUp() throws Exception { |
| BuiltinLFS.register(); |
| super.setUp(); |
| git = new Git(db); |
| // commit something |
| writeTrashFile("Test.txt", "Hello world"); |
| git.add().addFilepattern("Test.txt").call(); |
| initialCommit = git.commit().setMessage("Initial commit").call(); |
| |
| // create a test branch and switch to it |
| git.branchCreate().setName("test").call(); |
| RefUpdate rup = db.updateRef(Constants.HEAD); |
| rup.link("refs/heads/test"); |
| |
| // commit something on the test branch |
| writeTrashFile("Test.txt", "Some change"); |
| git.add().addFilepattern("Test.txt").call(); |
| secondCommit = git.commit().setMessage("Second commit").call(); |
| } |
| |
| @Test |
| public void testSimpleCheckout() throws Exception { |
| git.checkout().setName("test").call(); |
| } |
| |
| @Test |
| public void testCheckout() throws Exception { |
| git.checkout().setName("test").call(); |
| assertEquals("[Test.txt, mode:100644, content:Some change]", |
| indexState(CONTENT)); |
| Ref result = git.checkout().setName("master").call(); |
| assertEquals("[Test.txt, mode:100644, content:Hello world]", |
| indexState(CONTENT)); |
| assertEquals("refs/heads/master", result.getName()); |
| assertEquals("refs/heads/master", git.getRepository().getFullBranch()); |
| } |
| |
| @Test |
| public void testCheckoutForced() throws Exception { |
| writeTrashFile("Test.txt", "Garbage"); |
| try { |
| git.checkout().setName("master").call().getObjectId(); |
| fail("Expected CheckoutConflictException didn't occur"); |
| } catch (CheckoutConflictException e) { |
| // Expected |
| } |
| assertEquals(initialCommit.getId(), git.checkout().setName("master") |
| .setForced(true).call().getObjectId()); |
| } |
| |
| @Test |
| public void testCheckoutForced_deleteFileAndRestore() throws Exception { |
| File testFile = new File(db.getWorkTree(), "Test.txt"); |
| assertTrue(testFile.exists()); |
| |
| assertEquals("test", git.getRepository().getBranch()); |
| FileUtils.delete(testFile); |
| assertFalse(testFile.exists()); |
| // Switch from "test" to "master". |
| assertEquals(initialCommit.getId(), git.checkout().setName("master") |
| .setForced(true).call().getObjectId()); |
| assertTrue(testFile.exists()); |
| |
| assertEquals("master", git.getRepository().getBranch()); |
| FileUtils.delete(testFile); |
| assertFalse(testFile.exists()); |
| // Stay in current branch. |
| assertEquals(initialCommit.getId(), git.checkout().setName("master") |
| .setForced(true).call().getObjectId()); |
| assertTrue(testFile.exists()); |
| } |
| |
| @Test |
| public void testCheckoutForcedNoChangeNotInIndex() throws Exception { |
| git.checkout().setCreateBranch(true).setName("test2").call(); |
| File f = writeTrashFile("NewFile.txt", "New file"); |
| git.add().addFilepattern("NewFile.txt").call(); |
| git.commit().setMessage("New file created").call(); |
| git.checkout().setName("test").call(); |
| assertFalse("NewFile.txt should not exist", f.exists()); |
| writeTrashFile("NewFile.txt", "New file"); |
| git.add().addFilepattern("NewFile.txt").call(); |
| git.commit().setMessage("New file created again with same content") |
| .call(); |
| // Now remove the file from the index only. So it exists in both |
| // commits, and in the working tree, but not in the index. |
| git.rm().addFilepattern("NewFile.txt").setCached(true).call(); |
| assertTrue("NewFile.txt should exist", f.isFile()); |
| git.checkout().setForced(true).setName("test2").call(); |
| assertTrue("NewFile.txt should exist", f.isFile()); |
| assertEquals(Constants.R_HEADS + "test2", git.getRepository() |
| .exactRef(Constants.HEAD).getTarget().getName()); |
| assertTrue("Force checkout should have undone git rm --cached", |
| git.status().call().isClean()); |
| } |
| |
| @Test |
| public void testCheckoutNoChangeNotInIndex() throws Exception { |
| git.checkout().setCreateBranch(true).setName("test2").call(); |
| File f = writeTrashFile("NewFile.txt", "New file"); |
| git.add().addFilepattern("NewFile.txt").call(); |
| git.commit().setMessage("New file created").call(); |
| git.checkout().setName("test").call(); |
| assertFalse("NewFile.txt should not exist", f.exists()); |
| writeTrashFile("NewFile.txt", "New file"); |
| git.add().addFilepattern("NewFile.txt").call(); |
| git.commit().setMessage("New file created again with same content") |
| .call(); |
| // Now remove the file from the index only. So it exists in both |
| // commits, and in the working tree, but not in the index. |
| git.rm().addFilepattern("NewFile.txt").setCached(true).call(); |
| assertTrue("NewFile.txt should exist", f.isFile()); |
| git.checkout().setName("test2").call(); |
| assertTrue("NewFile.txt should exist", f.isFile()); |
| assertEquals(Constants.R_HEADS + "test2", git.getRepository() |
| .exactRef(Constants.HEAD).getTarget().getName()); |
| org.eclipse.jgit.api.Status status = git.status().call(); |
| assertEquals("[NewFile.txt]", status.getRemoved().toString()); |
| assertEquals("[NewFile.txt]", status.getUntracked().toString()); |
| } |
| |
| @Test |
| public void testCreateBranchOnCheckout() throws Exception { |
| git.checkout().setCreateBranch(true).setName("test2").call(); |
| assertNotNull(db.exactRef("refs/heads/test2")); |
| } |
| |
| @Test |
| public void testCheckoutToNonExistingBranch() throws GitAPIException { |
| try { |
| git.checkout().setName("badbranch").call(); |
| fail("Should have failed"); |
| } catch (RefNotFoundException e) { |
| // except to hit here |
| } |
| } |
| |
| @Test |
| public void testCheckoutWithConflict() throws Exception { |
| CheckoutCommand co = git.checkout(); |
| try { |
| writeTrashFile("Test.txt", "Another change"); |
| assertEquals(Status.NOT_TRIED, co.getResult().getStatus()); |
| co.setName("master").call(); |
| fail("Should have failed"); |
| } catch (Exception e) { |
| assertEquals(Status.CONFLICTS, co.getResult().getStatus()); |
| assertTrue(co.getResult().getConflictList().contains("Test.txt")); |
| } |
| git.checkout().setName("master").setForced(true).call(); |
| assertThat(read("Test.txt"), is("Hello world")); |
| } |
| |
| @Test |
| public void testCheckoutWithNonDeletedFiles() throws Exception { |
| File testFile = writeTrashFile("temp", ""); |
| try (FileInputStream fis = new FileInputStream(testFile)) { |
| FileUtils.delete(testFile); |
| return; |
| } catch (IOException e) { |
| // the test makes only sense if deletion of |
| // a file with open stream fails |
| } |
| FileUtils.delete(testFile); |
| CheckoutCommand co = git.checkout(); |
| // delete Test.txt in branch test |
| testFile = new File(db.getWorkTree(), "Test.txt"); |
| assertTrue(testFile.exists()); |
| FileUtils.delete(testFile); |
| assertFalse(testFile.exists()); |
| git.add().addFilepattern("Test.txt"); |
| git.commit().setMessage("Delete Test.txt").setAll(true).call(); |
| git.checkout().setName("master").call(); |
| assertTrue(testFile.exists()); |
| // lock the file so it can't be deleted (in Windows, that is) |
| try (FileInputStream fis = new FileInputStream(testFile)) { |
| assertEquals(Status.NOT_TRIED, co.getResult().getStatus()); |
| co.setName("test").call(); |
| assertTrue(testFile.exists()); |
| assertEquals(Status.NONDELETED, co.getResult().getStatus()); |
| assertTrue(co.getResult().getUndeletedList().contains("Test.txt")); |
| } |
| } |
| |
| @Test |
| public void testCheckoutCommit() throws Exception { |
| Ref result = git.checkout().setName(initialCommit.name()).call(); |
| assertEquals("[Test.txt, mode:100644, content:Hello world]", |
| indexState(CONTENT)); |
| assertNull(result); |
| assertEquals(initialCommit.name(), git.getRepository().getFullBranch()); |
| } |
| |
| @Test |
| public void testCheckoutLightweightTag() throws Exception { |
| git.tag().setAnnotated(false).setName("test-tag") |
| .setObjectId(initialCommit).call(); |
| Ref result = git.checkout().setName("test-tag").call(); |
| |
| assertNull(result); |
| assertEquals(initialCommit.getId(), db.resolve(Constants.HEAD)); |
| assertHeadDetached(); |
| } |
| |
| @Test |
| public void testCheckoutAnnotatedTag() throws Exception { |
| git.tag().setAnnotated(true).setName("test-tag") |
| .setObjectId(initialCommit).call(); |
| Ref result = git.checkout().setName("test-tag").call(); |
| |
| assertNull(result); |
| assertEquals(initialCommit.getId(), db.resolve(Constants.HEAD)); |
| assertHeadDetached(); |
| } |
| |
| @Test |
| public void testCheckoutRemoteTrackingWithUpstream() throws Exception { |
| Repository db2 = createRepositoryWithRemote(); |
| addRepoToClose(db2); |
| |
| Git.wrap(db2).checkout().setCreateBranch(true).setName("test") |
| .setStartPoint("origin/test") |
| .setUpstreamMode(SetupUpstreamMode.TRACK).call(); |
| |
| assertEquals("refs/heads/test", |
| db2.exactRef(Constants.HEAD).getTarget().getName()); |
| StoredConfig config = db2.getConfig(); |
| assertEquals("origin", config.getString( |
| ConfigConstants.CONFIG_BRANCH_SECTION, "test", |
| ConfigConstants.CONFIG_KEY_REMOTE)); |
| assertEquals("refs/heads/test", config.getString( |
| ConfigConstants.CONFIG_BRANCH_SECTION, "test", |
| ConfigConstants.CONFIG_KEY_MERGE)); |
| } |
| |
| @Test |
| public void testCheckoutRemoteTrackingWithoutLocalBranch() throws Exception { |
| Repository db2 = createRepositoryWithRemote(); |
| addRepoToClose(db2); |
| |
| // checkout remote tracking branch in second repository |
| // (no local branches exist yet in second repository) |
| Git.wrap(db2).checkout().setName("remotes/origin/test").call(); |
| assertEquals("[Test.txt, mode:100644, content:Some change]", |
| indexState(db2, CONTENT)); |
| } |
| |
| |
| |
| @Test |
| public void testCheckoutOfFileWithInexistentParentDir() throws Exception { |
| File a = writeTrashFile("dir/a.txt", "A"); |
| writeTrashFile("dir/b.txt", "A"); |
| git.add().addFilepattern("dir/a.txt").addFilepattern("dir/b.txt") |
| .call(); |
| git.commit().setMessage("Added dir").call(); |
| |
| File dir = new File(db.getWorkTree(), "dir"); |
| FileUtils.delete(dir, FileUtils.RECURSIVE); |
| |
| git.checkout().addPath("dir/a.txt").call(); |
| assertTrue(a.exists()); |
| } |
| |
| @Test |
| public void testCheckoutOfDirectoryShouldBeRecursive() throws Exception { |
| File a = writeTrashFile("dir/a.txt", "A"); |
| File b = writeTrashFile("dir/sub/b.txt", "B"); |
| git.add().addFilepattern("dir").call(); |
| git.commit().setMessage("Added dir").call(); |
| |
| write(a, "modified"); |
| write(b, "modified"); |
| git.checkout().addPath("dir").call(); |
| |
| assertThat(read(a), is("A")); |
| assertThat(read(b), is("B")); |
| } |
| |
| @Test |
| public void testCheckoutAllPaths() throws Exception { |
| File a = writeTrashFile("dir/a.txt", "A"); |
| File b = writeTrashFile("dir/sub/b.txt", "B"); |
| git.add().addFilepattern("dir").call(); |
| git.commit().setMessage("Added dir").call(); |
| |
| write(a, "modified"); |
| write(b, "modified"); |
| git.checkout().setAllPaths(true).call(); |
| |
| assertThat(read(a), is("A")); |
| assertThat(read(b), is("B")); |
| } |
| |
| @Test |
| public void testCheckoutWithStartPoint() throws Exception { |
| File a = writeTrashFile("a.txt", "A"); |
| git.add().addFilepattern("a.txt").call(); |
| RevCommit first = git.commit().setMessage("Added a").call(); |
| |
| write(a, "other"); |
| git.commit().setAll(true).setMessage("Other").call(); |
| |
| git.checkout().setCreateBranch(true).setName("a") |
| .setStartPoint(first.getId().getName()).call(); |
| |
| assertThat(read(a), is("A")); |
| } |
| |
| @Test |
| public void testCheckoutWithStartPointOnlyCertainFiles() throws Exception { |
| File a = writeTrashFile("a.txt", "A"); |
| File b = writeTrashFile("b.txt", "B"); |
| git.add().addFilepattern("a.txt").addFilepattern("b.txt").call(); |
| RevCommit first = git.commit().setMessage("First").call(); |
| |
| write(a, "other"); |
| write(b, "other"); |
| git.commit().setAll(true).setMessage("Other").call(); |
| |
| git.checkout().setCreateBranch(true).setName("a") |
| .setStartPoint(first.getId().getName()).addPath("a.txt").call(); |
| |
| assertThat(read(a), is("A")); |
| assertThat(read(b), is("other")); |
| } |
| |
| @Test |
| public void testDetachedHeadOnCheckout() throws JGitInternalException, |
| IOException, GitAPIException { |
| CheckoutCommand co = git.checkout(); |
| co.setName("master").call(); |
| |
| String commitId = db.exactRef(R_HEADS + MASTER).getObjectId().name(); |
| co = git.checkout(); |
| co.setName(commitId).call(); |
| |
| assertHeadDetached(); |
| } |
| |
| @Test |
| public void testUpdateSmudgedEntries() throws Exception { |
| git.branchCreate().setName("test2").call(); |
| RefUpdate rup = db.updateRef(Constants.HEAD); |
| rup.link("refs/heads/test2"); |
| |
| File file = new File(db.getWorkTree(), "Test.txt"); |
| long size = file.length(); |
| Instant mTime = TimeUtil.setLastModifiedWithOffset(file.toPath(), |
| -5000L); |
| |
| DirCache cache = DirCache.lock(db.getIndexFile(), db.getFS()); |
| DirCacheEntry entry = cache.getEntry("Test.txt"); |
| assertNotNull(entry); |
| entry.setLength(0); |
| entry.setLastModified(EPOCH); |
| cache.write(); |
| assertTrue(cache.commit()); |
| |
| cache = DirCache.read(db.getIndexFile(), db.getFS()); |
| entry = cache.getEntry("Test.txt"); |
| assertNotNull(entry); |
| assertEquals(0, entry.getLength()); |
| assertEquals(EPOCH, entry.getLastModifiedInstant()); |
| |
| Files.setLastModifiedTime(db.getIndexFile().toPath(), |
| FileTime.from(FS.DETECTED |
| .lastModifiedInstant(db.getIndexFile()) |
| .minusMillis(5000L))); |
| |
| assertNotNull(git.checkout().setName("test").call()); |
| |
| cache = DirCache.read(db.getIndexFile(), db.getFS()); |
| entry = cache.getEntry("Test.txt"); |
| assertNotNull(entry); |
| assertEquals(size, entry.getLength()); |
| assertEquals(mTime, entry.getLastModifiedInstant()); |
| } |
| |
| @Test |
| public void testCheckoutOrphanBranch() throws Exception { |
| CheckoutCommand co = newOrphanBranchCommand(); |
| assertCheckoutRef(co.call()); |
| |
| File HEAD = new File(trash, ".git/HEAD"); |
| String headRef = read(HEAD); |
| assertEquals("ref: refs/heads/orphanbranch\n", headRef); |
| assertEquals(2, trash.list().length); |
| |
| File heads = new File(trash, ".git/refs/heads"); |
| assertEquals(2, heads.listFiles().length); |
| |
| this.assertNoHead(); |
| this.assertRepositoryCondition(1); |
| assertEquals(CheckoutResult.NOT_TRIED_RESULT, co.getResult()); |
| } |
| |
| private Repository createRepositoryWithRemote() throws IOException, |
| URISyntaxException, MalformedURLException, GitAPIException, |
| InvalidRemoteException, TransportException { |
| // create second repository |
| Repository db2 = createWorkRepository(); |
| try (Git git2 = new Git(db2)) { |
| // setup the second repository to fetch from the first repository |
| final StoredConfig config = db2.getConfig(); |
| RemoteConfig remoteConfig = new RemoteConfig(config, "origin"); |
| URIish uri = new URIish(db.getDirectory().toURI().toURL()); |
| remoteConfig.addURI(uri); |
| remoteConfig.update(config); |
| config.save(); |
| |
| // fetch from first repository |
| git2.fetch().setRemote("origin") |
| .setRefSpecs("+refs/heads/*:refs/remotes/origin/*").call(); |
| return db2; |
| } |
| } |
| |
| private CheckoutCommand newOrphanBranchCommand() { |
| return git.checkout().setOrphan(true) |
| .setName("orphanbranch"); |
| } |
| |
| private static void assertCheckoutRef(Ref ref) { |
| assertNotNull(ref); |
| assertEquals("refs/heads/orphanbranch", ref.getTarget().getName()); |
| } |
| |
| private void assertNoHead() throws IOException { |
| assertNull(db.resolve("HEAD")); |
| } |
| |
| private void assertHeadDetached() throws IOException { |
| Ref head = db.exactRef(Constants.HEAD); |
| assertFalse(head.isSymbolic()); |
| assertSame(head, head.getTarget()); |
| } |
| |
| private void assertRepositoryCondition(int files) throws GitAPIException { |
| org.eclipse.jgit.api.Status status = this.git.status().call(); |
| assertFalse(status.isClean()); |
| assertEquals(files, status.getAdded().size()); |
| } |
| |
| @Test |
| public void testCreateOrphanBranchWithStartCommit() throws Exception { |
| CheckoutCommand co = newOrphanBranchCommand(); |
| Ref ref = co.setStartPoint(initialCommit).call(); |
| assertCheckoutRef(ref); |
| assertEquals(2, trash.list().length); |
| this.assertNoHead(); |
| this.assertRepositoryCondition(1); |
| } |
| |
| @Test |
| public void testCreateOrphanBranchWithStartPoint() throws Exception { |
| CheckoutCommand co = newOrphanBranchCommand(); |
| Ref ref = co.setStartPoint("HEAD^").call(); |
| assertCheckoutRef(ref); |
| |
| assertEquals(2, trash.list().length); |
| this.assertNoHead(); |
| this.assertRepositoryCondition(1); |
| } |
| |
| @Test |
| public void testInvalidRefName() throws Exception { |
| try { |
| git.checkout().setOrphan(true).setName("../invalidname").call(); |
| fail("Should have failed"); |
| } catch (InvalidRefNameException e) { |
| // except to hit here |
| } |
| } |
| |
| @Test |
| public void testNullRefName() throws Exception { |
| try { |
| git.checkout().setOrphan(true).setName(null).call(); |
| fail("Should have failed"); |
| } catch (InvalidRefNameException e) { |
| // except to hit here |
| } |
| } |
| |
| @Test |
| public void testAlreadyExists() throws Exception { |
| this.git.checkout().setCreateBranch(true).setName("orphanbranch") |
| .call(); |
| this.git.checkout().setName("master").call(); |
| |
| try { |
| newOrphanBranchCommand().call(); |
| fail("Should have failed"); |
| } catch (RefAlreadyExistsException e) { |
| // except to hit here |
| } |
| } |
| |
| // TODO: write a faster test which depends less on characteristics of |
| // underlying filesystem/OS. |
| @Test |
| public void testCheckoutAutoCrlfTrue() throws Exception { |
| int nrOfAutoCrlfTestFiles = 200; |
| |
| FileBasedConfig c = db.getConfig(); |
| c.setString("core", null, "autocrlf", "true"); |
| c.save(); |
| |
| AddCommand add = git.add(); |
| for (int i = 100; i < 100 + nrOfAutoCrlfTestFiles; i++) { |
| writeTrashFile("Test_" + i + ".txt", "Hello " + i |
| + " world\nX\nYU\nJK\n"); |
| add.addFilepattern("Test_" + i + ".txt"); |
| } |
| fsTick(null); |
| add.call(); |
| RevCommit c1 = git.commit().setMessage("add some lines").call(); |
| |
| add = git.add(); |
| for (int i = 100; i < 100 + nrOfAutoCrlfTestFiles; i++) { |
| writeTrashFile("Test_" + i + ".txt", "Hello " + i |
| + " world\nX\nY\n"); |
| add.addFilepattern("Test_" + i + ".txt"); |
| } |
| fsTick(null); |
| add.call(); |
| git.commit().setMessage("add more").call(); |
| |
| git.checkout().setName(c1.getName()).call(); |
| |
| boolean foundUnsmudged = false; |
| DirCache dc = db.readDirCache(); |
| for (int i = 100; i < 100 + nrOfAutoCrlfTestFiles; i++) { |
| DirCacheEntry entry = dc.getEntry( |
| "Test_" + i + ".txt"); |
| if (!entry.isSmudged()) { |
| foundUnsmudged = true; |
| assertEquals("unexpected file length in git index", 28, |
| entry.getLength()); |
| } |
| } |
| org.junit.Assume.assumeTrue(foundUnsmudged); |
| } |
| |
| @Test |
| public void testSmudgeFilter_modifyExisting() throws IOException, GitAPIException { |
| File script = writeTempFile("sed s/o/e/g"); |
| StoredConfig config = git.getRepository().getConfig(); |
| config.setString("filter", "lfs", "smudge", |
| "sh " + slashify(script.getPath())); |
| config.save(); |
| |
| writeTrashFile(".gitattributes", "*.txt filter=lfs"); |
| git.add().addFilepattern(".gitattributes").call(); |
| git.commit().setMessage("add filter").call(); |
| |
| writeTrashFile("src/a.tmp", "x"); |
| // Caution: we need a trailing '\n' since sed on mac always appends |
| // linefeeds if missing |
| writeTrashFile("src/a.txt", "x\n"); |
| git.add().addFilepattern("src/a.tmp").addFilepattern("src/a.txt") |
| .call(); |
| RevCommit content1 = git.commit().setMessage("add content").call(); |
| |
| writeTrashFile("src/a.tmp", "foo"); |
| writeTrashFile("src/a.txt", "foo\n"); |
| git.add().addFilepattern("src/a.tmp").addFilepattern("src/a.txt") |
| .call(); |
| RevCommit content2 = git.commit().setMessage("changed content").call(); |
| |
| git.checkout().setName(content1.getName()).call(); |
| git.checkout().setName(content2.getName()).call(); |
| |
| assertEquals( |
| "[.gitattributes, mode:100644, content:*.txt filter=lfs][Test.txt, mode:100644, content:Some change][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:foo\n]", |
| indexState(CONTENT)); |
| assertEquals(Sets.of("src/a.txt"), git.status().call().getModified()); |
| assertEquals("foo", read("src/a.tmp")); |
| assertEquals("fee\n", read("src/a.txt")); |
| } |
| |
| @Test |
| public void testSmudgeFilter_createNew() |
| throws IOException, GitAPIException { |
| File script = writeTempFile("sed s/o/e/g"); |
| StoredConfig config = git.getRepository().getConfig(); |
| config.setString("filter", "lfs", "smudge", |
| "sh " + slashify(script.getPath())); |
| config.save(); |
| |
| writeTrashFile("foo", "foo"); |
| git.add().addFilepattern("foo").call(); |
| RevCommit initial = git.commit().setMessage("initial").call(); |
| |
| writeTrashFile(".gitattributes", "*.txt filter=lfs"); |
| git.add().addFilepattern(".gitattributes").call(); |
| git.commit().setMessage("add filter").call(); |
| |
| writeTrashFile("src/a.tmp", "foo"); |
| // Caution: we need a trailing '\n' since sed on mac always appends |
| // linefeeds if missing |
| writeTrashFile("src/a.txt", "foo\n"); |
| git.add().addFilepattern("src/a.tmp").addFilepattern("src/a.txt") |
| .call(); |
| RevCommit content = git.commit().setMessage("added content").call(); |
| |
| git.checkout().setName(initial.getName()).call(); |
| git.checkout().setName(content.getName()).call(); |
| |
| assertEquals( |
| "[.gitattributes, mode:100644, content:*.txt filter=lfs][Test.txt, mode:100644, content:Some change][foo, mode:100644, content:foo][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:foo\n]", |
| indexState(CONTENT)); |
| assertEquals("foo", read("src/a.tmp")); |
| assertEquals("fee\n", read("src/a.txt")); |
| } |
| |
| @Test |
| public void testSmudgeFilter_deleteFileAndRestoreFromCommit() |
| throws IOException, GitAPIException { |
| File script = writeTempFile("sed s/o/e/g"); |
| StoredConfig config = git.getRepository().getConfig(); |
| config.setString("filter", "lfs", "smudge", |
| "sh " + slashify(script.getPath())); |
| config.save(); |
| |
| writeTrashFile("foo", "foo"); |
| git.add().addFilepattern("foo").call(); |
| git.commit().setMessage("initial").call(); |
| |
| writeTrashFile(".gitattributes", "*.txt filter=lfs"); |
| git.add().addFilepattern(".gitattributes").call(); |
| git.commit().setMessage("add filter").call(); |
| |
| writeTrashFile("src/a.tmp", "foo"); |
| // Caution: we need a trailing '\n' since sed on mac always appends |
| // linefeeds if missing |
| writeTrashFile("src/a.txt", "foo\n"); |
| git.add().addFilepattern("src/a.tmp").addFilepattern("src/a.txt") |
| .call(); |
| RevCommit content = git.commit().setMessage("added content").call(); |
| |
| deleteTrashFile("src/a.txt"); |
| git.checkout().setStartPoint(content.getName()).addPath("src/a.txt") |
| .call(); |
| |
| assertEquals( |
| "[.gitattributes, mode:100644, content:*.txt filter=lfs][Test.txt, mode:100644, content:Some change][foo, mode:100644, content:foo][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:foo\n]", |
| indexState(CONTENT)); |
| assertEquals("foo", read("src/a.tmp")); |
| assertEquals("fee\n", read("src/a.txt")); |
| } |
| |
| @Test |
| public void testSmudgeFilter_deleteFileAndRestoreFromIndex() |
| throws IOException, GitAPIException { |
| File script = writeTempFile("sed s/o/e/g"); |
| StoredConfig config = git.getRepository().getConfig(); |
| config.setString("filter", "lfs", "smudge", |
| "sh " + slashify(script.getPath())); |
| config.save(); |
| |
| writeTrashFile("foo", "foo"); |
| git.add().addFilepattern("foo").call(); |
| git.commit().setMessage("initial").call(); |
| |
| writeTrashFile(".gitattributes", "*.txt filter=lfs"); |
| git.add().addFilepattern(".gitattributes").call(); |
| git.commit().setMessage("add filter").call(); |
| |
| writeTrashFile("src/a.tmp", "foo"); |
| // Caution: we need a trailing '\n' since sed on mac always appends |
| // linefeeds if missing |
| writeTrashFile("src/a.txt", "foo\n"); |
| git.add().addFilepattern("src/a.tmp").addFilepattern("src/a.txt") |
| .call(); |
| git.commit().setMessage("added content").call(); |
| |
| deleteTrashFile("src/a.txt"); |
| git.checkout().addPath("src/a.txt").call(); |
| |
| assertEquals( |
| "[.gitattributes, mode:100644, content:*.txt filter=lfs][Test.txt, mode:100644, content:Some change][foo, mode:100644, content:foo][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:foo\n]", |
| indexState(CONTENT)); |
| assertEquals("foo", read("src/a.tmp")); |
| assertEquals("fee\n", read("src/a.txt")); |
| } |
| |
| @Test |
| public void testSmudgeFilter_deleteFileAndCreateBranchAndRestoreFromCommit() |
| throws IOException, GitAPIException { |
| File script = writeTempFile("sed s/o/e/g"); |
| StoredConfig config = git.getRepository().getConfig(); |
| config.setString("filter", "lfs", "smudge", |
| "sh " + slashify(script.getPath())); |
| config.save(); |
| |
| writeTrashFile("foo", "foo"); |
| git.add().addFilepattern("foo").call(); |
| git.commit().setMessage("initial").call(); |
| |
| writeTrashFile(".gitattributes", "*.txt filter=lfs"); |
| git.add().addFilepattern(".gitattributes").call(); |
| git.commit().setMessage("add filter").call(); |
| |
| writeTrashFile("src/a.tmp", "foo"); |
| // Caution: we need a trailing '\n' since sed on mac always appends |
| // linefeeds if missing |
| writeTrashFile("src/a.txt", "foo\n"); |
| git.add().addFilepattern("src/a.tmp").addFilepattern("src/a.txt") |
| .call(); |
| RevCommit content = git.commit().setMessage("added content").call(); |
| |
| deleteTrashFile("src/a.txt"); |
| git.checkout().setName("newBranch").setCreateBranch(true) |
| .setStartPoint(content).addPath("src/a.txt").call(); |
| |
| assertEquals( |
| "[.gitattributes, mode:100644, content:*.txt filter=lfs][Test.txt, mode:100644, content:Some change][foo, mode:100644, content:foo][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:foo\n]", |
| indexState(CONTENT)); |
| assertEquals("foo", read("src/a.tmp")); |
| assertEquals("fee\n", read("src/a.txt")); |
| } |
| |
| @Test |
| public void testSmudgeAndClean() throws Exception { |
| File clean_filter = writeTempFile("sed s/V1/@version/g"); |
| File smudge_filter = writeTempFile("sed s/@version/V1/g"); |
| |
| try (Git git2 = new Git(db)) { |
| StoredConfig config = git.getRepository().getConfig(); |
| config.setString("filter", "lfs", "smudge", |
| "sh " + slashify(smudge_filter.getPath())); |
| config.setString("filter", "lfs", "clean", |
| "sh " + slashify(clean_filter.getPath())); |
| config.setBoolean("filter", "lfs", "useJGitBuiltin", true); |
| config.save(); |
| writeTrashFile(".gitattributes", "filterTest.txt filter=lfs"); |
| git2.add().addFilepattern(".gitattributes").call(); |
| git2.commit().setMessage("add attributes").call(); |
| |
| fsTick(writeTrashFile("filterTest.txt", "hello world, V1\n")); |
| git2.add().addFilepattern("filterTest.txt").call(); |
| RevCommit one = git2.commit().setMessage("add filterText.txt").call(); |
| assertEquals( |
| "[.gitattributes, mode:100644, content:filterTest.txt filter=lfs][Test.txt, mode:100644, content:Some change][filterTest.txt, mode:100644, content:version https://git-lfs.github.com/spec/v1\noid sha256:7bd5d32e5c494354aa4c2473a1306d0ce7b52cc3bffeb342c03cd517ef8cf8da\nsize 16\n]", |
| indexState(CONTENT)); |
| |
| fsTick(writeTrashFile("filterTest.txt", "bon giorno world, V1\n")); |
| git2.add().addFilepattern("filterTest.txt").call(); |
| RevCommit two = git2.commit().setMessage("modified filterTest.txt").call(); |
| |
| assertTrue(git2.status().call().isClean()); |
| assertEquals( |
| "[.gitattributes, mode:100644, content:filterTest.txt filter=lfs][Test.txt, mode:100644, content:Some change][filterTest.txt, mode:100644, content:version https://git-lfs.github.com/spec/v1\noid sha256:087148cccf53b0049c56475c1595113c9da4b638997c3489af8ac7108d51ef13\nsize 21\n]", |
| indexState(CONTENT)); |
| |
| git2.checkout().setName(one.getName()).call(); |
| assertTrue(git2.status().call().isClean()); |
| assertEquals( |
| "[.gitattributes, mode:100644, content:filterTest.txt filter=lfs][Test.txt, mode:100644, content:Some change][filterTest.txt, mode:100644, content:version https://git-lfs.github.com/spec/v1\noid sha256:7bd5d32e5c494354aa4c2473a1306d0ce7b52cc3bffeb342c03cd517ef8cf8da\nsize 16\n]", |
| indexState(CONTENT)); |
| assertEquals("hello world, V1\n", read("filterTest.txt")); |
| |
| git2.checkout().setName(two.getName()).call(); |
| assertTrue(git2.status().call().isClean()); |
| assertEquals( |
| "[.gitattributes, mode:100644, content:filterTest.txt filter=lfs][Test.txt, mode:100644, content:Some change][filterTest.txt, mode:100644, content:version https://git-lfs.github.com/spec/v1\noid sha256:087148cccf53b0049c56475c1595113c9da4b638997c3489af8ac7108d51ef13\nsize 21\n]", |
| indexState(CONTENT)); |
| assertEquals("bon giorno world, V1\n", read("filterTest.txt")); |
| } |
| } |
| |
| @Test |
| public void testNonDeletableFilesOnWindows() |
| throws GitAPIException, IOException { |
| // Only on windows a FileInputStream blocks us from deleting a file |
| org.junit.Assume.assumeTrue(SystemReader.getInstance().isWindows()); |
| writeTrashFile("toBeModified.txt", "a"); |
| writeTrashFile("toBeDeleted.txt", "a"); |
| git.add().addFilepattern(".").call(); |
| RevCommit addFiles = git.commit().setMessage("add more files").call(); |
| |
| git.rm().setCached(false).addFilepattern("Test.txt") |
| .addFilepattern("toBeDeleted.txt").call(); |
| writeTrashFile("toBeModified.txt", "b"); |
| writeTrashFile("toBeCreated.txt", "a"); |
| git.add().addFilepattern(".").call(); |
| RevCommit crudCommit = git.commit().setMessage("delete, modify, add") |
| .call(); |
| git.checkout().setName(addFiles.getName()).call(); |
| try ( FileInputStream fis=new FileInputStream(new File(db.getWorkTree(), "Test.txt")) ) { |
| CheckoutCommand coCommand = git.checkout(); |
| coCommand.setName(crudCommit.getName()).call(); |
| CheckoutResult result = coCommand.getResult(); |
| assertEquals(Status.NONDELETED, result.getStatus()); |
| assertEquals("[toBeDeleted.txt]", |
| result.getRemovedList().toString()); |
| assertEquals("[toBeCreated.txt, toBeModified.txt]", |
| result.getModifiedList().toString()); |
| assertEquals("[Test.txt]", result.getUndeletedList().toString()); |
| assertTrue(result.getConflictList().isEmpty()); |
| } |
| } |
| |
| private File writeTempFile(String body) throws IOException { |
| File f = File.createTempFile("CheckoutCommandTest_", ""); |
| JGitTestUtil.write(f, body); |
| return f; |
| } |
| } |