Browse Source

Add EOL stream type detection to TreeWalk

TreeWalk provides the new method getEolStreamType. This new method can
be used with EolStreamTypeUtil in order to create a wrapped InputStream
or OutputStream when reading / writing files. The implementation
implements support for the git configuration options core.crlf, core.eol
and the .gitattributes "text", "eol" and "binary"

CQ: 10896
Bug: 486563
Change-Id: Ie4f6367afc2a6aec1de56faf95120fff0339a358
Signed-off-by: Ivan Motsch <ivan.motsch@bsiag.com>
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
stable-4.3
Ivan Motsch 9 years ago committed by Christian Halstrick
parent
commit
b811e4399e
  1. 692
      org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java
  2. 335
      org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolStreamTypeUtilTest.java
  3. 3
      org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
  4. 7
      org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoLFInputStreamTest.java
  5. 21
      org.eclipse.jgit/.settings/.api_filters
  6. 4
      org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java
  7. 22
      org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java
  8. 12
      org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java
  9. 4
      org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java
  10. 127
      org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
  11. 7
      org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
  12. 40
      org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
  13. 13
      org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java
  14. 89
      org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java
  15. 134
      org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
  16. 14
      org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeOptions.java
  17. 77
      org.eclipse.jgit/src/org/eclipse/jgit/util/Holder.java
  18. 2
      org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFInputStream.java
  19. 21
      org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFOutputStream.java
  20. 199
      org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java
  21. 200
      org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFOutputStream.java
  22. 121
      org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolCanonicalizingInputStream.java
  23. 255
      org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java

692
org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolRepositoryTest.java

