From 3c34e0acbfcac236daaf746690130c6e99a8e524 Mon Sep 17 00:00:00 2001 From: Thomas Wolf Date: Mon, 23 Mar 2020 16:55:35 +0100 Subject: [PATCH] Attributes: fix handling of text=auto in combination with eol In Git 2.10.0 the interpretation of gitattributes changed or was fixed such that "* text=auto eol=crlf" would indeed still do auto-detection of text vs. binary content.[1] Previously this was identical to "* text eol=crlf", i.e., treating all files as text. JGit still did the latter, which caused surprises because it changed binary files. [1] https://github.com/git/git/blob/master/Documentation/RelNotes/2.10.0.txt#L248 Bug: 561341 Change-Id: I5b6fb97b5e86fd950a98537b6b8574f768ae30e5 Signed-off-by: Thomas Wolf --- .../org/eclipse/jgit/attributes/add.png | Bin 0 -> 366 bytes .../jgit/attributes/AttributeFileTests.java | 107 ++++++++++++++++++ .../jgit/util/io/EolStreamTypeUtil.java | 17 +-- 3 files changed, 117 insertions(+), 7 deletions(-) create mode 100644 org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/attributes/add.png create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributeFileTests.java 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 0000000000000000000000000000000000000000..c6aeae4d462dd0c8f31506fbd5b1657fc51c459b GIT binary patch literal 366 zcmV-!0g?WRP)s0KuLhCE+?CHy}zc-p?O|6y$*s0MhH1pM-=_WumSi4y`o&pI3MAA~!0 z`u_)teSyjOR0ezl((7;rs6&B=f^VJI^VXxD|3SEPgV%qz63^8{Mb%7 literal 0 HcmV?d00001 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 000000000..7b90dcda0 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributeFileTests.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2020 Thomas Wolf 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.Constants; +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 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 deab4e67a..a6acb40ee 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 @@ public final class EolStreamTypeUtil { } // new git system + 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) + 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; - } - switch (options.getAutoCRLF()) { case TRUE: case INPUT: @@ -205,7 +205,10 @@ public final class EolStreamTypeUtil { // 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;