]> source.dussan.org Git - jgit.git/commitdiff
Fix handling of core.eol=native and of crlf attribute 22/127322/5
authorThomas Wolf <thomas.wolf@paranor.ch>
Sun, 12 Aug 2018 12:27:21 +0000 (14:27 +0200)
committerDavid Pursehouse <david.pursehouse@gmail.com>
Thu, 4 Oct 2018 23:17:41 +0000 (19:17 -0400)
EolStreamTypeUtil didn't handle these correctly on Windows.

Add three new tests to verify that the crlf attribute is handled as
described at [1], and that core.eol=native produces the expected
line endings on check-out.

[1] https://git-scm.com/docs/gitattributes

Bug: 497290
Change-Id: Idd9b435e3256c1e3251cc7b966f2f0460e787f07
Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CrLfNativeTest.java [new file with mode: 0644]
org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java

diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CrLfNativeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CrLfNativeTest.java
new file mode 100644 (file)
index 0000000..c726128
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * 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.assertTrue;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jgit.api.ResetCommand.ResetType;
+import org.eclipse.jgit.junit.MockSystemReader;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.treewalk.FileTreeIterator;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
+import org.eclipse.jgit.util.SystemReader;
+import org.junit.Test;
+
+public class CrLfNativeTest extends RepositoryTestCase {
+
+       @Test
+       public void checkoutWithCrLfNativeUnix() throws Exception {
+               verifyNativeCheckout(new MockSystemReader() {
+                       {
+                               setUnix();
+                       }
+               });
+       }
+
+       @Test
+       public void checkoutWithCrLfNativeWindows() throws Exception {
+               verifyNativeCheckout(new MockSystemReader() {
+                       {
+                               setWindows();
+                       }
+               });
+       }
+
+       private void verifyNativeCheckout(SystemReader systemReader)
+                       throws Exception {
+               SystemReader.setInstance(systemReader);
+               Git git = Git.wrap(db);
+               FileBasedConfig config = db.getConfig();
+               config.setString("core", null, "autocrlf", "false");
+               config.setString("core", null, "eol", "native");
+               config.save();
+               // core.eol is active only if text is set, or if text=auto
+               writeTrashFile(".gitattributes", "*.txt text\n");
+               File file = writeTrashFile("file.txt", "line 1\nline 2\n");
+               git.add().addFilepattern("file.txt").addFilepattern(".gitattributes")
+                               .call();
+               git.commit().setMessage("Initial").call();
+               // Check-in with core.eol=native normalization
+               assertEquals(
+                               "[.gitattributes, mode:100644, content:*.txt text\n]"
+                                               + "[file.txt, mode:100644, content:line 1\nline 2\n]",
+                               indexState(CONTENT));
+               writeTrashFile("file.txt", "something else");
+               git.add().addFilepattern("file.txt").call();
+               git.commit().setMessage("New commit").call();
+               git.reset().setMode(ResetType.HARD).setRef("HEAD~").call();
+               // Check-out should convert to the native line separator
+               checkFile(file, systemReader.isWindows() ? "line 1\r\nline 2\r\n"
+                               : "line 1\nline 2\n");
+               Status status = git.status().call();
+               assertTrue("git status should be clean", status.isClean());
+       }
+
+       /**
+        * Verifies the handling of the crlf attribute: crlf == text, -crlf ==
+        * -text, crlf=input == eol=lf
+        *
+        * @throws Exception
+        */
+       @Test
+       public void testCrLfAttribute() throws Exception {
+               FileBasedConfig config = db.getConfig();
+               config.setString("core", null, "autocrlf", "false");
+               config.setString("core", null, "eol", "crlf");
+               config.save();
+               writeTrashFile(".gitattributes",
+                               "*.txt text\n*.crlf crlf\n*.bin -text\n*.nocrlf -crlf\n*.input crlf=input\n*.eol eol=lf");
+               writeTrashFile("foo.txt", "");
+               writeTrashFile("foo.crlf", "");
+               writeTrashFile("foo.bin", "");
+               writeTrashFile("foo.nocrlf", "");
+               writeTrashFile("foo.input", "");
+               writeTrashFile("foo.eol", "");
+               Map<String, EolStreamType> inTypes = new HashMap<>();
+               Map<String, EolStreamType> outTypes = new HashMap<>();
+               try (TreeWalk walk = new TreeWalk(db)) {
+                       walk.addTree(new FileTreeIterator(db));
+                       while (walk.next()) {
+                               String path = walk.getPathString();
+                               if (".gitattributes".equals(path)) {
+                                       continue;
+                               }
+                               EolStreamType in = walk
+                                               .getEolStreamType(OperationType.CHECKIN_OP);
+                               EolStreamType out = walk
+                                               .getEolStreamType(OperationType.CHECKOUT_OP);
+                               inTypes.put(path, in);
+                               outTypes.put(path, out);
+                       }
+               }
+               assertEquals("", checkTypes("check-in", inTypes));
+               assertEquals("", checkTypes("check-out", outTypes));
+       }
+
+       private String checkTypes(String prefix, Map<String, EolStreamType> types) {
+               StringBuilder result = new StringBuilder();
+               EolStreamType a = types.get("foo.crlf");
+               EolStreamType b = types.get("foo.txt");
+               report(result, prefix, "crlf != text", a, b);
+               a = types.get("foo.nocrlf");
+               b = types.get("foo.bin");
+               report(result, prefix, "-crlf != -text", a, b);
+               a = types.get("foo.input");
+               b = types.get("foo.eol");
+               report(result, prefix, "crlf=input != eol=lf", a, b);
+               return result.toString();
+       }
+
+       private void report(StringBuilder result, String prefix, String label,
+                       EolStreamType a,
+                       EolStreamType b) {
+               if (a == null || b == null || !a.equals(b)) {
+                       result.append(prefix).append(' ').append(label).append(": ")
+                                       .append(toString(a)).append(" != ").append(toString(b))
+                                       .append('\n');
+               }
+       }
+
+       private String toString(EolStreamType type) {
+               return type == null ? "null" : type.name();
+       }
+}
index 822961f8ded0f736b42f8f8babb5ca48973a0907..d7c6bec2191fe3254e61cae9df3135121c3a75b2 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.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
@@ -49,6 +50,7 @@ import org.eclipse.jgit.attributes.Attributes;
 import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
 import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
 import org.eclipse.jgit.treewalk.WorkingTreeOptions;
+import org.eclipse.jgit.util.SystemReader;
 
 /**
  * Utility used to create input and output stream wrappers for
@@ -57,7 +59,6 @@ import org.eclipse.jgit.treewalk.WorkingTreeOptions;
  * @since 4.3
  */
 public final class EolStreamTypeUtil {
-       private static final boolean FORCE_EOL_LF_ON_CHECKOUT = false;
 
        private EolStreamTypeUtil() {
        }
@@ -164,11 +165,11 @@ public final class EolStreamTypeUtil {
 
                // old git system
                if (attrs.isSet("crlf")) {//$NON-NLS-1$
-                       return EolStreamType.TEXT_LF;
+                       return EolStreamType.TEXT_LF; // Same as isSet("text")
                } else if (attrs.isUnset("crlf")) {//$NON-NLS-1$
-                       return EolStreamType.DIRECT;
+                       return EolStreamType.DIRECT; // Same as isUnset("text")
                } else if ("input".equals(attrs.getValue("crlf"))) {//$NON-NLS-1$ //$NON-NLS-2$
-                       return EolStreamType.TEXT_LF;
+                       return EolStreamType.TEXT_LF; // Same as eol=lf
                }
 
                // new git system
@@ -196,6 +197,28 @@ public final class EolStreamTypeUtil {
                return EolStreamType.DIRECT;
        }
 
+       private static EolStreamType getOutputFormat(WorkingTreeOptions options) {
+               switch (options.getAutoCRLF()) {
+               case TRUE:
+                       return EolStreamType.TEXT_CRLF;
+               default:
+                       // no decision
+               }
+               switch (options.getEOL()) {
+               case CRLF:
+                       return EolStreamType.TEXT_CRLF;
+               case NATIVE:
+                       if (SystemReader.getInstance().isWindows()) {
+                               return EolStreamType.TEXT_CRLF;
+                       }
+                       return EolStreamType.TEXT_LF;
+               case LF:
+               default:
+                       break;
+               }
+               return EolStreamType.DIRECT;
+       }
+
        private static EolStreamType checkOutStreamType(WorkingTreeOptions options,
                        Attributes attrs) {
                if (attrs.isUnset("text")) {//$NON-NLS-1$
@@ -205,57 +228,35 @@ public final class EolStreamTypeUtil {
 
                // old git system
                if (attrs.isSet("crlf")) {//$NON-NLS-1$
-                       return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF
-                                       : EolStreamType.DIRECT;
+                       return getOutputFormat(options); // Same as isSet("text")
                } else if (attrs.isUnset("crlf")) {//$NON-NLS-1$
-                       return EolStreamType.DIRECT;
+                       return EolStreamType.DIRECT; // Same as isUnset("text")
                } else if ("input".equals(attrs.getValue("crlf"))) {//$NON-NLS-1$ //$NON-NLS-2$
-                       return EolStreamType.DIRECT;
+                       return EolStreamType.DIRECT; // Same as eol=lf
                }
 
                // new git system
                String eol = attrs.getValue("eol"); //$NON-NLS-1$
-               if (eol != null && "crlf".equals(eol)) //$NON-NLS-1$
-                       return EolStreamType.TEXT_CRLF;
-               if (eol != null && "lf".equals(eol)) //$NON-NLS-1$
-                       return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF
-                                       : EolStreamType.DIRECT;
-
-               if (attrs.isSet("text")) { //$NON-NLS-1$
-                       switch (options.getAutoCRLF()) {
-                       case TRUE:
-                               return EolStreamType.TEXT_CRLF;
-                       default:
-                               // no decision
-                       }
-                       switch (options.getEOL()) {
-                       case CRLF:
+               if (eol != null) {
+                       if ("crlf".equals(eol)) {//$NON-NLS-1$
                                return EolStreamType.TEXT_CRLF;
-                       case LF:
-                               return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF
-                                               : EolStreamType.DIRECT;
-                       case NATIVE:
-                       default:
+                       } else if ("lf".equals(eol)) { //$NON-NLS-1$
                                return EolStreamType.DIRECT;
                        }
                }
+               if (attrs.isSet("text")) { //$NON-NLS-1$
+                       return getOutputFormat(options);
+               }
 
                if ("auto".equals(attrs.getValue("text"))) { //$NON-NLS-1$ //$NON-NLS-2$
-                       switch (options.getAutoCRLF()) {
-                       case TRUE:
+                       EolStreamType basic = getOutputFormat(options);
+                       switch (basic) {
+                       case TEXT_CRLF:
                                return EolStreamType.AUTO_CRLF;
+                       case TEXT_LF:
+                               return EolStreamType.AUTO_LF;
                        default:
-                               // no decision
-                       }
-                       switch (options.getEOL()) {
-                       case CRLF:
-                               return EolStreamType.AUTO_CRLF;
-                       case LF:
-                               return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF
-                                               : EolStreamType.DIRECT;
-                       case NATIVE:
-                       default:
-                               return EolStreamType.DIRECT;
+                               return basic;
                        }
                }