@ -0,0 +1,692 @@
/*
* Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com>
*
* 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.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import org.eclipse.jgit.api.ResetCommand.ResetType;
import org.eclipse.jgit.api.errors.CheckoutConflictException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.NoFilepatternException;
import org.eclipse.jgit.attributes.Attribute;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.errors.RevisionSyntaxException;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
import org.eclipse.jgit.lib.CoreConfig.EOL;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.treewalk.FileTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.IO;
import org.junit.Assert;
import org.junit.Test;
import org.junit.experimental.theories.DataPoint;
import org.junit.experimental.theories.Theories;
import org.junit.runner.RunWith;
/**
* Unit tests for end-of-line conversion and settings using core.autocrlf, *
* core.eol and the .gitattributes eol, text, binary (macro for -diff -merge
* -text)
*/
@RunWith(Theories.class)
public class EolRepositoryTest extends RepositoryTestCase {
private static final FileMode D = FileMode.TREE;
private static final FileMode F = FileMode.REGULAR_FILE;
@DataPoint
public static String smallContents[] = {
generateTestData(3, 1, true, false),
generateTestData(3, 1, false, true),
generateTestData(3, 1, true, true) };
@DataPoint
public static String hugeContents[] = {
generateTestData(1000000, 17, true, false),
generateTestData(1000000, 17, false, true),
generateTestData(1000000, 17, true, true) };
static String generateTestData(int size, int lineSize, boolean withCRLF,
boolean withLF) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < size; i++) {
if (i > 0 && i % lineSize == 0) {
// newline
if (withCRLF && withLF) {
// mixed
if (i % 2 == 0)
sb.append("\r\n");
else
sb.append("\n");
} else if (withCRLF) {
sb.append("\r\n");
} else if (withLF) {
sb.append("\n");
}
}
sb.append("A");
}
return sb.toString();
}
public EolRepositoryTest(String[] testContent) {
CONTENT_CRLF = testContent[0];
CONTENT_LF = testContent[1];
CONTENT_MIXED = testContent[2];
}
protected String CONTENT_CRLF;
protected String CONTENT_LF;
protected String CONTENT_MIXED;
private TreeWalk walk;
/** work tree root .gitattributes */
private File dotGitattributes;
/** file containing CRLF */
private File fileCRLF;
/** file containing LF */
private File fileLF;
/** file containing mixed CRLF and LF */
private File fileMixed;
/** this values are set in {@link #collectRepositoryState()} */
private static class ActualEntry {
private String attrs;
private String file;
private String index;
private int indexContentLength;
}
private ActualEntry entryCRLF = new ActualEntry();
private ActualEntry entryLF = new ActualEntry();
private ActualEntry entryMixed = new ActualEntry();
private DirCache dc;
@Test
public void testDefaultSetup() throws Exception {
// for EOL to work, the text attribute must be set
setupGitAndDoHardReset(null, null, null, null, "* text=auto");
collectRepositoryState();
assertEquals("text=auto", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
}
public void checkEntryContent(ActualEntry entry, String fileContent,
String indexContent) {
assertEquals(fileContent, entry.file);
assertEquals(indexContent, entry.index);
assertEquals(fileContent.length(), entry.indexContentLength);
}
@Test
public void test_ConfigAutoCRLF_false() throws Exception {
// for EOL to work, the text attribute must be set
setupGitAndDoHardReset(AutoCRLF.FALSE, null, null, null, "* text=auto");
collectRepositoryState();
assertEquals("text=auto", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
}
@Test
public void test_ConfigAutoCRLF_true() throws Exception {
// for EOL to work, the text attribute must be set
setupGitAndDoHardReset(AutoCRLF.TRUE, null, null, null, "* text=auto");
collectRepositoryState();
assertEquals("text=auto", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
}
@Test
public void test_ConfigAutoCRLF_input() throws Exception {
// for EOL to work, the text attribute must be set
setupGitAndDoHardReset(AutoCRLF.INPUT, null, null, null, "* text=auto");
collectRepositoryState();
assertEquals("text=auto", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
}
@Test
public void test_ConfigEOL_lf() throws Exception {
// for EOL to work, the text attribute must be set
setupGitAndDoHardReset(null, EOL.LF, "*.txt text", null, null);
collectRepositoryState();
assertEquals("text", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
}
@Test
public void test_ConfigEOL_crlf() throws Exception {
// for EOL to work, the text attribute must be set
setupGitAndDoHardReset(null, EOL.CRLF, "*.txt text", null, null);
collectRepositoryState();
assertEquals("text", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
}
@Test
public void test_ConfigEOL_native_windows() throws Exception {
String origLineSeparator = System.getProperty("line.separator", "\n");
System.setProperty("line.separator", "\r\n");
try {
// for EOL to work, the text attribute must be set
setupGitAndDoHardReset(null, EOL.NATIVE, "*.txt text", null, null);
collectRepositoryState();
assertEquals("text", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
} finally {
System.setProperty("line.separator", origLineSeparator);
}
}
@Test
public void test_ConfigEOL_native_xnix() throws Exception {
String origLineSeparator = System.getProperty("line.separator", "\n");
System.setProperty("line.separator", "\n");
try {
// for EOL to work, the text attribute must be set
setupGitAndDoHardReset(null, EOL.NATIVE, "*.txt text", null, null);
collectRepositoryState();
assertEquals("text", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
} finally {
System.setProperty("line.separator", origLineSeparator);
}
}
@Test
public void test_ConfigAutoCRLF_false_ConfigEOL_lf() throws Exception {
// for EOL to work, the text attribute must be set
setupGitAndDoHardReset(AutoCRLF.FALSE, EOL.LF, "*.txt text", null, null);
collectRepositoryState();
assertEquals("text", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
}
@Test
public void test_ConfigAutoCRLF_false_ConfigEOL_native() throws Exception {
// for EOL to work, the text attribute must be set
setupGitAndDoHardReset(AutoCRLF.FALSE, EOL.NATIVE, "*.txt text", null, null);
collectRepositoryState();
assertEquals("text", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
}
@Test
public void test_ConfigAutoCRLF_true_ConfigEOL_lf() throws Exception {
// for EOL to work, the text attribute must be set
setupGitAndDoHardReset(AutoCRLF.TRUE, EOL.LF, "*.txt text", null, null);
collectRepositoryState();
assertEquals("text", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
}
@Test
public void test_switchToBranchWithTextAttributes()
throws Exception {
Git git = Git.wrap(db);
// for EOL to work, the text attribute must be set
setupGitAndDoHardReset(AutoCRLF.FALSE, EOL.CRLF, null, null,
"file1.txt text\nfile2.txt text\nfile3.txt text");
collectRepositoryState();
assertEquals("text", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
// switch to binary for file1
dotGitattributes = createAndAddFile(git, Constants.DOT_GIT_ATTRIBUTES,
"file1.txt binary\nfile2.txt text\nfile3.txt text");
gitCommit(git, "switchedToBinaryFor1");
recreateWorktree(git);
collectRepositoryState();
assertEquals("binary -diff -merge -text", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
assertEquals("text", entryLF.attrs);
checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
assertEquals("text", entryMixed.attrs);
checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
// checkout the commit which has text for file1
gitCheckout(git, "HEAD^");
recreateWorktree(git);
collectRepositoryState();
assertEquals("text", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
}
@Test
public void test_switchToBranchWithBinaryAttributes() throws Exception {
Git git = Git.wrap(db);
// for EOL to work, the text attribute must be set
setupGitAndDoHardReset(AutoCRLF.FALSE, EOL.LF, null, null,
"file1.txt binary\nfile2.txt binary\nfile3.txt binary");
collectRepositoryState();
assertEquals("binary -diff -merge -text", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_CRLF);
checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_MIXED, CONTENT_MIXED);
// switch to text for file1
dotGitattributes = createAndAddFile(git, Constants.DOT_GIT_ATTRIBUTES,
"file1.txt text\nfile2.txt binary\nfile3.txt binary");
gitCommit(git, "switchedToTextFor1");
recreateWorktree(git);
collectRepositoryState();
assertEquals("text", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
assertEquals("binary -diff -merge -text", entryLF.attrs);
checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
assertEquals("binary -diff -merge -text", entryMixed.attrs);
checkEntryContent(entryMixed, CONTENT_MIXED, CONTENT_MIXED);
// checkout the commit which has text for file1
gitCheckout(git, "HEAD^");
recreateWorktree(git);
collectRepositoryState();
assertEquals("binary -diff -merge -text", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_CRLF);
checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_MIXED, CONTENT_MIXED);
}
@Test
public void test_ConfigAutoCRLF_input_ConfigEOL_lf() throws Exception {
// for EOL to work, the text attribute must be set
setupGitAndDoHardReset(AutoCRLF.INPUT, EOL.LF, "*.txt text", null, null);
collectRepositoryState();
assertEquals("text", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
}
@Test
public void test_ConfigAutoCRLF_true_GlobalEOL_lf() throws Exception {
setupGitAndDoHardReset(AutoCRLF.TRUE, EOL.LF, "*.txt eol=lf", null, null);
collectRepositoryState();
assertEquals("eol=lf", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
}
@Test
public void test_ConfigAutoCRLF_false_GlobalEOL_lf() throws Exception {
setupGitAndDoHardReset(AutoCRLF.FALSE, EOL.LF, "*.txt eol=lf", null, null);
collectRepositoryState();
assertEquals("eol=lf", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
}
@Test
public void test_ConfigAutoCRLF_input_GlobalEOL_lf() throws Exception {
setupGitAndDoHardReset(AutoCRLF.INPUT, EOL.LF, "*.txt eol=lf", null, null);
collectRepositoryState();
assertEquals("eol=lf", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
}
@Test
public void test_ConfigAutoCRLF_true_GlobalEOL_crlf() throws Exception {
setupGitAndDoHardReset(AutoCRLF.TRUE, EOL.LF, "*.txt eol=crlf", null, null);
collectRepositoryState();
assertEquals("eol=crlf", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
}
@Test
public void test_ConfigAutoCRLF_false_GlobalEOL_crlf() throws Exception {
setupGitAndDoHardReset(AutoCRLF.FALSE, EOL.LF, "*.txt eol=crlf", null, null);
collectRepositoryState();
assertEquals("eol=crlf", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
}
@Test
public void test_ConfigAutoCRLF_input_GlobalEOL_crlf() throws Exception {
setupGitAndDoHardReset(AutoCRLF.INPUT, EOL.LF, "*.txt eol=crlf", null, null);
collectRepositoryState();
assertEquals("eol=crlf", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
}
@Test
public void test_ConfigAutoCRLF_true_GlobalEOL_lf_InfoEOL_crlf()
throws Exception {
setupGitAndDoHardReset(AutoCRLF.TRUE, null, "*.txt eol=lf", "*.txt eol=crlf", null);
// info decides
collectRepositoryState();
assertEquals("eol=crlf", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
}
@Test
public void test_ConfigAutoCRLF_false_GlobalEOL_crlf_InfoEOL_lf()
throws Exception {
setupGitAndDoHardReset(AutoCRLF.FALSE, null, "*.txt eol=crlf", "*.txt eol=lf", null);
// info decides
collectRepositoryState();
assertEquals("eol=lf", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
}
@Test
public void test_GlobalEOL_lf_RootEOL_crlf() throws Exception {
setupGitAndDoHardReset(null, null, "*.txt eol=lf", null, "*.txt eol=crlf");
// root over global
collectRepositoryState();
assertEquals("eol=crlf", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
}
@Test
public void test_GlobalEOL_lf_InfoEOL_crlf_RootEOL_lf() throws Exception {
setupGitAndDoHardReset(null, null, "*.txt eol=lf", "*.txt eol=crlf", "*.txt eol=lf");
// info overrides all
collectRepositoryState();
assertEquals("eol=crlf", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
}
@Test
public void test_GlobalEOL_lf_InfoEOL_crlf_RootEOL_unspec()
throws Exception {
setupGitAndDoHardReset(null, null, "*.txt eol=lf", "*.txt eol=crlf",
"*.txt text !eol");
// info overrides all
collectRepositoryState();
assertEquals("eol=crlf text", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_CRLF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_CRLF, CONTENT_LF);
}
@Test
public void test_GlobalEOL_lf_InfoEOL_unspec_RootEOL_crlf()
throws Exception {
setupGitAndDoHardReset(null, null, "*.txt eol=lf", "*.txt !eol",
"*.txt text eol=crlf");
// info overrides all
collectRepositoryState();
assertEquals("text", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_LF, CONTENT_LF);
}
@Test
public void testBinary1() throws Exception {
setupGitAndDoHardReset(AutoCRLF.TRUE, EOL.CRLF, "*.txt text", "*.txt binary",
"*.txt eol=crlf");
// info overrides all
collectRepositoryState();
assertEquals("binary -diff -merge -text eol=crlf", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_CRLF);
checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_MIXED, CONTENT_MIXED);
}
@Test
public void testBinary2() throws Exception {
setupGitAndDoHardReset(AutoCRLF.TRUE, EOL.CRLF, "*.txt text eol=crlf", null,
"*.txt binary");
// root over global
collectRepositoryState();
assertEquals("binary -diff -merge -text eol=crlf", entryCRLF.attrs);
checkEntryContent(entryCRLF, CONTENT_CRLF, CONTENT_CRLF);
checkEntryContent(entryLF, CONTENT_LF, CONTENT_LF);
checkEntryContent(entryMixed, CONTENT_MIXED, CONTENT_MIXED);
}
// create new repo with
// global .gitattributes
// info .git/config/info/.gitattributes
// workdir root .gitattributes
// text file lf.txt CONTENT_LF
// text file crlf.txt CONTENT_CRLF
//
// commit files (checkin)
// delete working dir files
// reset hard (checkout)
private void setupGitAndDoHardReset(AutoCRLF autoCRLF, EOL eol,
String globalAttributesContent, String infoAttributesContent,
String workDirRootAttributesContent) throws Exception {
Git git = new Git(db);
FileBasedConfig config = db.getConfig();
if (autoCRLF != null) {
config.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
ConfigConstants.CONFIG_KEY_AUTOCRLF, autoCRLF);
}
if (eol != null) {
config.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
ConfigConstants.CONFIG_KEY_EOL, eol);
}
if (globalAttributesContent != null) {
File f = new File(db.getDirectory(), "global/attrs");
write(f, globalAttributesContent);
config.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
ConfigConstants.CONFIG_KEY_ATTRIBUTESFILE,
f.getAbsolutePath());
}
if (infoAttributesContent != null) {
File f = new File(db.getDirectory(), Constants.INFO_ATTRIBUTES);
write(f, infoAttributesContent);
}
config.save();
if (workDirRootAttributesContent != null) {
dotGitattributes = createAndAddFile(git,
Constants.DOT_GIT_ATTRIBUTES, workDirRootAttributesContent);
} else {
dotGitattributes = null;
}
fileCRLF = createAndAddFile(git, "file1.txt", CONTENT_CRLF);
fileLF = createAndAddFile(git, "file2.txt", CONTENT_LF);
fileMixed = createAndAddFile(git, "file3.txt", CONTENT_MIXED);
gitCommit(git, "addFiles");
recreateWorktree(git);
}
private void recreateWorktree(Git git)
throws GitAPIException, CheckoutConflictException,
InterruptedException, IOException, NoFilepatternException {
// re-create file from the repo
for (File f : new File[] { dotGitattributes, fileCRLF, fileLF, fileMixed }) {
if (f == null)
continue;
f.delete();
Assert.assertFalse(f.exists());
}
gitResetHard(git);
fsTick(db.getIndexFile());
gitAdd(git, ".");
}
protected void gitCommit(Git git, String msg) throws GitAPIException {
git.commit().setMessage(msg).call();
}
protected void gitAdd(Git git, String path) throws GitAPIException {
git.add().addFilepattern(path).call();
}
protected void gitResetHard(Git git) throws GitAPIException {
git.reset().setMode(ResetType.HARD).call();
}
protected void gitCheckout(Git git, String revstr)
throws GitAPIException, RevisionSyntaxException, IOException {
git.checkout().setName(db.resolve(revstr).getName()).call();
}
// create a file and add it to the repo
private File createAndAddFile(Git git, String path, String content)
throws Exception {
File f;
int pos = path.lastIndexOf('/');
if (pos < 0) {
f = writeTrashFile(path, content);
} else {
f = writeTrashFile(path.substring(0, pos), path.substring(pos + 1),
content);
}
gitAdd(git, path);
Assert.assertTrue(f.exists());
return f;
}
private void collectRepositoryState() throws Exception {
dc = db.readDirCache();
walk = beginWalk();
if (dotGitattributes != null)
collectEntryContentAndAttributes(F, ".gitattributes", null);
collectEntryContentAndAttributes(F, fileCRLF.getName(), entryCRLF);
collectEntryContentAndAttributes(F, fileLF.getName(), entryLF);
collectEntryContentAndAttributes(F, fileMixed.getName(), entryMixed);
endWalk();
}
private TreeWalk beginWalk() throws Exception {
TreeWalk newWalk = new TreeWalk(db);
newWalk.addTree(new FileTreeIterator(db));
newWalk.addTree(new DirCacheIterator(db.readDirCache()));
return newWalk;
}
private void endWalk() throws IOException {
assertFalse("Not all files tested", walk.next());
}
private void collectEntryContentAndAttributes(FileMode type, String pathName,
ActualEntry e) throws IOException {
assertTrue("walk has entry", walk.next());
assertEquals(pathName, walk.getPathString());
assertEquals(type, walk.getFileMode(0));
if (e != null) {
e.attrs = "";
for (Attribute a : walk.getAttributes().getAll()) {
e.attrs += " " + a.toString();
}
e.attrs = e.attrs.trim();
e.file = new String(
IO.readFully(new File(db.getWorkTree(), pathName)));
DirCacheEntry dce = dc.getEntry(pathName);
ObjectLoader open = walk.getObjectReader().open(dce.getObjectId());
e.index = new String(open.getBytes());
e.indexContentLength = dce.getLength();
}
if (D.equals(type))
walk.enterSubtree();
}
}

335
org.eclipse.jgit.test/tst/org/eclipse/jgit/api/EolStreamTypeUtilTest.java

@ -0,0 +1,335 @@
/*
* Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com>
*
* 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.assertArrayEquals;
import static org.eclipse.jgit.lib.CoreConfig.EolStreamType.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.io.EolStreamTypeUtil;
import org.junit.Test;
/**
* Unit tests for end-of-line conversion streams
*/
public class EolStreamTypeUtilTest {
@Test
public void testCheckoutDirect() throws Exception {
testCheckout(DIRECT, DIRECT, "", "");
testCheckout(DIRECT, DIRECT, "\r", "\r");
testCheckout(DIRECT, DIRECT, "\n", "\n");
testCheckout(DIRECT, DIRECT, "\r\n", "\r\n");
testCheckout(DIRECT, DIRECT, "\n\r", "\n\r");
testCheckout(DIRECT, DIRECT, "\n\r\n", "\n\r\n");
testCheckout(DIRECT, DIRECT, "\r\n\r", "\r\n\r");
testCheckout(DIRECT, DIRECT, "a\nb\n", "a\nb\n");
testCheckout(DIRECT, DIRECT, "a\rb\r", "a\rb\r");
testCheckout(DIRECT, DIRECT, "a\n\rb\n\r", "a\n\rb\n\r");
testCheckout(DIRECT, DIRECT, "a\r\nb\r\n", "a\r\nb\r\n");
}
@Test
public void testCheckoutLF() throws Exception {
testCheckout(TEXT_LF, AUTO_LF, "", "");
testCheckout(TEXT_LF, AUTO_LF, "\r", "\r");
testCheckout(TEXT_LF, AUTO_LF, "\n", "\n");
testCheckout(TEXT_LF, AUTO_LF, "\r\n", "\n");
testCheckout(TEXT_LF, AUTO_LF, "\n\r", "\n\r");
testCheckout(TEXT_LF, AUTO_LF, "\n\r\n", "\n\n");
testCheckout(TEXT_LF, AUTO_LF, "\r\n\r", "\n\r");
testCheckout(TEXT_LF, AUTO_LF, "a\nb\n", "a\nb\n");
testCheckout(TEXT_LF, AUTO_LF, "a\rb\r", "a\rb\r");
testCheckout(TEXT_LF, AUTO_LF, "a\n\rb\n\r", "a\n\rb\n\r");
testCheckout(TEXT_LF, AUTO_LF, "a\r\nb\r\n", "a\nb\n");
}
@Test
public void testCheckoutCRLF() throws Exception {
testCheckout(TEXT_CRLF, AUTO_CRLF, "", "");
testCheckout(TEXT_CRLF, AUTO_CRLF, "\r", "\r");
testCheckout(TEXT_CRLF, AUTO_CRLF, "\n", "\r\n");
testCheckout(TEXT_CRLF, AUTO_CRLF, "\r\n", "\r\n");
testCheckout(TEXT_CRLF, AUTO_CRLF, "\n\r", "\r\n\r");
testCheckout(TEXT_CRLF, AUTO_CRLF, "\n\r\n", "\r\n\r\n");
testCheckout(TEXT_CRLF, AUTO_CRLF, "\r\n\r", "\r\n\r");
testCheckout(TEXT_CRLF, AUTO_CRLF, "a\nb\n", "a\r\nb\r\n");
testCheckout(TEXT_CRLF, AUTO_CRLF, "a\rb\r", "a\rb\r");
testCheckout(TEXT_CRLF, AUTO_CRLF, "a\n\rb\n\r", "a\r\n\rb\r\n\r");
testCheckout(TEXT_CRLF, AUTO_CRLF, "a\r\nb\r\n", "a\r\nb\r\n");
}
/**
* Test stream type detection based on stream content.
* <p>
* Tests three things with the output text:
* <p>
* 1) conversion if output was declared as text
* <p>
* 2) conversion if output was declared as potentially text (AUTO_...) and
* is in fact text
* <p>
* 3) conversion if modified output (now with binary characters) was
* declared as potentially text but now contains binary characters
* <p>
*
* @param streamTypeText
* is the enum meaning that the output is definitely text (no
* binary check at all)
* @param streamTypeWithBinaryCheck
* is the enum meaning that the output may be text (binary check
* is done)
* @param output
* is a text output without binary characters
* @param expectedConversion
* is the expected converted output without binary characters
* @throws Exception
*/
private void testCheckout(EolStreamType streamTypeText,
EolStreamType streamTypeWithBinaryCheck, String output,
String expectedConversion) throws Exception {
ByteArrayOutputStream b;
byte[] outputBytes = output.getBytes(StandardCharsets.UTF_8);
byte[] expectedConversionBytes = expectedConversion
.getBytes(StandardCharsets.UTF_8);
// test using output text and assuming it was declared TEXT
b = new ByteArrayOutputStream();
try (OutputStream out = EolStreamTypeUtil.wrapOutputStream(b,
streamTypeText)) {
out.write(outputBytes);
}
assertArrayEquals(expectedConversionBytes, b.toByteArray());
// test using ouput text and assuming it was declared AUTO, using binary
// detection
b = new ByteArrayOutputStream();
try (OutputStream out = EolStreamTypeUtil.wrapOutputStream(b,
streamTypeWithBinaryCheck)) {
out.write(outputBytes);
}
assertArrayEquals(expectedConversionBytes, b.toByteArray());
// now pollute output text with some binary bytes
outputBytes = extendWithBinaryData(outputBytes);
expectedConversionBytes = extendWithBinaryData(expectedConversionBytes);
// again, test using output text and assuming it was declared TEXT
b = new ByteArrayOutputStream();
try (OutputStream out = EolStreamTypeUtil.wrapOutputStream(b,
streamTypeText)) {
out.write(outputBytes);
}
assertArrayEquals(expectedConversionBytes, b.toByteArray());
// again, test using ouput text and assuming it was declared AUTO, using
// binary
// detection
b = new ByteArrayOutputStream();
try (OutputStream out = EolStreamTypeUtil.wrapOutputStream(b,
streamTypeWithBinaryCheck)) {
out.write(outputBytes);
}
// expect no conversion
assertArrayEquals(outputBytes, b.toByteArray());
}
@Test
public void testCheckinDirect() throws Exception {
testCheckin(DIRECT, DIRECT, "", "");
testCheckin(DIRECT, DIRECT, "\r", "\r");
testCheckin(DIRECT, DIRECT, "\n", "\n");
testCheckin(DIRECT, DIRECT, "\r\n", "\r\n");
testCheckin(DIRECT, DIRECT, "\n\r", "\n\r");
testCheckin(DIRECT, DIRECT, "\n\r\n", "\n\r\n");
testCheckin(DIRECT, DIRECT, "\r\n\r", "\r\n\r");
testCheckin(DIRECT, DIRECT, "a\nb\n", "a\nb\n");
testCheckin(DIRECT, DIRECT, "a\rb\r", "a\rb\r");
testCheckin(DIRECT, DIRECT, "a\n\rb\n\r", "a\n\rb\n\r");
testCheckin(DIRECT, DIRECT, "a\r\nb\r\n", "a\r\nb\r\n");
}
@Test
public void testCheckinLF() throws Exception {
testCheckin(TEXT_LF, AUTO_LF, "", "");
testCheckin(TEXT_LF, AUTO_LF, "\r", "\r");
testCheckin(TEXT_LF, AUTO_LF, "\n", "\n");
testCheckin(TEXT_LF, AUTO_LF, "\r\n", "\n");
testCheckin(TEXT_LF, AUTO_LF, "\n\r", "\n\r");
testCheckin(TEXT_LF, AUTO_LF, "\n\r\n", "\n\n");
testCheckin(TEXT_LF, AUTO_LF, "\r\n\r", "\n\r");
testCheckin(TEXT_LF, AUTO_LF, "a\nb\n", "a\nb\n");
testCheckin(TEXT_LF, AUTO_LF, "a\rb\r", "a\rb\r");
testCheckin(TEXT_LF, AUTO_LF, "a\n\rb\n\r", "a\n\rb\n\r");
testCheckin(TEXT_LF, AUTO_LF, "a\r\nb\r\n", "a\nb\n");
}
@Test
public void testCheckinCRLF() throws Exception {
testCheckin(TEXT_CRLF, AUTO_CRLF, "", "");
testCheckin(TEXT_CRLF, AUTO_CRLF, "\r", "\r");
testCheckin(TEXT_CRLF, AUTO_CRLF, "\n", "\r\n");
testCheckin(TEXT_CRLF, AUTO_CRLF, "\r\n", "\r\n");
testCheckin(TEXT_CRLF, AUTO_CRLF, "\n\r", "\r\n\r");
testCheckin(TEXT_CRLF, AUTO_CRLF, "\n\r\n", "\r\n\r\n");
testCheckin(TEXT_CRLF, AUTO_CRLF, "\r\n\r", "\r\n\r");
testCheckin(TEXT_CRLF, AUTO_CRLF, "a\nb\n", "a\r\nb\r\n");
testCheckin(TEXT_CRLF, AUTO_CRLF, "a\rb\r", "a\rb\r");
testCheckin(TEXT_CRLF, AUTO_CRLF, "a\n\rb\n\r", "a\r\n\rb\r\n\r");
testCheckin(TEXT_CRLF, AUTO_CRLF, "a\r\nb\r\n", "a\r\nb\r\n");
}
/**
* Test stream type detection based on stream content.
* <p>
* Tests three things with the input text:
* <p>
* 1) conversion if input was declared as text
* <p>
* 2) conversion if input was declared as potentially text (AUTO_...) and is
* in fact text
* <p>
* 3) conversion if modified input (now with binary characters) was declared
* as potentially text but now contains binary characters
* <p>
*
* @param streamTypeText
* is the enum meaning that the input is definitely text (no
* binary check at all)
* @param streamTypeWithBinaryCheck
* is the enum meaning that the input may be text (binary check
* is done)
* @param input
* is a text input without binary characters
* @param expectedConversion
* is the expected converted input without binary characters
* @throws Exception
*/
private void testCheckin(EolStreamType streamTypeText,
EolStreamType streamTypeWithBinaryCheck, String input,
String expectedConversion) throws Exception {
byte[] inputBytes = input.getBytes(StandardCharsets.UTF_8);
byte[] expectedConversionBytes = expectedConversion
.getBytes(StandardCharsets.UTF_8);
// test using input text and assuming it was declared TEXT
try (InputStream in = EolStreamTypeUtil.wrapInputStream(
new ByteArrayInputStream(inputBytes),
streamTypeText)) {
byte[] b = new byte[1024];
int len = IO.readFully(in, b, 0);
assertArrayEquals(expectedConversionBytes, Arrays.copyOf(b, len));
}
// test using input text and assuming it was declared AUTO, using binary
// detection
try (InputStream in = EolStreamTypeUtil.wrapInputStream(
new ByteArrayInputStream(inputBytes),
streamTypeWithBinaryCheck)) {
byte[] b = new byte[1024];
int len = IO.readFully(in, b, 0);
assertArrayEquals(expectedConversionBytes, Arrays.copyOf(b, len));
}
// now pollute input text with some binary bytes
inputBytes = extendWithBinaryData(inputBytes);
expectedConversionBytes = extendWithBinaryData(expectedConversionBytes);
// again, test using input text and assuming it was declared TEXT
try (InputStream in = EolStreamTypeUtil.wrapInputStream(
new ByteArrayInputStream(inputBytes), streamTypeText)) {
byte[] b = new byte[1024];
int len = IO.readFully(in, b, 0);
assertArrayEquals(expectedConversionBytes, Arrays.copyOf(b, len));
}
// again, test using input text and assuming it was declared AUTO, using
// binary
// detection
try (InputStream in = EolStreamTypeUtil.wrapInputStream(
new ByteArrayInputStream(inputBytes),
streamTypeWithBinaryCheck)) {
byte[] b = new byte[1024];
int len = IO.readFully(in, b, 0);
// expect no conversion
assertArrayEquals(inputBytes, Arrays.copyOf(b, len));
}
}
private byte[] extendWithBinaryData(byte[] data) throws Exception {
int n = 3;
byte[] dataEx = new byte[data.length + n];
System.arraycopy(data, 0, dataEx, 0, data.length);
for (int i = 0; i < n; i++) {
dataEx[data.length + i] = (byte) i;
}
return dataEx;
}
}

3
org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java

@ -65,6 +65,7 @@ import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.NoFilepatternException; import org.eclipse.jgit.api.errors.NoFilepatternException;
import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheCheckout; import org.eclipse.jgit.dircache.DirCacheCheckout;
import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata;
import org.eclipse.jgit.dircache.DirCacheEditor; import org.eclipse.jgit.dircache.DirCacheEditor;
import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit; import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.dircache.DirCacheEntry;
@ -113,7 +114,7 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
return dco.getRemoved(); return dco.getRemoved();
} }
private Map<String, String> getUpdated() { private Map<String, CheckoutMetadata> getUpdated() {
return dco.getUpdated(); return dco.getUpdated();
} }

7
org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/EolCanonicalizingInputStreamTest.java → org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoLFInputStreamTest.java

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2010, Marc Strapetz <marc.strapetz@syntevo.com> * Copyright (C) 2010, Marc Strapetz <marc.strapetz@syntevo.com>
* Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com>
* and other copyright owners as documented in the project's IP log. * and other copyright owners as documented in the project's IP log.
* *
* This program and the accompanying materials are made available * This program and the accompanying materials are made available
@ -52,7 +53,7 @@ import java.io.UnsupportedEncodingException;
import org.junit.Test; import org.junit.Test;
public class EolCanonicalizingInputStreamTest { public class AutoLFInputStreamTest {
@Test @Test
public void testLF() throws IOException { public void testLF() throws IOException {
@ -97,7 +98,7 @@ public class EolCanonicalizingInputStreamTest {
private static void test(byte[] input, byte[] expected, private static void test(byte[] input, byte[] expected,
boolean detectBinary) throws IOException { boolean detectBinary) throws IOException {
final InputStream bis1 = new ByteArrayInputStream(input); final InputStream bis1 = new ByteArrayInputStream(input);
final InputStream cis1 = new EolCanonicalizingInputStream(bis1, detectBinary); final InputStream cis1 = new AutoLFInputStream(bis1, detectBinary);
int index1 = 0; int index1 = 0;
for (int b = cis1.read(); b != -1; b = cis1.read()) { for (int b = cis1.read(); b != -1; b = cis1.read()) {
assertEquals(expected[index1], (byte) b); assertEquals(expected[index1], (byte) b);
@ -109,7 +110,7 @@ public class EolCanonicalizingInputStreamTest {
for (int bufferSize = 1; bufferSize < 10; bufferSize++) { for (int bufferSize = 1; bufferSize < 10; bufferSize++) {
final byte[] buffer = new byte[bufferSize]; final byte[] buffer = new byte[bufferSize];
final InputStream bis2 = new ByteArrayInputStream(input); final InputStream bis2 = new ByteArrayInputStream(input);
final InputStream cis2 = new EolCanonicalizingInputStream(bis2, detectBinary); final InputStream cis2 = new AutoLFInputStream(bis2, detectBinary);
int read = 0; int read = 0;
for (int readNow = cis2.read(buffer, 0, buffer.length); readNow != -1 for (int readNow = cis2.read(buffer, 0, buffer.length); readNow != -1

21
org.eclipse.jgit/.settings/.api_filters

@ -16,4 +16,25 @@
</message_arguments> </message_arguments>
</filter> </filter>
</resource> </resource>
<resource path="src/org/eclipse/jgit/dircache/DirCacheCheckout.java" type="org.eclipse.jgit.dircache.DirCacheCheckout">
<filter comment="add eol stream type conversion" id="338792546">
<message_arguments>
<message_argument value="org.eclipse.jgit.dircache.DirCacheCheckout"/>
<message_argument value="checkoutEntry(Repository, DirCacheEntry, ObjectReader, boolean)"/>
</message_arguments>
</filter>
<filter comment="add eol stream type conversion" id="338792546">
<message_arguments>
<message_argument value="org.eclipse.jgit.dircache.DirCacheCheckout"/>
<message_argument value="checkoutEntry(Repository, DirCacheEntry, ObjectReader, boolean, String)"/>
</message_arguments>
</filter>
<filter comment="add eol stream type conversion" id="1141899266">
<message_arguments>
<message_argument value="4.2"/>
<message_argument value="4.3"/>
<message_argument value="checkoutEntry(Repository, DirCacheEntry, ObjectReader, boolean, DirCacheCheckout.CheckoutMetadata)"/>
</message_arguments>
</filter>
</resource>
</component> </component>

4
org.eclipse.jgit/src/org/eclipse/jgit/api/BlameCommand.java

@ -66,7 +66,7 @@ import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.CoreConfig.AutoCRLF; import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
import org.eclipse.jgit.treewalk.WorkingTreeOptions; import org.eclipse.jgit.treewalk.WorkingTreeOptions;
import org.eclipse.jgit.util.IO; import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.io.EolCanonicalizingInputStream; import org.eclipse.jgit.util.io.AutoLFInputStream;
/** /**
* Blame command for building a {@link BlameResult} for a file path. * Blame command for building a {@link BlameResult} for a file path.
@ -248,7 +248,7 @@ public class BlameCommand extends GitCommand<BlameResult> {
rawText = new RawText(inTree); rawText = new RawText(inTree);
break; break;
case TRUE: case TRUE:
EolCanonicalizingInputStream in = new EolCanonicalizingInputStream( AutoLFInputStream in = new AutoLFInputStream(
new FileInputStream(inTree), true); new FileInputStream(inTree), true);
// Canonicalization should lead to same or shorter length // Canonicalization should lead to same or shorter length
// (CRLF to LF), so the file size on disk is an upper size bound // (CRLF to LF), so the file size on disk is an upper size bound

22
org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java

@ -59,6 +59,7 @@ import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
import org.eclipse.jgit.api.errors.RefNotFoundException; import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheCheckout; import org.eclipse.jgit.dircache.DirCacheCheckout;
import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata;
import org.eclipse.jgit.dircache.DirCacheEditor; import org.eclipse.jgit.dircache.DirCacheEditor;
import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit; import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.dircache.DirCacheEntry;
@ -68,6 +69,7 @@ import org.eclipse.jgit.errors.UnmergedPathException;
import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.ObjectReader;
@ -395,7 +397,8 @@ public class CheckoutCommand extends GitCommand<Ref> {
RefNotFoundException { RefNotFoundException {
DirCache dc = repo.lockDirCache(); DirCache dc = repo.lockDirCache();
try (RevWalk revWalk = new RevWalk(repo); try (RevWalk revWalk = new RevWalk(repo);
TreeWalk treeWalk = new TreeWalk(revWalk.getObjectReader())) { TreeWalk treeWalk = new TreeWalk(repo,
revWalk.getObjectReader())) {
treeWalk.setRecursive(true); treeWalk.setRecursive(true);
if (!checkoutAllPaths) if (!checkoutAllPaths)
treeWalk.setFilter(PathFilterGroup.createFromStrings(paths)); treeWalk.setFilter(PathFilterGroup.createFromStrings(paths));
@ -426,20 +429,23 @@ public class CheckoutCommand extends GitCommand<Ref> {
if (path.equals(previousPath)) if (path.equals(previousPath))
continue; continue;
final EolStreamType eolStreamType = treeWalk.getEolStreamType();
editor.add(new PathEdit(path) { editor.add(new PathEdit(path) {
public void apply(DirCacheEntry ent) { public void apply(DirCacheEntry ent) {
int stage = ent.getStage(); int stage = ent.getStage();
if (stage > DirCacheEntry.STAGE_0) { if (stage > DirCacheEntry.STAGE_0) {
if (checkoutStage != null) { if (checkoutStage != null) {
if (stage == checkoutStage.number) if (stage == checkoutStage.number)
checkoutPath(ent, r); checkoutPath(ent, r, new CheckoutMetadata(
eolStreamType, null));
} else { } else {
UnmergedPathException e = new UnmergedPathException( UnmergedPathException e = new UnmergedPathException(
ent); ent);
throw new JGitInternalException(e.getMessage(), e); throw new JGitInternalException(e.getMessage(), e);
} }
} else { } else {
checkoutPath(ent, r); checkoutPath(ent, r,
new CheckoutMetadata(eolStreamType, null));
} }
} }
}); });
@ -457,20 +463,24 @@ public class CheckoutCommand extends GitCommand<Ref> {
while (treeWalk.next()) { while (treeWalk.next()) {
final ObjectId blobId = treeWalk.getObjectId(0); final ObjectId blobId = treeWalk.getObjectId(0);
final FileMode mode = treeWalk.getFileMode(0); final FileMode mode = treeWalk.getFileMode(0);
final EolStreamType eolStreamType = treeWalk.getEolStreamType();
editor.add(new PathEdit(treeWalk.getPathString()) { editor.add(new PathEdit(treeWalk.getPathString()) {
public void apply(DirCacheEntry ent) { public void apply(DirCacheEntry ent) {
ent.setObjectId(blobId); ent.setObjectId(blobId);
ent.setFileMode(mode); ent.setFileMode(mode);
checkoutPath(ent, r); checkoutPath(ent, r,
new CheckoutMetadata(eolStreamType, null));
} }
}); });
} }
editor.commit(); editor.commit();
} }
private void checkoutPath(DirCacheEntry entry, ObjectReader reader) { private void checkoutPath(DirCacheEntry entry, ObjectReader reader,
CheckoutMetadata checkoutMetadata) {
try { try {
DirCacheCheckout.checkoutEntry(repo, entry, reader, true); DirCacheCheckout.checkoutEntry(repo, entry, reader, true,
checkoutMetadata);
} catch (IOException e) { } catch (IOException e) {
throw new JGitInternalException(MessageFormat.format( throw new JGitInternalException(MessageFormat.format(
JGitText.get().checkoutConflictWithFile, JGitText.get().checkoutConflictWithFile,

12
org.eclipse.jgit/src/org/eclipse/jgit/api/StashApplyCommand.java

@ -54,11 +54,13 @@ import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder; import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheCheckout; import org.eclipse.jgit.dircache.DirCacheCheckout;
import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata;
import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.errors.CheckoutConflictException; import org.eclipse.jgit.errors.CheckoutConflictException;
import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
@ -336,6 +338,7 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
// Not in commit, don't create untracked // Not in commit, don't create untracked
continue; continue;
final EolStreamType eolStreamType = walk.getEolStreamType();
final DirCacheEntry entry = new DirCacheEntry(walk.getRawPath()); final DirCacheEntry entry = new DirCacheEntry(walk.getRawPath());
entry.setFileMode(cIter.getEntryFileMode()); entry.setFileMode(cIter.getEntryFileMode());
entry.setObjectIdFromRaw(cIter.idBuffer(), cIter.idOffset()); entry.setObjectIdFromRaw(cIter.idBuffer(), cIter.idOffset());
@ -350,14 +353,17 @@ public class StashApplyCommand extends GitCommand<ObjectId> {
} }
} }
checkoutPath(entry, reader); checkoutPath(entry, reader,
new CheckoutMetadata(eolStreamType, null));
} }
} }
} }
private void checkoutPath(DirCacheEntry entry, ObjectReader reader) { private void checkoutPath(DirCacheEntry entry, ObjectReader reader,
CheckoutMetadata checkoutMetadata) {
try { try {
DirCacheCheckout.checkoutEntry(repo, entry, reader, true); DirCacheCheckout.checkoutEntry(repo, entry, reader, true,
checkoutMetadata);
} catch (IOException e) { } catch (IOException e) {
throw new JGitInternalException(MessageFormat.format( throw new JGitInternalException(MessageFormat.format(
JGitText.get().checkoutConflictWithFile, JGitText.get().checkoutConflictWithFile,

4
org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java

@ -245,12 +245,14 @@ public class StashCreateCommand extends GitCommand<RevCommit> {
DirCache cache = repo.lockDirCache(); DirCache cache = repo.lockDirCache();
ObjectId commitId; ObjectId commitId;
try (ObjectInserter inserter = repo.newObjectInserter(); try (ObjectInserter inserter = repo.newObjectInserter();
TreeWalk treeWalk = new TreeWalk(reader)) { TreeWalk treeWalk = new TreeWalk(repo, reader)) {
treeWalk.setRecursive(true); treeWalk.setRecursive(true);
treeWalk.addTree(headCommit.getTree()); treeWalk.addTree(headCommit.getTree());
treeWalk.addTree(new DirCacheIterator(cache)); treeWalk.addTree(new DirCacheIterator(cache));
treeWalk.addTree(new FileTreeIterator(repo)); treeWalk.addTree(new FileTreeIterator(repo));
treeWalk.getTree(2, FileTreeIterator.class)
.setDirCacheIterator(treeWalk, 1);
treeWalk.setFilter(AndTreeFilter.create(new SkipWorkTreeFilter( treeWalk.setFilter(AndTreeFilter.create(new SkipWorkTreeFilter(
1), new IndexDiffFilter(1, 2))); 1), new IndexDiffFilter(1, 2)));

127
org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java vendored

@ -62,6 +62,7 @@ import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.CoreConfig.AutoCRLF; import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
import org.eclipse.jgit.lib.CoreConfig.SymLinks; import org.eclipse.jgit.lib.CoreConfig.SymLinks;
import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.NullProgressMonitor;
@ -84,16 +85,43 @@ import org.eclipse.jgit.util.FS.ExecutionResult;
import org.eclipse.jgit.util.FileUtils; import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.RawParseUtils; import org.eclipse.jgit.util.RawParseUtils;
import org.eclipse.jgit.util.SystemReader; import org.eclipse.jgit.util.SystemReader;
import org.eclipse.jgit.util.io.AutoCRLFOutputStream; import org.eclipse.jgit.util.io.EolStreamTypeUtil;
/** /**
* This class handles checking out one or two trees merging with the index. * This class handles checking out one or two trees merging with the index.
*/ */
public class DirCacheCheckout { public class DirCacheCheckout {
private static final int MAX_EXCEPTION_TEXT_SIZE = 10 * 1024; private static final int MAX_EXCEPTION_TEXT_SIZE = 10 * 1024;
/**
* Metadata used in checkout process
*
* @since 4.3
*/
public static class CheckoutMetadata {
/** git attributes */
public final EolStreamType eolStreamType;
/** filter command to apply */
public final String smudgeFilterCommand;
/**
* @param eolStreamType
* @param smudgeFilterCommand
*/
public CheckoutMetadata(EolStreamType eolStreamType,
String smudgeFilterCommand) {
this.eolStreamType = eolStreamType;
this.smudgeFilterCommand = smudgeFilterCommand;
}
static CheckoutMetadata EMPTY = new CheckoutMetadata(
EolStreamType.DIRECT, null);
}
private Repository repo; private Repository repo;
private HashMap<String, String> updated = new HashMap<String, String>(); private HashMap<String, CheckoutMetadata> updated = new HashMap<String, CheckoutMetadata>();
private ArrayList<String> conflicts = new ArrayList<String>(); private ArrayList<String> conflicts = new ArrayList<String>();
@ -120,7 +148,7 @@ public class DirCacheCheckout {
/** /**
* @return a list of updated paths and smudgeFilterCommands * @return a list of updated paths and smudgeFilterCommands
*/ */
public Map<String, String> getUpdated() { public Map<String, CheckoutMetadata> getUpdated() {
return updated; return updated;
} }
@ -450,11 +478,12 @@ public class DirCacheCheckout {
if (file != null) if (file != null)
removeEmptyParents(file); removeEmptyParents(file);
for (String path : updated.keySet()) { for (Map.Entry<String, CheckoutMetadata> e : updated.entrySet()) {
String path = e.getKey();
CheckoutMetadata meta = e.getValue();
DirCacheEntry entry = dc.getEntry(path); DirCacheEntry entry = dc.getEntry(path);
if (!FileMode.GITLINK.equals(entry.getRawMode())) if (!FileMode.GITLINK.equals(entry.getRawMode()))
checkoutEntry(repo, entry, objectReader, false, checkoutEntry(repo, entry, objectReader, false, meta);
updated.get(path));
} }
// commit the index builder - a new index is persisted // commit the index builder - a new index is persisted
@ -1006,8 +1035,8 @@ public class DirCacheCheckout {
private void update(String path, ObjectId mId, FileMode mode) private void update(String path, ObjectId mId, FileMode mode)
throws IOException { throws IOException {
if (!FileMode.TREE.equals(mode)) { if (!FileMode.TREE.equals(mode)) {
updated.put(path, updated.put(path, new CheckoutMetadata(walk.getEolStreamType(),
walk.getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE)); walk.getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE)));
DirCacheEntry entry = new DirCacheEntry(path, DirCacheEntry.STAGE_0); DirCacheEntry entry = new DirCacheEntry(path, DirCacheEntry.STAGE_0);
entry.setObjectId(mId); entry.setObjectId(mId);
@ -1190,52 +1219,22 @@ public class DirCacheCheckout {
* @param deleteRecursive * @param deleteRecursive
* true to recursively delete final path if it exists on the file * true to recursively delete final path if it exists on the file
* system * system
* * @param checkoutMetadata
* @throws IOException * containing
* @since 4.2 * <ul>
*/ * <li>smudgeFilterCommand to be run for smudging the entry to be
public static void checkoutEntry(Repository repo, DirCacheEntry entry, * checked out</li>
ObjectReader or, boolean deleteRecursive) throws IOException { * <li>eolStreamType used for stream conversion</li>
checkoutEntry(repo, entry, or, deleteRecursive, null); * </ul>
}
/**
* Updates the file in the working tree with content and mode from an entry
* in the index. The new content is first written to a new temporary file in
* the same directory as the real file. Then that new file is renamed to the
* final filename.
*
* <p>
* <b>Note:</b> if the entry path on local file system exists as a file, it
* will be deleted and if it exists as a directory, it will be deleted
* recursively, independently if has any content.
* </p>
*
* <p>
* TODO: this method works directly on File IO, we may need another
* abstraction (like WorkingTreeIterator). This way we could tell e.g.
* Eclipse that Files in the workspace got changed
* </p>
*
* @param repo
* repository managing the destination work tree.
* @param entry
* the entry containing new mode and content
* @param or
* object reader to use for checkout
* @param deleteRecursive
* true to recursively delete final path if it exists on the file
* system
* @param smudgeFilterCommand
* the filter command to be run for smudging the entry to be
* checked out
* *
* @throws IOException * @throws IOException
* @since 4.2 * @since 4.2
*/ */
public static void checkoutEntry(Repository repo, DirCacheEntry entry, public static void checkoutEntry(Repository repo, DirCacheEntry entry,
ObjectReader or, boolean deleteRecursive, ObjectReader or, boolean deleteRecursive,
String smudgeFilterCommand) throws IOException { CheckoutMetadata checkoutMetadata) throws IOException {
if (checkoutMetadata == null)
checkoutMetadata = CheckoutMetadata.EMPTY;
ObjectLoader ol = or.open(entry.getObjectId()); ObjectLoader ol = or.open(entry.getObjectId());
File f = new File(repo.getWorkTree(), entry.getPathString()); File f = new File(repo.getWorkTree(), entry.getPathString());
File parentDir = f.getParentFile(); File parentDir = f.getParentFile();
@ -1257,12 +1256,19 @@ public class DirCacheCheckout {
File tmpFile = File.createTempFile( File tmpFile = File.createTempFile(
"._" + f.getName(), null, parentDir); //$NON-NLS-1$ "._" + f.getName(), null, parentDir); //$NON-NLS-1$
OutputStream channel = new FileOutputStream(tmpFile); EolStreamType nonNullEolStreamType;
if (opt.getAutoCRLF() == AutoCRLF.TRUE) if (checkoutMetadata.eolStreamType != null) {
channel = new AutoCRLFOutputStream(channel); nonNullEolStreamType = checkoutMetadata.eolStreamType;
if (smudgeFilterCommand != null) { } else if (opt.getAutoCRLF() == AutoCRLF.TRUE) {
ProcessBuilder filterProcessBuilder = fs nonNullEolStreamType = EolStreamType.AUTO_CRLF;
.runInShell(smudgeFilterCommand, new String[0]); } else {
nonNullEolStreamType = EolStreamType.DIRECT;
}
OutputStream channel = EolStreamTypeUtil.wrapOutputStream(
new FileOutputStream(tmpFile), nonNullEolStreamType);
if (checkoutMetadata.smudgeFilterCommand != null) {
ProcessBuilder filterProcessBuilder = fs.runInShell(
checkoutMetadata.smudgeFilterCommand, new String[0]);
filterProcessBuilder.directory(repo.getWorkTree()); filterProcessBuilder.directory(repo.getWorkTree());
filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY, filterProcessBuilder.environment().put(Constants.GIT_DIR_KEY,
repo.getDirectory().getAbsolutePath()); repo.getDirectory().getAbsolutePath());
@ -1278,14 +1284,16 @@ public class DirCacheCheckout {
} }
} catch (IOException | InterruptedException e) { } catch (IOException | InterruptedException e) {
throw new IOException(new FilterFailedException(e, throw new IOException(new FilterFailedException(e,
smudgeFilterCommand, entry.getPathString())); checkoutMetadata.smudgeFilterCommand,
entry.getPathString()));
} finally { } finally {
channel.close(); channel.close();
} }
if (rc != 0) { if (rc != 0) {
throw new IOException(new FilterFailedException(rc, throw new IOException(new FilterFailedException(rc,
smudgeFilterCommand, entry.getPathString(), checkoutMetadata.smudgeFilterCommand,
entry.getPathString(),
result.getStdout().toByteArray(MAX_EXCEPTION_TEXT_SIZE), result.getStdout().toByteArray(MAX_EXCEPTION_TEXT_SIZE),
RawParseUtils.decode(result.getStderr() RawParseUtils.decode(result.getStderr()
.toByteArray(MAX_EXCEPTION_TEXT_SIZE)))); .toByteArray(MAX_EXCEPTION_TEXT_SIZE))));
@ -1301,10 +1309,11 @@ public class DirCacheCheckout {
// was filtered (either by autocrlf handling or smudge filters) ask the // was filtered (either by autocrlf handling or smudge filters) ask the
// filesystem again for the length. Otherwise the objectloader knows the // filesystem again for the length. Otherwise the objectloader knows the
// size // size
if (opt.getAutoCRLF() == AutoCRLF.TRUE || smudgeFilterCommand != null) { if (checkoutMetadata.eolStreamType == EolStreamType.DIRECT
entry.setLength(tmpFile.length()); && checkoutMetadata.smudgeFilterCommand == null) {
} else {
entry.setLength(ol.getSize()); entry.setLength(ol.getSize());
} else {
entry.setLength(tmpFile.length());
} }
if (opt.isFileMode() && fs.supportsExecute()) { if (opt.isFileMode() && fs.supportsExecute()) {

7
org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java

@ -107,6 +107,13 @@ public class ConfigConstants {
/** The "autocrlf" key */ /** The "autocrlf" key */
public static final String CONFIG_KEY_AUTOCRLF = "autocrlf"; public static final String CONFIG_KEY_AUTOCRLF = "autocrlf";
/**
* The "eol" key
*
* @since 4.3
*/
public static final String CONFIG_KEY_EOL = "eol";
/** The "bare" key */ /** The "bare" key */
public static final String CONFIG_KEY_BARE = "bare"; public static final String CONFIG_KEY_BARE = "bare";

40
org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java

@ -75,6 +75,46 @@ public class CoreConfig {
INPUT; INPUT;
} }
/**
* Permissible values for {@code core.eol}.
* <p>
* https://git-scm.com/docs/gitattributes
*
* @since 4.3
*/
public static enum EOL {
/** checkin with LF, checkout with CRLF. */
CRLF,
/** checkin with LF, checkout without conversion. */
LF,
/** use the platform's native line ending. */
NATIVE;
}
/**
* EOL stream conversion protocol
*
* @since 4.3
*/
public static enum EolStreamType {
/** convert to CRLF without binary detection */
TEXT_CRLF,
/** convert to LF without binary detection */
TEXT_LF,
/** convert to CRLF with binary detection */
AUTO_CRLF,
/** convert to LF with binary detection */
AUTO_LF,
/** do not convert */
DIRECT;
}
/** /**
* Permissible values for {@code core.checkstat} * Permissible values for {@code core.checkstat}
* *

13
org.eclipse.jgit/src/org/eclipse/jgit/treewalk/NameConflictTreeWalk.java

@ -101,6 +101,19 @@ public class NameConflictTreeWalk extends TreeWalk {
super(repo); super(repo);
} }
/**
* Create a new tree walker for a given repository.
*
* @param repo
* the repository the walker will obtain data from.
* @param or
* the reader the walker will obtain tree data from.
* @since 4.3
*/
public NameConflictTreeWalk(Repository repo, final ObjectReader or) {
super(repo, or);
}
/** /**
* Create a new tree walker for a given repository. * Create a new tree walker for a given repository.
* *

89
org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java

@ -49,6 +49,7 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.attributes.Attribute; import org.eclipse.jgit.attributes.Attribute;
import org.eclipse.jgit.attributes.Attributes; import org.eclipse.jgit.attributes.Attributes;
@ -64,6 +65,7 @@ import org.eclipse.jgit.errors.StopWalkException;
import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.MutableObjectId; import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
@ -74,6 +76,7 @@ import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.eclipse.jgit.treewalk.filter.TreeFilter; import org.eclipse.jgit.treewalk.filter.TreeFilter;
import org.eclipse.jgit.util.QuotedString; import org.eclipse.jgit.util.QuotedString;
import org.eclipse.jgit.util.RawParseUtils; import org.eclipse.jgit.util.RawParseUtils;
import org.eclipse.jgit.util.io.EolStreamTypeUtil;
/** /**
* Walks one or more {@link AbstractTreeIterator}s in parallel. * Walks one or more {@link AbstractTreeIterator}s in parallel.
@ -161,7 +164,44 @@ public class TreeWalk implements AutoCloseable, AttributesProvider {
public static TreeWalk forPath(final ObjectReader reader, final String path, public static TreeWalk forPath(final ObjectReader reader, final String path,
final AnyObjectId... trees) throws MissingObjectException, final AnyObjectId... trees) throws MissingObjectException,
IncorrectObjectTypeException, CorruptObjectException, IOException { IncorrectObjectTypeException, CorruptObjectException, IOException {
TreeWalk tw = new TreeWalk(reader); return forPath(null, reader, path, trees);
}
/**
* Open a tree walk and filter to exactly one path.
* <p>
* The returned tree walk is already positioned on the requested path, so
* the caller should not need to invoke {@link #next()} unless they are
* looking for a possible directory/file name conflict.
*
* @param repo
* repository to read config data and
* {@link AttributesNodeProvider} from.
* @param reader
* the reader the walker will obtain tree data from.
* @param path
* single path to advance the tree walk instance into.
* @param trees
* one or more trees to walk through, all with the same root.
* @return a new tree walk configured for exactly this one path; null if no
* path was found in any of the trees.
* @throws IOException
* reading a pack file or loose object failed.
* @throws CorruptObjectException
* an tree object could not be read as its data stream did not
* appear to be a tree, or could not be inflated.
* @throws IncorrectObjectTypeException
* an object we expected to be a tree was not a tree.
* @throws MissingObjectException
* a tree object was not found.
* @since 4.3
*/
public static TreeWalk forPath(final @Nullable Repository repo,
final ObjectReader reader, final String path,
final AnyObjectId... trees)
throws MissingObjectException, IncorrectObjectTypeException,
CorruptObjectException, IOException {
TreeWalk tw = new TreeWalk(repo, reader);
PathFilter f = PathFilter.create(path); PathFilter f = PathFilter.create(path);
tw.setFilter(f); tw.setFilter(f);
tw.reset(trees); tw.reset(trees);
@ -206,7 +246,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider {
final AnyObjectId... trees) throws MissingObjectException, final AnyObjectId... trees) throws MissingObjectException,
IncorrectObjectTypeException, CorruptObjectException, IOException { IncorrectObjectTypeException, CorruptObjectException, IOException {
try (ObjectReader reader = db.newObjectReader()) { try (ObjectReader reader = db.newObjectReader()) {
return forPath(reader, path, trees); return forPath(db, reader, path, trees);
} }
} }
@ -282,9 +322,23 @@ public class TreeWalk implements AutoCloseable, AttributesProvider {
* when the walker is closed. * when the walker is closed.
*/ */
public TreeWalk(final Repository repo) { public TreeWalk(final Repository repo) {
this(repo.newObjectReader(), true); this(repo, repo.newObjectReader(), true);
config = repo.getConfig(); }
attributesNodeProvider = repo.createAttributesNodeProvider();
/**
* Create a new tree walker for a given repository.
*
* @param repo
* the repository the walker will obtain data from. An
* ObjectReader will be created by the walker, and will be closed
* when the walker is closed.
* @param or
* the reader the walker will obtain tree data from. The reader
* is not closed when the walker is closed.
* @since 4.3
*/
public TreeWalk(final @Nullable Repository repo, final ObjectReader or) {
this(repo, or, false);
} }
/** /**
@ -295,10 +349,18 @@ public class TreeWalk implements AutoCloseable, AttributesProvider {
* is not closed when the walker is closed. * is not closed when the walker is closed.
*/ */
public TreeWalk(final ObjectReader or) { public TreeWalk(final ObjectReader or) {
this(or, false); this(null, or, false);
} }
private TreeWalk(final ObjectReader or, final boolean closeReader) { private TreeWalk(final @Nullable Repository repo, final ObjectReader or,
final boolean closeReader) {
if (repo != null) {
config = repo.getConfig();
attributesNodeProvider = repo.createAttributesNodeProvider();
} else {
config = null;
attributesNodeProvider = null;
}
reader = or; reader = or;
filter = TreeFilter.ALL; filter = TreeFilter.ALL;
trees = NO_TREES; trees = NO_TREES;
@ -517,6 +579,19 @@ public class TreeWalk implements AutoCloseable, AttributesProvider {
} }
} }
/**
* @return the EOL stream type of the current entry using the config and
* {@link #getAttributes()} Note that this method may return null if
* the {@link TreeWalk} is not based on a working tree
* @since 4.3
*/
public @Nullable EolStreamType getEolStreamType() {
if (attributesNodeProvider == null || config == null)
return null;
return EolStreamTypeUtil.detectStreamType(operationType,
config.get(WorkingTreeOptions.KEY), getAttributes());
}
/** Reset this walker so new tree iterators can be added to it. */ /** Reset this walker so new tree iterators can be added to it. */
public void reset() { public void reset() {
attrs = null; attrs = null;

134
org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java

@ -77,8 +77,8 @@ import org.eclipse.jgit.ignore.IgnoreNode;
import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.CoreConfig; import org.eclipse.jgit.lib.CoreConfig;
import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
import org.eclipse.jgit.lib.CoreConfig.CheckStat; import org.eclipse.jgit.lib.CoreConfig.CheckStat;
import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
import org.eclipse.jgit.lib.CoreConfig.SymLinks; import org.eclipse.jgit.lib.CoreConfig.SymLinks;
import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
@ -88,10 +88,12 @@ import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.submodule.SubmoduleWalk; import org.eclipse.jgit.submodule.SubmoduleWalk;
import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FS.ExecutionResult; import org.eclipse.jgit.util.FS.ExecutionResult;
import org.eclipse.jgit.util.Holder;
import org.eclipse.jgit.util.IO; import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.Paths; import org.eclipse.jgit.util.Paths;
import org.eclipse.jgit.util.RawParseUtils; import org.eclipse.jgit.util.RawParseUtils;
import org.eclipse.jgit.util.io.EolCanonicalizingInputStream; import org.eclipse.jgit.util.io.AutoLFInputStream;
import org.eclipse.jgit.util.io.EolStreamTypeUtil;
/** /**
* Walks a working directory tree as part of a {@link TreeWalk}. * Walks a working directory tree as part of a {@link TreeWalk}.
@ -140,7 +142,17 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
/** If there is a .gitignore file present, the parsed rules from it. */ /** If there is a .gitignore file present, the parsed rules from it. */
private IgnoreNode ignoreNode; private IgnoreNode ignoreNode;
private String cleanFilterCommand; /**
* cached clean filter command. Use a Ref in order to distinguish between
* the ref not cached yet and the value null
*/
private Holder<String> cleanFilterCommandHolder;
/**
* cached eol stream type. Use a Ref in order to distinguish between the ref
* not cached yet and the value null
*/
private Holder<EolStreamType> eolStreamTypeHolder;
/** Repository that is the root level being iterated over */ /** Repository that is the root level being iterated over */
protected Repository repository; protected Repository repository;
@ -357,8 +369,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
private InputStream possiblyFilteredInputStream(final Entry e, private InputStream possiblyFilteredInputStream(final Entry e,
final InputStream is, final long len) throws IOException { final InputStream is, final long len) throws IOException {
boolean mightNeedCleaning = mightNeedCleaning(); if (getCleanFilterCommand() == null
if (!mightNeedCleaning) { && getEolStreamType() == EolStreamType.DIRECT) {
canonLen = len; canonLen = len;
return is; return is;
} }
@ -376,8 +388,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
return new ByteArrayInputStream(raw, 0, n); return new ByteArrayInputStream(raw, 0, n);
} }
// TODO: fix autocrlf causing mightneedcleaning if (getCleanFilterCommand() == null && isBinary(e)) {
if (!mightNeedCleaning && isBinary(e)) {
canonLen = len; canonLen = len;
return is; return is;
} }
@ -401,20 +412,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
} }
} }
private boolean mightNeedCleaning() throws IOException {
switch (getOptions().getAutoCRLF()) {
case FALSE:
default:
if (getCleanFilterCommand() != null)
return true;
return false;
case TRUE:
case INPUT:
return true;
}
}
private static boolean isBinary(byte[] content, int sz) { private static boolean isBinary(byte[] content, int sz) {
return RawText.isBinary(content, sz); return RawText.isBinary(content, sz);
} }
@ -467,12 +464,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
return in; return in;
} }
private InputStream handleAutoCRLF(InputStream in) { private InputStream handleAutoCRLF(InputStream in) throws IOException {
AutoCRLF autoCRLF = getOptions().getAutoCRLF(); return EolStreamTypeUtil.wrapInputStream(in, getEolStreamType());
if (autoCRLF == AutoCRLF.TRUE || autoCRLF == AutoCRLF.INPUT) {
in = new EolCanonicalizingInputStream(in, true);
}
return in;
} }
/** /**
@ -531,7 +524,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
System.arraycopy(e.encodedName, 0, path, pathOffset, nameLen); System.arraycopy(e.encodedName, 0, path, pathOffset, nameLen);
pathLen = pathOffset + nameLen; pathLen = pathOffset + nameLen;
canonLen = -1; canonLen = -1;
cleanFilterCommand = null; cleanFilterCommandHolder = null;
eolStreamTypeHolder = null;
} }
/** /**
@ -594,10 +588,11 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
*/ */
public InputStream openEntryStream() throws IOException { public InputStream openEntryStream() throws IOException {
InputStream rawis = current().openInputStream(); InputStream rawis = current().openInputStream();
if (mightNeedCleaning()) if (getCleanFilterCommand() == null
return filterClean(rawis); && getEolStreamType() == EolStreamType.DIRECT)
else
return rawis; return rawis;
else
return filterClean(rawis);
} }
/** /**
@ -971,10 +966,11 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
// Content differs: that's a real change, perhaps // Content differs: that's a real change, perhaps
if (reader == null) // deprecated use, do no further checks if (reader == null) // deprecated use, do no further checks
return true; return true;
switch (getOptions().getAutoCRLF()) {
case INPUT: switch (getEolStreamType()) {
case TRUE: case DIRECT:
InputStream dcIn = null; return true;
default:
try { try {
ObjectLoader loader = reader.open(entry.getObjectId()); ObjectLoader loader = reader.open(entry.getObjectId());
if (loader == null) if (loader == null)
@ -982,37 +978,26 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
// We need to compute the length, but only if it is not // We need to compute the length, but only if it is not
// a binary stream. // a binary stream.
dcIn = new EolCanonicalizingInputStream(
loader.openStream(), true, true /* abort if binary */);
long dcInLen; long dcInLen;
try { try (InputStream dcIn = new AutoLFInputStream(
loader.openStream(), true,
true /* abort if binary */)) {
dcInLen = computeLength(dcIn); dcInLen = computeLength(dcIn);
} catch (EolCanonicalizingInputStream.IsBinaryException e) { } catch (AutoLFInputStream.IsBinaryException e) {
return true; return true;
} finally {
dcIn.close();
} }
dcIn = new EolCanonicalizingInputStream( try (InputStream dcIn = new AutoLFInputStream(
loader.openStream(), true); loader.openStream(), true)) {
byte[] autoCrLfHash = computeHash(dcIn, dcInLen); byte[] autoCrLfHash = computeHash(dcIn, dcInLen);
boolean changed = getEntryObjectId().compareTo( boolean changed = getEntryObjectId()
autoCrLfHash, 0) != 0; .compareTo(autoCrLfHash, 0) != 0;
return changed; return changed;
}
} catch (IOException e) { } catch (IOException e) {
return true; return true;
} finally {
if (dcIn != null)
try {
dcIn.close();
} catch (IOException e) {
// empty
}
} }
case FALSE:
break;
} }
return true;
} }
} }
@ -1308,10 +1293,43 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
* @since 4.2 * @since 4.2
*/ */
public String getCleanFilterCommand() throws IOException { public String getCleanFilterCommand() throws IOException {
if (cleanFilterCommand == null && state.walk != null) { if (cleanFilterCommandHolder == null) {
cleanFilterCommand = state.walk String cmd = null;
if (state.walk != null) {
cmd = state.walk
.getFilterCommand(Constants.ATTR_FILTER_TYPE_CLEAN); .getFilterCommand(Constants.ATTR_FILTER_TYPE_CLEAN);
} }
return cleanFilterCommand; cleanFilterCommandHolder = new Holder<String>(cmd);
}
return cleanFilterCommandHolder.get();
}
/**
* @return the eol stream type for the current entry or <code>null</code> if
* it cannot be determined. When state or state.walk is null or the
* {@link TreeWalk} is not based on a {@link Repository} then null
* is returned.
* @throws IOException
* @since 4.3
*/
public EolStreamType getEolStreamType() throws IOException {
if (eolStreamTypeHolder == null) {
EolStreamType type=null;
if (state.walk != null) {
type=state.walk.getEolStreamType();
} else {
switch (getOptions().getAutoCRLF()) {
case FALSE:
type = EolStreamType.DIRECT;
break;
case TRUE:
case INPUT:
type = EolStreamType.AUTO_LF;
break;
}
}
eolStreamTypeHolder = new Holder<EolStreamType>(type);
}
return eolStreamTypeHolder.get();
} }
} }

14
org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeOptions.java

@ -48,6 +48,7 @@ import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Config.SectionParser; import org.eclipse.jgit.lib.Config.SectionParser;
import org.eclipse.jgit.lib.CoreConfig.AutoCRLF; import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
import org.eclipse.jgit.lib.CoreConfig.CheckStat; import org.eclipse.jgit.lib.CoreConfig.CheckStat;
import org.eclipse.jgit.lib.CoreConfig.EOL;
import org.eclipse.jgit.lib.CoreConfig.HideDotFiles; import org.eclipse.jgit.lib.CoreConfig.HideDotFiles;
import org.eclipse.jgit.lib.CoreConfig.SymLinks; import org.eclipse.jgit.lib.CoreConfig.SymLinks;
@ -64,6 +65,8 @@ public class WorkingTreeOptions {
private final AutoCRLF autoCRLF; private final AutoCRLF autoCRLF;
private final EOL eol;
private final CheckStat checkStat; private final CheckStat checkStat;
private final SymLinks symlinks; private final SymLinks symlinks;
@ -75,6 +78,8 @@ public class WorkingTreeOptions {
ConfigConstants.CONFIG_KEY_FILEMODE, true); ConfigConstants.CONFIG_KEY_FILEMODE, true);
autoCRLF = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null, autoCRLF = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
ConfigConstants.CONFIG_KEY_AUTOCRLF, AutoCRLF.FALSE); ConfigConstants.CONFIG_KEY_AUTOCRLF, AutoCRLF.FALSE);
eol = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
ConfigConstants.CONFIG_KEY_EOL, EOL.NATIVE);
checkStat = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null, checkStat = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
ConfigConstants.CONFIG_KEY_CHECKSTAT, CheckStat.DEFAULT); ConfigConstants.CONFIG_KEY_CHECKSTAT, CheckStat.DEFAULT);
symlinks = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null, symlinks = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
@ -94,6 +99,15 @@ public class WorkingTreeOptions {
return autoCRLF; return autoCRLF;
} }
/**
* @return how text line endings should be normalized.
*
* @since 4.3
*/
public EOL getEOL() {
return eol;
}
/** /**
* @return how stat data is compared * @return how stat data is compared
* @since 3.0 * @since 3.0

77
org.eclipse.jgit/src/org/eclipse/jgit/util/Holder.java

@ -0,0 +1,77 @@
/*
* Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com>
*
* 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.util;
/**
* Holder of an object.
*
* @param <T>
* the type of value held by this {@link Holder}
*
* @since 4.3
*/
public class Holder<T> {
private T value;
/**
* @param value
* is the initial value that is {@link #set(Object)}
*/
public Holder(T value) {
set(value);
}
/**
* @return the value held by this {@link Holder}
*/
public T get() {
return value;
}
/**
* @param value
* to be set as new value held by this {@link Holder}
*/
public void set(T value) {
this.value = value;
}
}

2
org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFInputStream.java

@ -50,7 +50,7 @@ import java.io.InputStream;
import org.eclipse.jgit.diff.RawText; import org.eclipse.jgit.diff.RawText;
/** /**
* An OutputStream that expands LF to CRLF. * An InputStream that expands LF to CRLF.
* *
* Existing CRLF are not expanded to CRCRLF, but retained as is. * Existing CRLF are not expanded to CRCRLF, but retained as is.
* *

21
org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFOutputStream.java

@ -50,8 +50,11 @@ import org.eclipse.jgit.diff.RawText;
/** /**
* An OutputStream that expands LF to CRLF. * An OutputStream that expands LF to CRLF.
* <p> *
* Existing CRLF are not expanded to CRCRLF, but retained as is. * Existing CRLF are not expanded to CRCRLF, but retained as is.
*
* A binary check on the first 8000 bytes is performed and in case of binary
* files, canonicalization is turned off (for the complete file).
*/ */
public class AutoCRLFOutputStream extends OutputStream { public class AutoCRLFOutputStream extends OutputStream {
@ -67,13 +70,26 @@ public class AutoCRLFOutputStream extends OutputStream {
private int binbufcnt = 0; private int binbufcnt = 0;
private boolean detectBinary;
private boolean isBinary; private boolean isBinary;
/** /**
* @param out * @param out
*/ */
public AutoCRLFOutputStream(OutputStream out) { public AutoCRLFOutputStream(OutputStream out) {
this(out, true);
}
/**
* @param out
* @param detectBinary
* whether binaries should be detected
* @since 4.3
*/
public AutoCRLFOutputStream(OutputStream out, boolean detectBinary) {
this.out = out; this.out = out;
this.detectBinary = detectBinary;
} }
@Override @Override
@ -141,7 +157,10 @@ public class AutoCRLFOutputStream extends OutputStream {
} }
private void decideMode() throws IOException { private void decideMode() throws IOException {
if (detectBinary) {
isBinary = RawText.isBinary(binbuf, binbufcnt); isBinary = RawText.isBinary(binbuf, binbufcnt);
detectBinary = false;
}
int cachedLen = binbufcnt; int cachedLen = binbufcnt;
binbufcnt = binbuf.length + 1; // full! binbufcnt = binbuf.length + 1; // full!
write(binbuf, 0, cachedLen); write(binbuf, 0, cachedLen);

199
org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFInputStream.java

@ -0,0 +1,199 @@
/*
* Copyright (C) 2010, 2013 Marc Strapetz <marc.strapetz@syntevo.com>
* 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
* 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.util.io;
import java.io.IOException;
import java.io.InputStream;
import org.eclipse.jgit.diff.RawText;
/**
* An InputStream that normalizes CRLF to LF.
*
* Existing single CR are not changed to LF, but retained as is.
*
* Optionally, a binary check on the first 8000 bytes is performed and in case
* of binary files, canonicalization is turned off (for the complete file).
* <p>
* This is the former EolCanonicalizingInputStream with a new name in order to
* have same naming for all LF / CRLF streams
*
* @since 4.3
*/
public class AutoLFInputStream extends InputStream {
private final byte[] single = new byte[1];
private final byte[] buf = new byte[8096];
private final InputStream in;
private int cnt;
private int ptr;
private boolean isBinary;
private boolean detectBinary;
private boolean abortIfBinary;
/**
* A special exception thrown when {@link AutoLFInputStream} is told to
* throw an exception when attempting to read a binary file. The exception
* may be thrown at any stage during reading.
*
* @since 3.3
*/
public static class IsBinaryException extends IOException {
private static final long serialVersionUID = 1L;
IsBinaryException() {
super();
}
}
/**
* Creates a new InputStream, wrapping the specified stream
*
* @param in
* raw input stream
* @param detectBinary
* whether binaries should be detected
* @since 2.0
*/
public AutoLFInputStream(InputStream in, boolean detectBinary) {
this(in, detectBinary, false);
}
/**
* Creates a new InputStream, wrapping the specified stream
*
* @param in
* raw input stream
* @param detectBinary
* whether binaries should be detected
* @param abortIfBinary
* throw an IOException if the file is binary
* @since 3.3
*/
public AutoLFInputStream(InputStream in, boolean detectBinary,
boolean abortIfBinary) {
this.in = in;
this.detectBinary = detectBinary;
this.abortIfBinary = abortIfBinary;
}
@Override
public int read() throws IOException {
final int read = read(single, 0, 1);
return read == 1 ? single[0] & 0xff : -1;
}
@Override
public int read(byte[] bs, final int off, final int len)
throws IOException {
if (len == 0)
return 0;
if (cnt == -1)
return -1;
int i = off;
final int end = off + len;
while (i < end) {
if (ptr == cnt && !fillBuffer()) {
break;
}
byte b = buf[ptr++];
if (isBinary || b != '\r') {
// Logic for binary files ends here
bs[i++] = b;
continue;
}
if (ptr == cnt && !fillBuffer()) {
bs[i++] = '\r';
break;
}
if (buf[ptr] == '\n') {
bs[i++] = '\n';
ptr++;
} else
bs[i++] = '\r';
}
return i == off ? -1 : i - off;
}
/**
* @return true if the stream has detected as a binary so far
* @since 3.3
*/
public boolean isBinary() {
return isBinary;
}
@Override
public void close() throws IOException {
in.close();
}
private boolean fillBuffer() throws IOException {
cnt = in.read(buf, 0, buf.length);
if (cnt < 1)
return false;
if (detectBinary) {
isBinary = RawText.isBinary(buf, cnt);
detectBinary = false;
if (isBinary && abortIfBinary)
throw new IsBinaryException();
}
ptr = 0;
return true;
}
}

200
org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoLFOutputStream.java

@ -0,0 +1,200 @@
/*
* Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com>
*
* 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.util.io;
import java.io.IOException;
import java.io.OutputStream;
import org.eclipse.jgit.diff.RawText;
/**
* An OutputStream that reduces CRLF to LF.
*
* Existing single CR are not changed to LF, but retained as is.
*
* A binary check on the first 8000 bytes is performed and in case of binary
* files, canonicalization is turned off (for the complete file).
*
* @since 4.3
*/
public class AutoLFOutputStream extends OutputStream {
static final int BUFFER_SIZE = 8000;
private final OutputStream out;
private int buf = -1;
private byte[] binbuf = new byte[BUFFER_SIZE];
private byte[] onebytebuf = new byte[1];
private int binbufcnt = 0;
private boolean detectBinary;
private boolean isBinary;
/**
* @param out
*/
public AutoLFOutputStream(OutputStream out) {
this(out, true);
}
/**
* @param out
* @param detectBinary
* whether binaries should be detected
*/
public AutoLFOutputStream(OutputStream out, boolean detectBinary) {
this.out = out;
this.detectBinary = detectBinary;
}
@Override
public void write(int b) throws IOException {
onebytebuf[0] = (byte) b;
write(onebytebuf, 0, 1);
}
@Override
public void write(byte[] b) throws IOException {
int overflow = buffer(b, 0, b.length);
if (overflow > 0) {
write(b, b.length - overflow, overflow);
}
}
@Override
public void write(byte[] b, final int startOff, final int startLen)
throws IOException {
final int overflow = buffer(b, startOff, startLen);
if (overflow < 0) {
return;
}
final int off = startOff + startLen - overflow;
final int len = overflow;
if (len == 0) {
return;
}
int lastw = off;
if (isBinary) {
out.write(b, off, len);
return;
}
for (int i = off; i < off + len; ++i) {
final byte c = b[i];
if (c == '\r') {
// skip write r but backlog r
if (lastw < i) {
out.write(b, lastw, i - lastw);
}
lastw = i + 1;
buf = '\r';
} else if (c == '\n') {
if (buf == '\r') {
out.write('\n');
lastw = i + 1;
buf = -1;
} else {
if (lastw < i + 1) {
out.write(b, lastw, i + 1 - lastw);
}
lastw = i + 1;
}
} else {
if (buf == '\r') {
out.write('\r');
lastw = i;
}
buf = -1;
}
}
if (lastw < off + len) {
out.write(b, lastw, off + len - lastw);
}
}
private int buffer(byte[] b, int off, int len) throws IOException {
if (binbufcnt > binbuf.length) {
return len;
}
int copy = Math.min(binbuf.length - binbufcnt, len);
System.arraycopy(b, off, binbuf, binbufcnt, copy);
binbufcnt += copy;
int remaining = len - copy;
if (remaining > 0) {
decideMode();
}
return remaining;
}
private void decideMode() throws IOException {
if (detectBinary) {
isBinary = RawText.isBinary(binbuf, binbufcnt);
detectBinary = false;
}
int cachedLen = binbufcnt;
binbufcnt = binbuf.length + 1; // full!
write(binbuf, 0, cachedLen);
}
@Override
public void flush() throws IOException {
if (binbufcnt <= binbuf.length) {
decideMode();
}
out.flush();
}
@Override
public void close() throws IOException {
flush();
if (buf == '\r') {
out.write(buf);
buf = -1;
}
out.close();
}
}

121
org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolCanonicalizingInputStream.java

@ -46,46 +46,16 @@ package org.eclipse.jgit.util.io;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import org.eclipse.jgit.diff.RawText;
/** /**
* An input stream which canonicalizes EOLs bytes on the fly to '\n'. * An input stream which canonicalizes EOLs bytes on the fly to '\n'.
* *
* Optionally, a binary check on the first 8000 bytes is performed * Optionally, a binary check on the first 8000 bytes is performed and in case
* and in case of binary files, canonicalization is turned off * of binary files, canonicalization is turned off (for the complete file).
* (for the complete file).
*/
public class EolCanonicalizingInputStream extends InputStream {
private final byte[] single = new byte[1];
private final byte[] buf = new byte[8096];
private final InputStream in;
private int cnt;
private int ptr;
private boolean isBinary;
private boolean detectBinary;
private boolean abortIfBinary;
/**
* A special exception thrown when {@link EolCanonicalizingInputStream} is
* told to throw an exception when attempting to read a binary file. The
* exception may be thrown at any stage during reading.
* *
* @since 3.3 * @deprecated use {@link AutoLFInputStream} instead
*/ */
public static class IsBinaryException extends IOException { @Deprecated
private static final long serialVersionUID = 1L; public class EolCanonicalizingInputStream extends AutoLFInputStream {
IsBinaryException() {
super();
}
}
/** /**
* Creates a new InputStream, wrapping the specified stream * Creates a new InputStream, wrapping the specified stream
@ -94,10 +64,9 @@ public class EolCanonicalizingInputStream extends InputStream {
* raw input stream * raw input stream
* @param detectBinary * @param detectBinary
* whether binaries should be detected * whether binaries should be detected
* @since 2.0
*/ */
public EolCanonicalizingInputStream(InputStream in, boolean detectBinary) { public EolCanonicalizingInputStream(InputStream in, boolean detectBinary) {
this(in, detectBinary, false); super(in, detectBinary);
} }
/** /**
@ -109,83 +78,25 @@ public class EolCanonicalizingInputStream extends InputStream {
* whether binaries should be detected * whether binaries should be detected
* @param abortIfBinary * @param abortIfBinary
* throw an IOException if the file is binary * throw an IOException if the file is binary
* @since 3.3
*/ */
public EolCanonicalizingInputStream(InputStream in, boolean detectBinary, public EolCanonicalizingInputStream(InputStream in, boolean detectBinary,
boolean abortIfBinary) { boolean abortIfBinary) {
this.in = in; super(in, detectBinary, abortIfBinary);
this.detectBinary = detectBinary;
this.abortIfBinary = abortIfBinary;
}
@Override
public int read() throws IOException {
final int read = read(single, 0, 1);
return read == 1 ? single[0] & 0xff : -1;
}
@Override
public int read(byte[] bs, final int off, final int len) throws IOException {
if (len == 0)
return 0;
if (cnt == -1)
return -1;
int i = off;
final int end = off + len;
while (i < end) {
if (ptr == cnt && !fillBuffer()) {
break;
}
byte b = buf[ptr++];
if (isBinary || b != '\r') {
// Logic for binary files ends here
bs[i++] = b;
continue;
}
if (ptr == cnt && !fillBuffer()) {
bs[i++] = '\r';
break;
}
if (buf[ptr] == '\n') {
bs[i++] = '\n';
ptr++;
} else
bs[i++] = '\r';
}
return i == off ? -1 : i - off;
} }
/** /**
* @return true if the stream has detected as a binary so far * A special exception thrown when {@link AutoLFInputStream} is told to
* throw an exception when attempting to read a binary file. The exception
* may be thrown at any stage during reading.
*
* @since 3.3 * @since 3.3
*/ */
public boolean isBinary() { public static class IsBinaryException extends IOException {
return isBinary; private static final long serialVersionUID = 1L;
}
@Override
public void close() throws IOException {
in.close();
}
private boolean fillBuffer() throws IOException { IsBinaryException() {
cnt = in.read(buf, 0, buf.length); super();
if (cnt < 1)
return false;
if (detectBinary) {
isBinary = RawText.isBinary(buf, cnt);
detectBinary = false;
if (isBinary && abortIfBinary)
throw new IsBinaryException();
} }
ptr = 0;
return true;
} }
} }

255
org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolStreamTypeUtil.java

@ -0,0 +1,255 @@
/*
* Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com>
*
* 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.util.io;
import java.io.InputStream;
import java.io.OutputStream;
import org.eclipse.jgit.attributes.Attributes;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
import org.eclipse.jgit.treewalk.WorkingTreeOptions;
/**
* Utility used to create input and output stream wrappers for
* {@link EolStreamType}
*
* @since 4.3
*/
public final class EolStreamTypeUtil {
private static final boolean FORCE_EOL_LF_ON_CHECKOUT = false;
private EolStreamTypeUtil() {
}
/**
* Convenience method used to detect if CRLF conversion has been configured
* using the
* <ul>
* <li>global repo options</li>
* <li>global attributes</li>
* <li>info attributes</li>
* <li>working tree .gitattributes</li>
*
* @param op
* is the {@link OperationType} of the current traversal
* @param options
* are the {@link Config} options with key
* {@link WorkingTreeOptions#KEY}
* @param attrs
* are the {@link Attributes} of the file for which the
* {@link EolStreamType} is to be detected
*
* @return the stream conversion {@link EolStreamType} to be performed for
* the selected {@link OperationType}
*/
public static EolStreamType detectStreamType(OperationType op,
WorkingTreeOptions options, Attributes attrs) {
switch (op) {
case CHECKIN_OP:
return checkInStreamType(options, attrs);
case CHECKOUT_OP:
return checkOutStreamType(options, attrs);
default:
throw new IllegalArgumentException("unknown OperationType " + op); //$NON-NLS-1$
}
}
/**
* @param in
* original stream
* @param conversion
* to be performed
* @return the converted stream depending on {@link EolStreamType}
*/
public static InputStream wrapInputStream(InputStream in,
EolStreamType conversion) {
switch (conversion) {
case TEXT_CRLF:
return new AutoCRLFInputStream(in, false);
case TEXT_LF:
return new AutoLFInputStream(in, false);
case AUTO_CRLF:
return new AutoCRLFInputStream(in, true);
case AUTO_LF:
return new AutoLFInputStream(in, true);
default:
return in;
}
}
/**
* @param out
* original stream
* @param conversion
* to be performed
* @return the converted stream depending on {@link EolStreamType}
*/
public static OutputStream wrapOutputStream(OutputStream out,
EolStreamType conversion) {
switch (conversion) {
case TEXT_CRLF:
return new AutoCRLFOutputStream(out, false);
case AUTO_CRLF:
return new AutoCRLFOutputStream(out, true);
case TEXT_LF:
return new AutoLFOutputStream(out, false);
case AUTO_LF:
return new AutoLFOutputStream(out, true);
default:
return out;
}
}
private static EolStreamType checkInStreamType(WorkingTreeOptions options,
Attributes attrs) {
// old git system
if (attrs.isSet("crlf")) {//$NON-NLS-1$
return EolStreamType.TEXT_LF;
} else if (attrs.isUnset("crlf")) {//$NON-NLS-1$
return EolStreamType.DIRECT;
} else if ("input".equals(attrs.getValue("crlf"))) {//$NON-NLS-1$ //$NON-NLS-2$
return EolStreamType.TEXT_LF;
}
// new git system
if (attrs.isUnset("text")) {//$NON-NLS-1$
return EolStreamType.DIRECT;
}
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;
}
switch (options.getAutoCRLF()) {
case TRUE:
case INPUT:
return EolStreamType.AUTO_LF;
case FALSE:
return EolStreamType.DIRECT;
}
return EolStreamType.DIRECT;
}
private static EolStreamType checkOutStreamType(WorkingTreeOptions options,
Attributes attrs) {
// old git system
if (attrs.isSet("crlf")) {//$NON-NLS-1$
return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF
: EolStreamType.DIRECT;
} else if (attrs.isUnset("crlf")) {//$NON-NLS-1$
return EolStreamType.DIRECT;
} else if ("input".equals(attrs.getValue("crlf"))) {//$NON-NLS-1$ //$NON-NLS-2$
return EolStreamType.DIRECT;
}
// new git system
if (attrs.isUnset("text")) {//$NON-NLS-1$
return EolStreamType.DIRECT;
}
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:
return EolStreamType.TEXT_CRLF;
case LF:
return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF
: EolStreamType.DIRECT;
case NATIVE:
default:
return EolStreamType.DIRECT;
}
}
if ("auto".equals(attrs.getValue("text"))) { //$NON-NLS-1$ //$NON-NLS-2$
switch (options.getAutoCRLF()) {
case TRUE:
return EolStreamType.AUTO_CRLF;
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;
}
}
switch (options.getAutoCRLF()) {
case TRUE:
return EolStreamType.AUTO_CRLF;
default:
// no decision
}
return EolStreamType.DIRECT;
}
}
Loading…
Cancel
Save