aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF2
-rw-r--r--org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java360
-rw-r--r--org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ConfigTest.java30
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java211
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java25
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java166
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java2
8 files changed, 729 insertions, 69 deletions
diff --git a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
index 5869d76153..b467d2dab6 100644
--- a/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.pgm.test/META-INF/MANIFEST.MF
@@ -8,6 +8,8 @@ Bundle-Localization: plugin
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Import-Package: org.eclipse.jgit.api;version="[3.4.0,3.5.0)",
+ org.eclipse.jgit.api.errors;version="[3.4.0,3.5.0)",
+ org.eclipse.jgit.diff;version="[3.4.0,3.5.0)",
org.eclipse.jgit.dircache;version="[3.4.0,3.5.0)",
org.eclipse.jgit.junit;version="[3.4.0,3.5.0)",
org.eclipse.jgit.lib;version="[3.4.0,3.5.0)",
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java
index 2c1f59f250..90284736cf 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CheckoutTest.java
@@ -43,15 +43,21 @@
package org.eclipse.jgit.pgm;
import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNotNull;
import java.io.File;
+import java.util.List;
import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.CheckoutConflictException;
+import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.lib.CLIRepositoryTestCase;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.FileTreeIterator.FileEntry;
+import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.FileUtils;
import org.junit.Assert;
import org.junit.Test;
@@ -150,6 +156,7 @@ public class CheckoutTest extends CLIRepositoryTestCase {
* <li>Delete file 'a' in the working tree
* <li>Checkout branch '1'
* </ol>
+ * <p>
* The working tree should contain 'a' with FileMode.REGULAR_FILE after the
* checkout.
*
@@ -181,6 +188,359 @@ public class CheckoutTest extends CLIRepositoryTestCase {
assertEquals("Hello world a", read(fileA));
}
+ /**
+ * Steps:
+ * <ol>
+ * <li>Add file 'b'
+ * <li>Commit
+ * <li>Create branch '1'
+ * <li>Add folder 'a'
+ * <li>Commit
+ * <li>Replace folder 'a' by file 'a' in the working tree
+ * <li>Checkout branch '1'
+ * </ol>
+ * <p>
+ * The working tree should contain 'a' with FileMode.REGULAR_FILE after the
+ * checkout.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void fileModeTestMissingThenFolderWithFileInWorkingTree()
+ throws Exception {
+ Git git = new Git(db);
+ writeTrashFile("b", "Hello world b");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("add file b").call();
+ Ref branch_1 = git.branchCreate().setName("branch_1").call();
+ File folderA = new File(db.getWorkTree(), "a");
+ FileUtils.mkdirs(folderA);
+ writeTrashFile("a/c", "Hello world c");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("add folder a").call();
+
+ FileEntry entry = new FileTreeIterator.FileEntry(new File(
+ db.getWorkTree(), "a"), db.getFS());
+ assertEquals(FileMode.TREE, entry.getMode());
+
+ FileUtils.delete(folderA, FileUtils.RECURSIVE);
+ writeTrashFile("a", "b");
+
+ entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
+ db.getFS());
+ assertEquals(FileMode.REGULAR_FILE, entry.getMode());
+
+ git.checkout().setName(branch_1.getName()).call();
+
+ entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
+ db.getFS());
+ assertEquals(FileMode.REGULAR_FILE, entry.getMode());
+ }
+
+ /**
+ * Steps:
+ * <ol>
+ * <li>Add file 'a'
+ * <li>Commit
+ * <li>Create branch '1'
+ * <li>Replace file 'a' by folder 'a'
+ * <li>Commit
+ * <li>Delete folder 'a' in the working tree
+ * <li>Checkout branch '1'
+ * </ol>
+ * <p>
+ * The working tree should contain 'a' with FileMode.REGULAR_FILE after the
+ * checkout.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void fileModeTestFolderWithMissingInWorkingTree() throws Exception {
+ Git git = new Git(db);
+ writeTrashFile("b", "Hello world b");
+ writeTrashFile("a", "b");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("add file b & file a").call();
+ Ref branch_1 = git.branchCreate().setName("branch_1").call();
+ git.rm().addFilepattern("a").call();
+ File folderA = new File(db.getWorkTree(), "a");
+ FileUtils.mkdirs(folderA);
+ writeTrashFile("a/c", "Hello world c");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("add folder a").call();
+
+ FileEntry entry = new FileTreeIterator.FileEntry(new File(
+ db.getWorkTree(), "a"), db.getFS());
+ assertEquals(FileMode.TREE, entry.getMode());
+
+ FileUtils.delete(folderA, FileUtils.RECURSIVE);
+
+ git.checkout().setName(branch_1.getName()).call();
+
+ entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
+ db.getFS());
+ assertEquals(FileMode.REGULAR_FILE, entry.getMode());
+ }
+
+ /**
+ * Steps:
+ * <ol>
+ * <li>Add file 'a'
+ * <li>Commit
+ * <li>Create branch '1'
+ * <li>Delete file 'a'
+ * <li>Commit
+ * <li>Add folder 'a' in the working tree
+ * <li>Checkout branch '1'
+ * </ol>
+ * <p>
+ * The checkout command should raise an error. The conflicting paths are 'a'
+ * and 'a/c'.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void fileModeTestMissingWithFolderInWorkingTree() throws Exception {
+ Git git = new Git(db);
+ writeTrashFile("b", "Hello world b");
+ writeTrashFile("a", "b");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("add file b & file a").call();
+ Ref branch_1 = git.branchCreate().setName("branch_1").call();
+ git.rm().addFilepattern("a").call();
+ git.commit().setMessage("delete file a").call();
+
+ FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
+ writeTrashFile("a/c", "Hello world c");
+
+ FileEntry entry = new FileTreeIterator.FileEntry(new File(
+ db.getWorkTree(), "a"), db.getFS());
+ assertEquals(FileMode.TREE, entry.getMode());
+
+ CheckoutConflictException exception = null;
+ try {
+ git.checkout().setName(branch_1.getName()).call();
+ } catch (CheckoutConflictException e) {
+ exception = e;
+ }
+ assertNotNull(exception);
+ assertEquals(2, exception.getConflictingPaths().size());
+ assertEquals("a", exception.getConflictingPaths().get(0));
+ assertEquals("a/c", exception.getConflictingPaths().get(1));
+ }
+
+ /**
+ * Steps:
+ * <ol>
+ * <li>Add folder 'a'
+ * <li>Commit
+ * <li>Create branch '1'
+ * <li>Delete folder 'a'
+ * <li>Commit
+ * <li>Add file 'a' in the working tree
+ * <li>Checkout branch '1'
+ * </ol>
+ * <p>
+ * The checkout command should raise an error. The conflicting path is 'a'.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void fileModeTestFolderThenMissingWithFileInWorkingTree()
+ throws Exception {
+ Git git = new Git(db);
+ FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
+ writeTrashFile("a/c", "Hello world c");
+ writeTrashFile("b", "Hello world b");
+ git.add().addFilepattern(".").call();
+ RevCommit commit1 = git.commit().setMessage("add folder a & file b")
+ .call();
+ Ref branch_1 = git.branchCreate().setName("branch_1").call();
+ git.rm().addFilepattern("a").call();
+ RevCommit commit2 = git.commit().setMessage("delete folder a").call();
+
+ TreeWalk tw = new TreeWalk(db);
+ tw.addTree(commit1.getTree());
+ tw.addTree(commit2.getTree());
+ List<DiffEntry> scan = DiffEntry.scan(tw);
+ assertEquals(1, scan.size());
+ assertEquals(FileMode.MISSING, scan.get(0).getNewMode());
+ assertEquals(FileMode.TREE, scan.get(0).getOldMode());
+
+ writeTrashFile("a", "b");
+
+ FileEntry entry = new FileTreeIterator.FileEntry(new File(
+ db.getWorkTree(), "a"), db.getFS());
+ assertEquals(FileMode.REGULAR_FILE, entry.getMode());
+
+ CheckoutConflictException exception = null;
+ try {
+ git.checkout().setName(branch_1.getName()).call();
+ } catch (CheckoutConflictException e) {
+ exception = e;
+ }
+ assertNotNull(exception);
+ assertEquals(1, exception.getConflictingPaths().size());
+ assertEquals("a", exception.getConflictingPaths().get(0));
+ }
+
+ /**
+ * Steps:
+ * <ol>
+ * <li>Add folder 'a'
+ * <li>Commit
+ * <li>Create branch '1'
+ * <li>Replace folder 'a'by file 'a'
+ * <li>Commit
+ * <li>Delete file 'a' in the working tree
+ * <li>Checkout branch '1'
+ * </ol>
+ * <p>
+ * The working tree should contain 'a' with FileMode.TREE after the
+ * checkout.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void fileModeTestFolderThenFileWithMissingInWorkingTree()
+ throws Exception {
+ Git git = new Git(db);
+ FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
+ writeTrashFile("a/c", "Hello world c");
+ writeTrashFile("b", "Hello world b");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("add folder a & file b").call();
+ Ref branch_1 = git.branchCreate().setName("branch_1").call();
+ git.rm().addFilepattern("a").call();
+ File fileA = new File(db.getWorkTree(), "a");
+ writeTrashFile("a", "b");
+ git.add().addFilepattern("a").call();
+ git.commit().setMessage("add file a").call();
+
+ FileEntry entry = new FileTreeIterator.FileEntry(new File(
+ db.getWorkTree(), "a"), db.getFS());
+ assertEquals(FileMode.REGULAR_FILE, entry.getMode());
+
+ FileUtils.delete(fileA);
+
+ git.checkout().setName(branch_1.getName()).call();
+
+ entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
+ db.getFS());
+ assertEquals(FileMode.TREE, entry.getMode());
+ }
+
+ /**
+ * Steps:
+ * <ol>
+ * <li>Add file 'a'
+ * <li>Commit
+ * <li>Create branch '1'
+ * <li>Modify file 'a'
+ * <li>Commit
+ * <li>Delete file 'a' & replace by folder 'a' in the working tree & index
+ * <li>Checkout branch '1'
+ * </ol>
+ * <p>
+ * The checkout command should raise an error. The conflicting path is 'a'.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void fileModeTestFileThenFileWithFolderInIndex() throws Exception {
+ Git git = new Git(db);
+ writeTrashFile("a", "Hello world a");
+ writeTrashFile("b", "Hello world b");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("add files a & b").call();
+ Ref branch_1 = git.branchCreate().setName("branch_1").call();
+ writeTrashFile("a", "b");
+ git.add().addFilepattern("a").call();
+ git.commit().setMessage("add file a").call();
+
+ FileEntry entry = new FileTreeIterator.FileEntry(new File(
+ db.getWorkTree(), "a"), db.getFS());
+ assertEquals(FileMode.REGULAR_FILE, entry.getMode());
+
+ git.rm().addFilepattern("a").call();
+ FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
+ writeTrashFile("a/c", "Hello world c");
+ git.add().addFilepattern(".").call();
+
+ entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
+ db.getFS());
+ assertEquals(FileMode.TREE, entry.getMode());
+
+ CheckoutConflictException exception = null;
+ try {
+ git.checkout().setName(branch_1.getName()).call();
+ } catch (CheckoutConflictException e) {
+ exception = e;
+ }
+ assertNotNull(exception);
+ assertEquals(1, exception.getConflictingPaths().size());
+ assertEquals("a", exception.getConflictingPaths().get(0));
+ }
+
+ /**
+ * Steps:
+ * <ol>
+ * <li>Add file 'a'
+ * <li>Commit
+ * <li>Create branch '1'
+ * <li>Modify file 'a'
+ * <li>Commit
+ * <li>Delete file 'a' & replace by folder 'a' in the working tree & index
+ * <li>Checkout branch '1'
+ * </ol>
+ * <p>
+ * The checkout command should raise an error. The conflicting paths are 'a'
+ * and 'a/c'.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void fileModeTestFileWithFolderInIndex() throws Exception {
+ Git git = new Git(db);
+ writeTrashFile("b", "Hello world b");
+ writeTrashFile("a", "b");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("add file b & file a").call();
+ Ref branch_1 = git.branchCreate().setName("branch_1").call();
+ git.rm().addFilepattern("a").call();
+ writeTrashFile("a", "Hello world a");
+ git.add().addFilepattern("a").call();
+ git.commit().setMessage("add file a").call();
+
+ FileEntry entry = new FileTreeIterator.FileEntry(new File(
+ db.getWorkTree(), "a"), db.getFS());
+ assertEquals(FileMode.REGULAR_FILE, entry.getMode());
+
+ git.rm().addFilepattern("a").call();
+ FileUtils.mkdirs(new File(db.getWorkTree(), "a"));
+ writeTrashFile("a/c", "Hello world c");
+ git.add().addFilepattern(".").call();
+
+ entry = new FileTreeIterator.FileEntry(new File(db.getWorkTree(), "a"),
+ db.getFS());
+ assertEquals(FileMode.TREE, entry.getMode());
+
+ CheckoutConflictException exception = null;
+ try {
+ git.checkout().setName(branch_1.getName()).call();
+ } catch (CheckoutConflictException e) {
+ exception = e;
+ }
+ assertNotNull(exception);
+ assertEquals(1, exception.getConflictingPaths().size());
+ assertEquals("a", exception.getConflictingPaths().get(0));
+
+ // TODO: ideally we'd like to get two paths from this exception
+ // assertEquals(2, exception.getConflictingPaths().size());
+ // assertEquals("a", exception.getConflictingPaths().get(0));
+ // assertEquals("a/c", exception.getConflictingPaths().get(1));
+ }
+
static private void assertEquals(Object expected, Object actual) {
Assert.assertEquals(expected, actual);
}
diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ConfigTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ConfigTest.java
index 3c62e85502..aefdff185c 100644
--- a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ConfigTest.java
+++ b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/ConfigTest.java
@@ -42,15 +42,15 @@
*/
package org.eclipse.jgit.pgm;
-import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.CLIRepositoryTestCase;
+import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.SystemReader;
import org.junit.Before;
import org.junit.Test;
@@ -77,28 +77,12 @@ public class ConfigTest extends CLIRepositoryTestCase {
if (isMac)
expect.add("core.precomposeunicode=true");
expect.add("core.repositoryformatversion=0");
- if (SystemReader.getInstance().isWindows() && osVersion() < 6
- || javaVersion() < 1.7) {
+ if (!FS.DETECTED.supportsSymlinks())
expect.add("core.symlinks=false");
- }
expect.add(""); // ends with LF (last line empty)
- assertArrayEquals("expected default configuration", expect.toArray(),
- output);
+ assertEquals("expected default configuration",
+ Arrays.asList(expect.toArray()).toString(),
+ Arrays.asList(output).toString());
}
- private static float javaVersion() {
- String versionString = System.getProperty("java.version");
- Matcher matcher = Pattern.compile("(\\d+\\.\\d+).*").matcher(
- versionString);
- matcher.matches();
- return Float.parseFloat(matcher.group(1));
- }
-
- private static float osVersion() {
- String versionString = System.getProperty("os.version");
- Matcher matcher = Pattern.compile("(\\d+\\.\\d+).*").matcher(
- versionString);
- matcher.matches();
- return Float.parseFloat(matcher.group(1));
- }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java
new file mode 100644
index 0000000000..d85fb54720
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ArchiveCommandTest.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2014, Shaul Zorea <shaulzorea@gmail.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.*;
+
+public class ArchiveCommandTest extends RepositoryTestCase {
+
+ private static final String UNEXPECTED_ARCHIVE_SIZE = "Unexpected archive size";
+ private static final String UNEXPECTED_FILE_CONTENTS = "Unexpected file contents";
+ private static final String UNEXPECTED_TREE_CONTENTS = "Unexpected tree contents";
+
+ private MockFormat format = null;
+
+ @Before
+ public void setup() {
+ format = new MockFormat();
+ ArchiveCommand.registerFormat(format.SUFFIXES.get(0), format);
+ }
+
+ @After
+ public void tearDown() {
+ ArchiveCommand.unregisterFormat(format.SUFFIXES.get(0));
+ }
+
+ @Test
+ public void archiveHeadAllFiles() throws IOException, GitAPIException {
+ Git git = new Git(db);
+ writeTrashFile("file_1.txt", "content_1_1");
+ git.add().addFilepattern("file_1.txt").call();
+ git.commit().setMessage("create file").call();
+
+ writeTrashFile("file_1.txt", "content_1_2");
+ writeTrashFile("file_2.txt", "content_2_2");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("updated file").call();
+
+ git.archive().setOutputStream(new MockOutputStream())
+ .setFormat(format.SUFFIXES.get(0))
+ .setTree(git.getRepository().resolve("HEAD")).call();
+
+ assertEquals(UNEXPECTED_ARCHIVE_SIZE, 2, format.size());
+ assertEquals(UNEXPECTED_FILE_CONTENTS, "content_1_2", format.getByPath("file_1.txt"));
+ assertEquals(UNEXPECTED_FILE_CONTENTS, "content_2_2", format.getByPath("file_2.txt"));
+ }
+
+ @Test
+ public void archiveHeadSpecificPath() throws IOException, GitAPIException {
+ Git git = new Git(db);
+ writeTrashFile("file_1.txt", "content_1_1");
+ git.add().addFilepattern("file_1.txt").call();
+ git.commit().setMessage("create file").call();
+
+ writeTrashFile("file_1.txt", "content_1_2");
+ String expectedFilePath = "some_directory/file_2.txt";
+ writeTrashFile(expectedFilePath, "content_2_2");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("updated file").call();
+
+ git.archive().setOutputStream(new MockOutputStream())
+ .setFormat(format.SUFFIXES.get(0))
+ .setTree(git.getRepository().resolve("HEAD"))
+ .setPaths(expectedFilePath).call();
+
+ assertEquals(UNEXPECTED_ARCHIVE_SIZE, 2, format.size());
+ assertEquals(UNEXPECTED_FILE_CONTENTS, "content_2_2", format.getByPath(expectedFilePath));
+ assertNull(UNEXPECTED_TREE_CONTENTS, format.getByPath("some_directory"));
+ }
+
+ @Test
+ public void archiveByIdSpecificFile() throws IOException, GitAPIException {
+ Git git = new Git(db);
+ writeTrashFile("file_1.txt", "content_1_1");
+ git.add().addFilepattern("file_1.txt").call();
+ RevCommit first = git.commit().setMessage("create file").call();
+
+ writeTrashFile("file_1.txt", "content_1_2");
+ String expectedFilePath = "some_directory/file_2.txt";
+ writeTrashFile(expectedFilePath, "content_2_2");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("updated file").call();
+
+ git.archive().setOutputStream(new MockOutputStream())
+ .setFormat(format.SUFFIXES.get(0)).setTree(first)
+ .setPaths("file_1.txt").call();
+
+ assertEquals(UNEXPECTED_ARCHIVE_SIZE, 1, format.size());
+ assertEquals(UNEXPECTED_FILE_CONTENTS, "content_1_1", format.getByPath("file_1.txt"));
+ }
+
+ @Test
+ public void archiveByDirectoryPath() throws GitAPIException, IOException {
+ Git git = new Git(db);
+ writeTrashFile("file_0.txt", "content_0_1");
+ git.add().addFilepattern("file_0.txt").call();
+ git.commit().setMessage("commit_1").call();
+
+ writeTrashFile("file_0.txt", "content_0_2");
+ String expectedFilePath1 = "some_directory/file_1.txt";
+ writeTrashFile(expectedFilePath1, "content_1_2");
+ String expectedFilePath2 = "some_directory/file_2.txt";
+ writeTrashFile(expectedFilePath2, "content_2_2");
+ String expectedFilePath3 = "some_directory/nested_directory/file_3.txt";
+ writeTrashFile(expectedFilePath3, "content_3_2");
+ git.add().addFilepattern(".").call();
+ git.commit().setMessage("commit_2").call();
+ git.archive().setOutputStream(new MockOutputStream())
+ .setFormat(format.SUFFIXES.get(0))
+ .setTree(git.getRepository().resolve("HEAD"))
+ .setPaths("some_directory/").call();
+
+ assertEquals(UNEXPECTED_ARCHIVE_SIZE, 5, format.size());
+ assertEquals(UNEXPECTED_FILE_CONTENTS, "content_1_2", format.getByPath(expectedFilePath1));
+ assertEquals(UNEXPECTED_FILE_CONTENTS, "content_2_2", format.getByPath(expectedFilePath2));
+ assertEquals(UNEXPECTED_FILE_CONTENTS, "content_3_2", format.getByPath(expectedFilePath3));
+ assertNull(UNEXPECTED_TREE_CONTENTS, format.getByPath("some_directory"));
+ assertNull(UNEXPECTED_TREE_CONTENTS, format.getByPath("some_directory/nested_directory"));
+ }
+
+ private class MockFormat implements ArchiveCommand.Format<MockOutputStream> {
+
+ private Map<String, String> entries = new HashMap<String, String>();
+
+ private int size() {
+ return entries.size();
+ }
+
+ private String getByPath(String path) {
+ return entries.get(path);
+ }
+
+ private final List<String> SUFFIXES = Collections
+ .unmodifiableList(Arrays.asList(".mck"));
+
+ public MockOutputStream createArchiveOutputStream(OutputStream s)
+ throws IOException {
+ return new MockOutputStream();
+ }
+
+ public void putEntry(MockOutputStream out, String path, FileMode mode, ObjectLoader loader) {
+ String content = mode != FileMode.TREE ? new String(loader.getBytes()) : null;
+ entries.put(path, content);
+ }
+
+ public Iterable<String> suffixes() {
+ return SUFFIXES;
+ }
+ }
+
+ private class MockOutputStream extends OutputStream {
+
+ @Override
+ public void write(int b) throws IOException {
+ // Do nothing. for testing purposes.
+ }
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
index 98ec706bec..afbad6ab2c 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
@@ -619,7 +619,7 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
@Test
public void testDirectoryFileConflicts_9() throws Exception {
// 9
- doit(mk("DF"), mkmap("DF", "QP"), mk("DF/DF"));
+ doit(mkmap("DF", "QP"), mkmap("DF", "QP"), mkmap("DF/DF", "DF/DF"));
assertRemoved("DF/DF");
assertUpdated("DF");
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java
index 70ab73015f..88bc2aee7d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ArchiveCommand.java
@@ -46,6 +46,9 @@ import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;
import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -60,6 +63,7 @@ import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
/**
* Create an archive of files from a named tree.
@@ -324,6 +328,7 @@ public class ArchiveCommand extends GitCommand<OutputStream> {
private ObjectId tree;
private String prefix;
private String format;
+ private List<String> paths = new ArrayList<String>();
/** Filename suffix, for automatically choosing a format. */
private String suffix;
@@ -347,6 +352,9 @@ public class ArchiveCommand extends GitCommand<OutputStream> {
final RevWalk rw = new RevWalk(walk.getObjectReader());
walk.reset(rw.parseTree(tree));
+ if (!paths.isEmpty())
+ walk.setFilter(PathFilterGroup.createFromStrings(paths));
+
while (walk.next()) {
final String name = pfx + walk.getPathString();
FileMode mode = walk.getFileMode(0);
@@ -462,4 +470,21 @@ public class ArchiveCommand extends GitCommand<OutputStream> {
this.format = fmt;
return this;
}
+
+ /**
+ * Set an optional parameter path. without an optional path parameter, all
+ * files and subdirectories of the current working directory are included in
+ * the archive. If one or more paths are specified, only these are included.
+ * @param paths
+ * file names (e.g <code>file1.c</code>) or directory names (e.g.
+ * <code>dir</code> to add <code>dir/file1</code> and
+ * <code>dir/file2</code>) can also be given to add all files in
+ * the directory, recursively. Fileglobs (e.g. *.c) are not yet
+ * supported.
+ * @return this
+ */
+ public ArchiveCommand setPaths(String... paths) {
+ this.paths = Arrays.asList(paths);
+ return this;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
index fdf8c052fe..80dda8eb83 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -562,7 +562,7 @@ public class DirCacheCheckout {
* 6b D F F N N N N Conflict
* 7 F D F Y Y N N Update
* 8 F D F N Y N N Conflict
- * 9 F D F Y N N N Update
+ * 9 F D F N N N Conflict
* 10 F D D N N Y Keep
* 11 F D D N N N Conflict
* 12 F F D Y N Y N Update
@@ -610,7 +610,7 @@ public class DirCacheCheckout {
// switch processes all relevant cases.
switch (ffMask) {
case 0xDDF: // 1 2
- if (isModified(name)) {
+ if (f != null && isModifiedSubtree_IndexWorkingtree(name)) {
conflict(name, dce, h, m); // 1
} else {
update(name, mId, mMode); // 2
@@ -647,32 +647,29 @@ public class DirCacheCheckout {
break;
case 0xFDF: // 7 8 9
if (equalIdAndMode(hId, hMode, mId, mMode)) {
- if (isModified(name))
+ if (isModifiedSubtree_IndexWorkingtree(name))
conflict(name, dce, h, m); // 8
else
update(name, mId, mMode); // 7
- } else if (!isModified(name))
- update(name, mId, mMode); // 9
- else
- // To be confirmed - this case is not in the table.
- conflict(name, dce, h, m);
+ } else
+ conflict(name, dce, h, m); // 9
break;
case 0xFD0: // keep without a rule
keep(dce);
break;
case 0xFFD: // 12 13 14
if (equalIdAndMode(hId, hMode, iId, iMode))
- if (f == null
- || f.isModified(dce, true,
+ if (f != null
+ && f.isModified(dce, true,
this.walk.getObjectReader()))
- conflict(name, dce, h, m);
+ conflict(name, dce, h, m); // 13
else
- remove(name);
+ remove(name); // 12
else
- conflict(name, dce, h, m);
+ conflict(name, dce, h, m); // 14
break;
case 0x0DF: // 16 17
- if (!isModified(name))
+ if (!isModifiedSubtree_IndexWorkingtree(name))
update(name, mId, mMode);
else
conflict(name, dce, h, m);
@@ -684,12 +681,14 @@ public class DirCacheCheckout {
}
// if we have no file at all then there is nothing to do
- if ((ffMask & 0x222) == 0)
+ if ((ffMask & 0x222) == 0
+ && (f == null || FileMode.TREE.equals(f.getEntryFileMode())))
return;
if ((ffMask == 0x00F) && f != null && FileMode.TREE.equals(f.getEntryFileMode())) {
// File/Directory conflict case #20
conflict(name, null, h, m);
+ return;
}
if (i == null) {
@@ -768,7 +767,9 @@ public class DirCacheCheckout {
* </pre>
*/
- if (m == null || equalIdAndMode(mId, mMode, iId, iMode)) {
+ if (m == null
+ || !isModified_IndexTree(name, iId, iMode, mId, mMode,
+ mergeCommitTree)) {
// Merge contains nothing or the same as Index
// Nothing in Head
// Something in Index
@@ -824,7 +825,7 @@ public class DirCacheCheckout {
* clean I==H I==M H M Result
* -----------------------------------------------------
* 10 yes yes N/A exists nothing remove path from index
- * 11 no yes N/A exists nothing fail
+ * 11 no yes N/A exists nothing keep file
* 12 yes no N/A exists nothing fail
* 13 no no N/A exists nothing fail
* </pre>
@@ -841,23 +842,31 @@ public class DirCacheCheckout {
// Something different from a submodule in Index
// Nothing in Merge
// Something in Head
- if (equalIdAndMode(hId, hMode, iId, iMode)) {
+ if (!isModified_IndexTree(name, iId, iMode, hId, hMode,
+ headCommitTree)) {
// Index contains the same as Head
// Something different from a submodule in Index
// Nothing in Merge
// Something in Head
- if (f == null
- || f.isModified(dce, true,
- this.walk.getObjectReader()))
+ if (f != null
+ && f.isModified(dce, true,
+ this.walk.getObjectReader())) {
// file is dirty
// Index contains the same as Head
// Something different from a submodule in Index
// Nothing in Merge
// Something in Head
- // -> file is dirty but is should be removed. That's
- // a conflict
- conflict(name, dce, h, m);
- else
+
+ if (!FileMode.TREE.equals(f.getEntryFileMode())
+ && FileMode.TREE.equals(iMode))
+ // The workingtree contains a file and the index semantically contains a folder.
+ // Git considers the workingtree file as untracked. Just keep the untracked file.
+ return;
+ else
+ // -> file is dirty and tracked but is should be
+ // removed. That's a conflict
+ conflict(name, dce, h, m);
+ } else
// file doesn't exist or is clean
// Index contains the same as Head
// Something different from a submodule in Index
@@ -880,8 +889,10 @@ public class DirCacheCheckout {
// Something in Head
// Something in Index
if (!equalIdAndMode(hId, hMode, mId, mMode)
- && !equalIdAndMode(hId, hMode, iId, iMode)
- && !equalIdAndMode(mId, mMode, iId, iMode))
+ && isModified_IndexTree(name, iId, iMode, hId, hMode,
+ headCommitTree)
+ && isModified_IndexTree(name, iId, iMode, mId, mMode,
+ mergeCommitTree))
// All three contents in Head, Merge, Index differ from each
// other
// -> All contents differ. Report a conflict.
@@ -893,8 +904,10 @@ public class DirCacheCheckout {
// Something in Head
// Something in Index
- if (equalIdAndMode(hId, hMode, iId, iMode)
- && !equalIdAndMode(mId, mMode, iId, iMode)) {
+ if (!isModified_IndexTree(name, iId, iMode, hId, hMode,
+ headCommitTree)
+ && isModified_IndexTree(name, iId, iMode, mId, mMode,
+ mergeCommitTree)) {
// Head contains the same as Index. Merge differs
// Something in Merge
@@ -1036,25 +1049,88 @@ public class DirCacheCheckout {
}
}
- private boolean isModified(String path) throws CorruptObjectException, IOException {
+ /**
+ * Checks whether the subtree starting at a given path differs between Index and
+ * workingtree.
+ *
+ * @param path
+ * @return true if the subtrees differ
+ * @throws CorruptObjectException
+ * @throws IOException
+ */
+ private boolean isModifiedSubtree_IndexWorkingtree(String path)
+ throws CorruptObjectException, IOException {
+ NameConflictTreeWalk tw = new NameConflictTreeWalk(repo);
+ try {
+ tw.addTree(new DirCacheIterator(dc));
+ tw.addTree(new FileTreeIterator(repo));
+ tw.setRecursive(true);
+ tw.setFilter(PathFilter.create(path));
+ DirCacheIterator dcIt;
+ WorkingTreeIterator wtIt;
+ while (tw.next()) {
+ dcIt = tw.getTree(0, DirCacheIterator.class);
+ wtIt = tw.getTree(1, WorkingTreeIterator.class);
+ if (dcIt == null || wtIt == null)
+ return true;
+ if (wtIt.isModified(dcIt.getDirCacheEntry(), true,
+ this.walk.getObjectReader())) {
+ return true;
+ }
+ }
+ return false;
+ } finally {
+ tw.release();
+ }
+ }
+
+ private boolean isModified_IndexTree(String path, ObjectId iId,
+ FileMode iMode, ObjectId tId, FileMode tMode, ObjectId rootTree)
+ throws CorruptObjectException, IOException {
+ if (iMode != tMode)
+ return true;
+ if (FileMode.TREE.equals(iMode)
+ && (iId == null || ObjectId.zeroId().equals(iId)))
+ return isModifiedSubtree_IndexTree(path, rootTree);
+ else
+ return !equalIdAndMode(iId, iMode, tId, tMode);
+ }
+
+ /**
+ * Checks whether the subtree starting at a given path differs between Index and
+ * some tree.
+ *
+ * @param path
+ * @param tree
+ * the tree to compare
+ * @return true if the subtrees differ
+ * @throws CorruptObjectException
+ * @throws IOException
+ */
+ private boolean isModifiedSubtree_IndexTree(String path, ObjectId tree)
+ throws CorruptObjectException, IOException {
NameConflictTreeWalk tw = new NameConflictTreeWalk(repo);
- tw.addTree(new DirCacheIterator(dc));
- tw.addTree(new FileTreeIterator(repo));
- tw.setRecursive(true);
- tw.setFilter(PathFilter.create(path));
- DirCacheIterator dcIt;
- WorkingTreeIterator wtIt;
- while(tw.next()) {
- dcIt = tw.getTree(0, DirCacheIterator.class);
- wtIt = tw.getTree(1, WorkingTreeIterator.class);
- if (dcIt == null || wtIt == null)
- return true;
- if (wtIt.isModified(dcIt.getDirCacheEntry(), true,
- this.walk.getObjectReader())) {
- return true;
+ try {
+ tw.addTree(new DirCacheIterator(dc));
+ tw.addTree(tree);
+ tw.setRecursive(true);
+ tw.setFilter(PathFilter.create(path));
+ while (tw.next()) {
+ AbstractTreeIterator dcIt = tw.getTree(0,
+ DirCacheIterator.class);
+ AbstractTreeIterator treeIt = tw.getTree(1,
+ AbstractTreeIterator.class);
+ if (dcIt == null || treeIt == null)
+ return true;
+ if (dcIt.getEntryRawMode() != treeIt.getEntryRawMode())
+ return true;
+ if (!dcIt.getEntryObjectId().equals(treeIt.getEntryObjectId()))
+ return true;
}
+ return false;
+ } finally {
+ tw.release();
}
- return false;
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
index 964869bb87..9eb4285557 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -832,6 +832,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
*/
public boolean isModified(DirCacheEntry entry, boolean forceContentCheck,
ObjectReader reader) throws IOException {
+ if (entry == null)
+ return !FileMode.MISSING.equals(getEntryFileMode());
MetadataDiff diff = compareMetadata(entry);
switch (diff) {
case DIFFER_BY_TIMESTAMP: