Merge "Bazel: Remove superfluous dependencies flagged by unused_deps"
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/attributes/add.png b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/attributes/add.png
new file mode 100644
index 0000000..c6aeae4
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/attributes/add.png
Binary files differ
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributeFileTests.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributeFileTests.java
new file mode 100644
index 0000000..5d05a98
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributeFileTests.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2020 Thomas Wolf <thomas.wolf@paranor.ch> 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.attributes;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.Arrays;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.ResetCommand.ResetType;
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.IO;
+import org.eclipse.jgit.util.RawParseUtils;
+import org.junit.Test;
+
+/**
+ * End-to-end tests for some attribute combinations. Writes files, commit them,
+ * examines the index, deletes the files, performs a hard reset and checks file
+ * contents again.
+ */
+public class AttributeFileTests extends RepositoryTestCase {
+
+	@Test
+	public void testTextAutoCoreEolCoreAutoCrLfInput() throws Exception {
+		FileBasedConfig cfg = db.getConfig();
+		cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+				ConfigConstants.CONFIG_KEY_AUTOCRLF, false);
+		cfg.save();
+		final String content = "Line1\nLine2\n";
+		try (Git git = Git.wrap(db)) {
+			writeTrashFile(".gitattributes", "* text=auto");
+			File dummy = writeTrashFile("dummy.txt", content);
+			git.add().addFilepattern(".").call();
+			git.commit().setMessage("Commit with LF").call();
+			assertEquals("Unexpected index state",
+					"[.gitattributes, mode:100644, content:* text=auto]"
+							+ "[dummy.txt, mode:100644, content:" + content
+							+ ']',
+					indexState(CONTENT));
+			assertTrue("Should be able to delete " + dummy, dummy.delete());
+			cfg.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
+					ConfigConstants.CONFIG_KEY_EOL, "crlf");
+			cfg.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
+					ConfigConstants.CONFIG_KEY_AUTOCRLF, "input");
+			cfg.save();
+			git.reset().setMode(ResetType.HARD).call();
+			assertTrue("File " + dummy + "should exist", dummy.isFile());
+			String textFile = RawParseUtils.decode(IO.readFully(dummy, 512));
+			assertEquals("Unexpected text content", content, textFile);
+		}
+	}
+
+	@Test
+	public void testTextAutoEolLf() throws Exception {
+		writeTrashFile(".gitattributes", "* text=auto eol=lf");
+		performTest("Test\r\nFile", "Test\nFile", "Test\nFile");
+	}
+
+	@Test
+	public void testTextAutoEolCrLf() throws Exception {
+		writeTrashFile(".gitattributes", "* text=auto eol=crlf");
+		performTest("Test\r\nFile", "Test\nFile", "Test\r\nFile");
+	}
+
+	private void performTest(String initial, String index, String finalText)
+			throws Exception {
+		File dummy = writeTrashFile("dummy.foo", initial);
+		byte[] data = readTestResource("add.png");
+		assertTrue("Expected some binary data", data.length > 100);
+		File binary = writeTrashFile("add.png", "");
+		Files.write(binary.toPath(), data);
+		try (Git git = Git.wrap(db)) {
+			git.add().addFilepattern(".").call();
+			git.commit().setMessage("test commit").call();
+			// binary should be unchanged, dummy should match "index"
+			verifyIndexContent("dummy.foo",
+					index.getBytes(StandardCharsets.UTF_8));
+			verifyIndexContent("add.png", data);
+			assertTrue("Should be able to delete " + dummy, dummy.delete());
+			assertTrue("Should be able to delete " + binary, binary.delete());
+			git.reset().setMode(ResetType.HARD).call();
+			assertTrue("File " + dummy + " should exist", dummy.isFile());
+			assertTrue("File " + binary + " should exist", binary.isFile());
+			// binary should be unchanged, dummy should match "finalText"
+			String textFile = RawParseUtils.decode(IO.readFully(dummy, 512));
+			assertEquals("Unexpected text content", finalText, textFile);
+			byte[] binaryFile = IO.readFully(binary, 512);
+			assertArrayEquals("Unexpected binary content", data, binaryFile);
+		}
+	}
+
+	private byte[] readTestResource(String name) throws Exception {
+		try (InputStream in = new BufferedInputStream(
+				getClass().getResourceAsStream(name))) {
+			byte[] data = new byte[512];
+			int read = in.read(data);
+			if (read == data.length) {
+				return data;
+			}
+			return Arrays.copyOf(data, read);
+		}
+	}
+
+	private void verifyIndexContent(String path, byte[] expectedContent)
+			throws Exception {
+		DirCache dc = db.readDirCache();
+		for (int i = 0; i < dc.getEntryCount(); ++i) {
+			DirCacheEntry entry = dc.getEntry(i);
+			if (path.equals(entry.getPathString())) {
+				byte[] data = db.open(entry.getObjectId(), Constants.OBJ_BLOB)
+						.getCachedBytes();
+				assertArrayEquals("Unexpected index content for " + path,
+						expectedContent, data);
+				return;
+			}
+		}
+		fail("Path not found in index: " + path);
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java
index deab4e6..c33c869 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java
@@ -140,19 +140,19 @@
 		}
 
 		// new git system
-		String eol = attrs.getValue("eol"); //$NON-NLS-1$
-		if (eol != null)
-			// check-in is always normalized to LF
-			return EolStreamType.TEXT_LF;
-
-		if (attrs.isSet("text")) { //$NON-NLS-1$
-			return EolStreamType.TEXT_LF;
-		}
-
 		if ("auto".equals(attrs.getValue("text"))) { //$NON-NLS-1$ //$NON-NLS-2$
 			return EolStreamType.AUTO_LF;
 		}
 
+		String eol = attrs.getValue("eol"); //$NON-NLS-1$
+		if (eol != null) {
+			// check-in is always normalized to LF
+			return EolStreamType.TEXT_LF;
+		}
+		if (attrs.isSet("text")) { //$NON-NLS-1$
+			return EolStreamType.TEXT_LF;
+		}
+
 		switch (options.getAutoCRLF()) {
 		case TRUE:
 		case INPUT:
@@ -168,6 +168,8 @@
 		switch (options.getAutoCRLF()) {
 		case TRUE:
 			return EolStreamType.TEXT_CRLF;
+		case INPUT:
+			return EolStreamType.DIRECT;
 		default:
 			// no decision
 		}
@@ -205,7 +207,10 @@
 		// new git system
 		String eol = attrs.getValue("eol"); //$NON-NLS-1$
 		if (eol != null) {
-			if ("crlf".equals(eol)) {//$NON-NLS-1$
+			if ("crlf".equals(eol)) { //$NON-NLS-1$
+				if ("auto".equals(attrs.getValue("text"))) { //$NON-NLS-1$ //$NON-NLS-2$
+					return EolStreamType.AUTO_CRLF;
+				}
 				return EolStreamType.TEXT_CRLF;
 			} else if ("lf".equals(eol)) { //$NON-NLS-1$
 				return EolStreamType.DIRECT;