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>tags/v4.3.0.201603230630-rc1
@@ -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(); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -65,6 +65,7 @@ import org.eclipse.jgit.api.errors.GitAPIException; | |||
import org.eclipse.jgit.api.errors.NoFilepatternException; | |||
import org.eclipse.jgit.dircache.DirCache; | |||
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.PathEdit; | |||
import org.eclipse.jgit.dircache.DirCacheEntry; | |||
@@ -113,7 +114,7 @@ public class DirCacheCheckoutTest extends RepositoryTestCase { | |||
return dco.getRemoved(); | |||
} | |||
private Map<String, String> getUpdated() { | |||
private Map<String, CheckoutMetadata> getUpdated() { | |||
return dco.getUpdated(); | |||
} | |||
@@ -1,5 +1,6 @@ | |||
/* | |||
* 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. | |||
* | |||
* This program and the accompanying materials are made available | |||
@@ -52,7 +53,7 @@ import java.io.UnsupportedEncodingException; | |||
import org.junit.Test; | |||
public class EolCanonicalizingInputStreamTest { | |||
public class AutoLFInputStreamTest { | |||
@Test | |||
public void testLF() throws IOException { | |||
@@ -97,7 +98,7 @@ public class EolCanonicalizingInputStreamTest { | |||
private static void test(byte[] input, byte[] expected, | |||
boolean detectBinary) throws IOException { | |||
final InputStream bis1 = new ByteArrayInputStream(input); | |||
final InputStream cis1 = new EolCanonicalizingInputStream(bis1, detectBinary); | |||
final InputStream cis1 = new AutoLFInputStream(bis1, detectBinary); | |||
int index1 = 0; | |||
for (int b = cis1.read(); b != -1; b = cis1.read()) { | |||
assertEquals(expected[index1], (byte) b); | |||
@@ -109,7 +110,7 @@ public class EolCanonicalizingInputStreamTest { | |||
for (int bufferSize = 1; bufferSize < 10; bufferSize++) { | |||
final byte[] buffer = new byte[bufferSize]; | |||
final InputStream bis2 = new ByteArrayInputStream(input); | |||
final InputStream cis2 = new EolCanonicalizingInputStream(bis2, detectBinary); | |||
final InputStream cis2 = new AutoLFInputStream(bis2, detectBinary); | |||
int read = 0; | |||
for (int readNow = cis2.read(buffer, 0, buffer.length); readNow != -1 |
@@ -16,4 +16,25 @@ | |||
</message_arguments> | |||
</filter> | |||
</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> |
@@ -66,7 +66,7 @@ import org.eclipse.jgit.lib.Repository; | |||
import org.eclipse.jgit.lib.CoreConfig.AutoCRLF; | |||
import org.eclipse.jgit.treewalk.WorkingTreeOptions; | |||
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. | |||
@@ -248,7 +248,7 @@ public class BlameCommand extends GitCommand<BlameResult> { | |||
rawText = new RawText(inTree); | |||
break; | |||
case TRUE: | |||
EolCanonicalizingInputStream in = new EolCanonicalizingInputStream( | |||
AutoLFInputStream in = new AutoLFInputStream( | |||
new FileInputStream(inTree), true); | |||
// Canonicalization should lead to same or shorter length | |||
// (CRLF to LF), so the file size on disk is an upper size bound |
@@ -59,6 +59,7 @@ import org.eclipse.jgit.api.errors.RefAlreadyExistsException; | |||
import org.eclipse.jgit.api.errors.RefNotFoundException; | |||
import org.eclipse.jgit.dircache.DirCache; | |||
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.PathEdit; | |||
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.lib.AnyObjectId; | |||
import org.eclipse.jgit.lib.Constants; | |||
import org.eclipse.jgit.lib.CoreConfig.EolStreamType; | |||
import org.eclipse.jgit.lib.FileMode; | |||
import org.eclipse.jgit.lib.ObjectId; | |||
import org.eclipse.jgit.lib.ObjectReader; | |||
@@ -395,7 +397,8 @@ public class CheckoutCommand extends GitCommand<Ref> { | |||
RefNotFoundException { | |||
DirCache dc = repo.lockDirCache(); | |||
try (RevWalk revWalk = new RevWalk(repo); | |||
TreeWalk treeWalk = new TreeWalk(revWalk.getObjectReader())) { | |||
TreeWalk treeWalk = new TreeWalk(repo, | |||
revWalk.getObjectReader())) { | |||
treeWalk.setRecursive(true); | |||
if (!checkoutAllPaths) | |||
treeWalk.setFilter(PathFilterGroup.createFromStrings(paths)); | |||
@@ -426,20 +429,23 @@ public class CheckoutCommand extends GitCommand<Ref> { | |||
if (path.equals(previousPath)) | |||
continue; | |||
final EolStreamType eolStreamType = treeWalk.getEolStreamType(); | |||
editor.add(new PathEdit(path) { | |||
public void apply(DirCacheEntry ent) { | |||
int stage = ent.getStage(); | |||
if (stage > DirCacheEntry.STAGE_0) { | |||
if (checkoutStage != null) { | |||
if (stage == checkoutStage.number) | |||
checkoutPath(ent, r); | |||
checkoutPath(ent, r, new CheckoutMetadata( | |||
eolStreamType, null)); | |||
} else { | |||
UnmergedPathException e = new UnmergedPathException( | |||
ent); | |||
throw new JGitInternalException(e.getMessage(), e); | |||
} | |||
} else { | |||
checkoutPath(ent, r); | |||
checkoutPath(ent, r, | |||
new CheckoutMetadata(eolStreamType, null)); | |||
} | |||
} | |||
}); | |||
@@ -457,20 +463,24 @@ public class CheckoutCommand extends GitCommand<Ref> { | |||
while (treeWalk.next()) { | |||
final ObjectId blobId = treeWalk.getObjectId(0); | |||
final FileMode mode = treeWalk.getFileMode(0); | |||
final EolStreamType eolStreamType = treeWalk.getEolStreamType(); | |||
editor.add(new PathEdit(treeWalk.getPathString()) { | |||
public void apply(DirCacheEntry ent) { | |||
ent.setObjectId(blobId); | |||
ent.setFileMode(mode); | |||
checkoutPath(ent, r); | |||
checkoutPath(ent, r, | |||
new CheckoutMetadata(eolStreamType, null)); | |||
} | |||
}); | |||
} | |||
editor.commit(); | |||
} | |||
private void checkoutPath(DirCacheEntry entry, ObjectReader reader) { | |||
private void checkoutPath(DirCacheEntry entry, ObjectReader reader, | |||
CheckoutMetadata checkoutMetadata) { | |||
try { | |||
DirCacheCheckout.checkoutEntry(repo, entry, reader, true); | |||
DirCacheCheckout.checkoutEntry(repo, entry, reader, true, | |||
checkoutMetadata); | |||
} catch (IOException e) { | |||
throw new JGitInternalException(MessageFormat.format( | |||
JGitText.get().checkoutConflictWithFile, |
@@ -54,11 +54,13 @@ import org.eclipse.jgit.api.errors.WrongRepositoryStateException; | |||
import org.eclipse.jgit.dircache.DirCache; | |||
import org.eclipse.jgit.dircache.DirCacheBuilder; | |||
import org.eclipse.jgit.dircache.DirCacheCheckout; | |||
import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata; | |||
import org.eclipse.jgit.dircache.DirCacheEntry; | |||
import org.eclipse.jgit.dircache.DirCacheIterator; | |||
import org.eclipse.jgit.errors.CheckoutConflictException; | |||
import org.eclipse.jgit.internal.JGitText; | |||
import org.eclipse.jgit.lib.Constants; | |||
import org.eclipse.jgit.lib.CoreConfig.EolStreamType; | |||
import org.eclipse.jgit.lib.ObjectId; | |||
import org.eclipse.jgit.lib.ObjectReader; | |||
import org.eclipse.jgit.lib.Repository; | |||
@@ -336,6 +338,7 @@ public class StashApplyCommand extends GitCommand<ObjectId> { | |||
// Not in commit, don't create untracked | |||
continue; | |||
final EolStreamType eolStreamType = walk.getEolStreamType(); | |||
final DirCacheEntry entry = new DirCacheEntry(walk.getRawPath()); | |||
entry.setFileMode(cIter.getEntryFileMode()); | |||
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 { | |||
DirCacheCheckout.checkoutEntry(repo, entry, reader, true); | |||
DirCacheCheckout.checkoutEntry(repo, entry, reader, true, | |||
checkoutMetadata); | |||
} catch (IOException e) { | |||
throw new JGitInternalException(MessageFormat.format( | |||
JGitText.get().checkoutConflictWithFile, |
@@ -245,12 +245,14 @@ public class StashCreateCommand extends GitCommand<RevCommit> { | |||
DirCache cache = repo.lockDirCache(); | |||
ObjectId commitId; | |||
try (ObjectInserter inserter = repo.newObjectInserter(); | |||
TreeWalk treeWalk = new TreeWalk(reader)) { | |||
TreeWalk treeWalk = new TreeWalk(repo, reader)) { | |||
treeWalk.setRecursive(true); | |||
treeWalk.addTree(headCommit.getTree()); | |||
treeWalk.addTree(new DirCacheIterator(cache)); | |||
treeWalk.addTree(new FileTreeIterator(repo)); | |||
treeWalk.getTree(2, FileTreeIterator.class) | |||
.setDirCacheIterator(treeWalk, 1); | |||
treeWalk.setFilter(AndTreeFilter.create(new SkipWorkTreeFilter( | |||
1), new IndexDiffFilter(1, 2))); | |||
@@ -62,6 +62,7 @@ import org.eclipse.jgit.errors.MissingObjectException; | |||
import org.eclipse.jgit.internal.JGitText; | |||
import org.eclipse.jgit.lib.Constants; | |||
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.FileMode; | |||
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.RawParseUtils; | |||
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. | |||
*/ | |||
public class DirCacheCheckout { | |||
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 HashMap<String, String> updated = new HashMap<String, String>(); | |||
private HashMap<String, CheckoutMetadata> updated = new HashMap<String, CheckoutMetadata>(); | |||
private ArrayList<String> conflicts = new ArrayList<String>(); | |||
@@ -120,7 +148,7 @@ public class DirCacheCheckout { | |||
/** | |||
* @return a list of updated paths and smudgeFilterCommands | |||
*/ | |||
public Map<String, String> getUpdated() { | |||
public Map<String, CheckoutMetadata> getUpdated() { | |||
return updated; | |||
} | |||
@@ -450,11 +478,12 @@ public class DirCacheCheckout { | |||
if (file != null) | |||
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); | |||
if (!FileMode.GITLINK.equals(entry.getRawMode())) | |||
checkoutEntry(repo, entry, objectReader, false, | |||
updated.get(path)); | |||
checkoutEntry(repo, entry, objectReader, false, meta); | |||
} | |||
// 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) | |||
throws IOException { | |||
if (!FileMode.TREE.equals(mode)) { | |||
updated.put(path, | |||
walk.getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE)); | |||
updated.put(path, new CheckoutMetadata(walk.getEolStreamType(), | |||
walk.getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE))); | |||
DirCacheEntry entry = new DirCacheEntry(path, DirCacheEntry.STAGE_0); | |||
entry.setObjectId(mId); | |||
@@ -1190,52 +1219,22 @@ public class DirCacheCheckout { | |||
* @param deleteRecursive | |||
* true to recursively delete final path if it exists on the file | |||
* system | |||
* | |||
* @throws IOException | |||
* @since 4.2 | |||
*/ | |||
public static void checkoutEntry(Repository repo, DirCacheEntry entry, | |||
ObjectReader or, boolean deleteRecursive) throws IOException { | |||
checkoutEntry(repo, entry, or, deleteRecursive, null); | |||
} | |||
/** | |||
* 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 | |||
* @param checkoutMetadata | |||
* containing | |||
* <ul> | |||
* <li>smudgeFilterCommand to be run for smudging the entry to be | |||
* checked out</li> | |||
* <li>eolStreamType used for stream conversion</li> | |||
* </ul> | |||
* | |||
* @throws IOException | |||
* @since 4.2 | |||
*/ | |||
public static void checkoutEntry(Repository repo, DirCacheEntry entry, | |||
ObjectReader or, boolean deleteRecursive, | |||
String smudgeFilterCommand) throws IOException { | |||
CheckoutMetadata checkoutMetadata) throws IOException { | |||
if (checkoutMetadata == null) | |||
checkoutMetadata = CheckoutMetadata.EMPTY; | |||
ObjectLoader ol = or.open(entry.getObjectId()); | |||
File f = new File(repo.getWorkTree(), entry.getPathString()); | |||
File parentDir = f.getParentFile(); | |||
@@ -1257,12 +1256,19 @@ public class DirCacheCheckout { | |||
File tmpFile = File.createTempFile( | |||
"._" + f.getName(), null, parentDir); //$NON-NLS-1$ | |||
OutputStream channel = new FileOutputStream(tmpFile); | |||
if (opt.getAutoCRLF() == AutoCRLF.TRUE) | |||
channel = new AutoCRLFOutputStream(channel); | |||
if (smudgeFilterCommand != null) { | |||
ProcessBuilder filterProcessBuilder = fs | |||
.runInShell(smudgeFilterCommand, new String[0]); | |||
EolStreamType nonNullEolStreamType; | |||
if (checkoutMetadata.eolStreamType != null) { | |||
nonNullEolStreamType = checkoutMetadata.eolStreamType; | |||
} else if (opt.getAutoCRLF() == AutoCRLF.TRUE) { | |||
nonNullEolStreamType = EolStreamType.AUTO_CRLF; | |||
} 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.environment().put(Constants.GIT_DIR_KEY, | |||
repo.getDirectory().getAbsolutePath()); | |||
@@ -1278,14 +1284,16 @@ public class DirCacheCheckout { | |||
} | |||
} catch (IOException | InterruptedException e) { | |||
throw new IOException(new FilterFailedException(e, | |||
smudgeFilterCommand, entry.getPathString())); | |||
checkoutMetadata.smudgeFilterCommand, | |||
entry.getPathString())); | |||
} finally { | |||
channel.close(); | |||
} | |||
if (rc != 0) { | |||
throw new IOException(new FilterFailedException(rc, | |||
smudgeFilterCommand, entry.getPathString(), | |||
checkoutMetadata.smudgeFilterCommand, | |||
entry.getPathString(), | |||
result.getStdout().toByteArray(MAX_EXCEPTION_TEXT_SIZE), | |||
RawParseUtils.decode(result.getStderr() | |||
.toByteArray(MAX_EXCEPTION_TEXT_SIZE)))); | |||
@@ -1301,10 +1309,11 @@ public class DirCacheCheckout { | |||
// was filtered (either by autocrlf handling or smudge filters) ask the | |||
// filesystem again for the length. Otherwise the objectloader knows the | |||
// size | |||
if (opt.getAutoCRLF() == AutoCRLF.TRUE || smudgeFilterCommand != null) { | |||
entry.setLength(tmpFile.length()); | |||
} else { | |||
if (checkoutMetadata.eolStreamType == EolStreamType.DIRECT | |||
&& checkoutMetadata.smudgeFilterCommand == null) { | |||
entry.setLength(ol.getSize()); | |||
} else { | |||
entry.setLength(tmpFile.length()); | |||
} | |||
if (opt.isFileMode() && fs.supportsExecute()) { |
@@ -107,6 +107,13 @@ public class ConfigConstants { | |||
/** The "autocrlf" key */ | |||
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 */ | |||
public static final String CONFIG_KEY_BARE = "bare"; | |||
@@ -75,6 +75,46 @@ public class CoreConfig { | |||
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} | |||
* |
@@ -101,6 +101,19 @@ public class NameConflictTreeWalk extends TreeWalk { | |||
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. | |||
* |
@@ -49,6 +49,7 @@ import java.util.HashMap; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import org.eclipse.jgit.annotations.Nullable; | |||
import org.eclipse.jgit.api.errors.JGitInternalException; | |||
import org.eclipse.jgit.attributes.Attribute; | |||
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.Config; | |||
import org.eclipse.jgit.lib.Constants; | |||
import org.eclipse.jgit.lib.CoreConfig.EolStreamType; | |||
import org.eclipse.jgit.lib.FileMode; | |||
import org.eclipse.jgit.lib.MutableObjectId; | |||
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.util.QuotedString; | |||
import org.eclipse.jgit.util.RawParseUtils; | |||
import org.eclipse.jgit.util.io.EolStreamTypeUtil; | |||
/** | |||
* 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, | |||
final AnyObjectId... trees) throws MissingObjectException, | |||
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); | |||
tw.setFilter(f); | |||
tw.reset(trees); | |||
@@ -206,7 +246,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { | |||
final AnyObjectId... trees) throws MissingObjectException, | |||
IncorrectObjectTypeException, CorruptObjectException, IOException { | |||
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. | |||
*/ | |||
public TreeWalk(final Repository repo) { | |||
this(repo.newObjectReader(), true); | |||
config = repo.getConfig(); | |||
attributesNodeProvider = repo.createAttributesNodeProvider(); | |||
this(repo, repo.newObjectReader(), true); | |||
} | |||
/** | |||
* 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. | |||
*/ | |||
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; | |||
filter = TreeFilter.ALL; | |||
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. */ | |||
public void reset() { | |||
attrs = null; |
@@ -77,8 +77,8 @@ import org.eclipse.jgit.ignore.IgnoreNode; | |||
import org.eclipse.jgit.internal.JGitText; | |||
import org.eclipse.jgit.lib.Constants; | |||
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.EolStreamType; | |||
import org.eclipse.jgit.lib.CoreConfig.SymLinks; | |||
import org.eclipse.jgit.lib.FileMode; | |||
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.util.FS; | |||
import org.eclipse.jgit.util.FS.ExecutionResult; | |||
import org.eclipse.jgit.util.Holder; | |||
import org.eclipse.jgit.util.IO; | |||
import org.eclipse.jgit.util.Paths; | |||
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}. | |||
@@ -140,7 +142,17 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { | |||
/** If there is a .gitignore file present, the parsed rules from it. */ | |||
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 */ | |||
protected Repository repository; | |||
@@ -357,8 +369,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { | |||
private InputStream possiblyFilteredInputStream(final Entry e, | |||
final InputStream is, final long len) throws IOException { | |||
boolean mightNeedCleaning = mightNeedCleaning(); | |||
if (!mightNeedCleaning) { | |||
if (getCleanFilterCommand() == null | |||
&& getEolStreamType() == EolStreamType.DIRECT) { | |||
canonLen = len; | |||
return is; | |||
} | |||
@@ -376,11 +388,10 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { | |||
return new ByteArrayInputStream(raw, 0, n); | |||
} | |||
// TODO: fix autocrlf causing mightneedcleaning | |||
if (!mightNeedCleaning && isBinary(e)) { | |||
canonLen = len; | |||
return is; | |||
} | |||
if (getCleanFilterCommand() == null && isBinary(e)) { | |||
canonLen = len; | |||
return is; | |||
} | |||
final InputStream lenIs = filterClean(e.openInputStream()); | |||
try { | |||
@@ -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) { | |||
return RawText.isBinary(content, sz); | |||
} | |||
@@ -467,12 +464,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { | |||
return in; | |||
} | |||
private InputStream handleAutoCRLF(InputStream in) { | |||
AutoCRLF autoCRLF = getOptions().getAutoCRLF(); | |||
if (autoCRLF == AutoCRLF.TRUE || autoCRLF == AutoCRLF.INPUT) { | |||
in = new EolCanonicalizingInputStream(in, true); | |||
} | |||
return in; | |||
private InputStream handleAutoCRLF(InputStream in) throws IOException { | |||
return EolStreamTypeUtil.wrapInputStream(in, getEolStreamType()); | |||
} | |||
/** | |||
@@ -531,7 +524,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { | |||
System.arraycopy(e.encodedName, 0, path, pathOffset, nameLen); | |||
pathLen = pathOffset + nameLen; | |||
canonLen = -1; | |||
cleanFilterCommand = null; | |||
cleanFilterCommandHolder = null; | |||
eolStreamTypeHolder = null; | |||
} | |||
/** | |||
@@ -594,10 +588,11 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { | |||
*/ | |||
public InputStream openEntryStream() throws IOException { | |||
InputStream rawis = current().openInputStream(); | |||
if (mightNeedCleaning()) | |||
return filterClean(rawis); | |||
else | |||
if (getCleanFilterCommand() == null | |||
&& getEolStreamType() == EolStreamType.DIRECT) | |||
return rawis; | |||
else | |||
return filterClean(rawis); | |||
} | |||
/** | |||
@@ -971,10 +966,11 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { | |||
// Content differs: that's a real change, perhaps | |||
if (reader == null) // deprecated use, do no further checks | |||
return true; | |||
switch (getOptions().getAutoCRLF()) { | |||
case INPUT: | |||
case TRUE: | |||
InputStream dcIn = null; | |||
switch (getEolStreamType()) { | |||
case DIRECT: | |||
return true; | |||
default: | |||
try { | |||
ObjectLoader loader = reader.open(entry.getObjectId()); | |||
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 | |||
// a binary stream. | |||
dcIn = new EolCanonicalizingInputStream( | |||
loader.openStream(), true, true /* abort if binary */); | |||
long dcInLen; | |||
try { | |||
try (InputStream dcIn = new AutoLFInputStream( | |||
loader.openStream(), true, | |||
true /* abort if binary */)) { | |||
dcInLen = computeLength(dcIn); | |||
} catch (EolCanonicalizingInputStream.IsBinaryException e) { | |||
} catch (AutoLFInputStream.IsBinaryException e) { | |||
return true; | |||
} finally { | |||
dcIn.close(); | |||
} | |||
dcIn = new EolCanonicalizingInputStream( | |||
loader.openStream(), true); | |||
byte[] autoCrLfHash = computeHash(dcIn, dcInLen); | |||
boolean changed = getEntryObjectId().compareTo( | |||
autoCrLfHash, 0) != 0; | |||
return changed; | |||
try (InputStream dcIn = new AutoLFInputStream( | |||
loader.openStream(), true)) { | |||
byte[] autoCrLfHash = computeHash(dcIn, dcInLen); | |||
boolean changed = getEntryObjectId() | |||
.compareTo(autoCrLfHash, 0) != 0; | |||
return changed; | |||
} | |||
} catch (IOException e) { | |||
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 | |||
*/ | |||
public String getCleanFilterCommand() throws IOException { | |||
if (cleanFilterCommand == null && state.walk != null) { | |||
cleanFilterCommand = state.walk | |||
.getFilterCommand(Constants.ATTR_FILTER_TYPE_CLEAN); | |||
if (cleanFilterCommandHolder == null) { | |||
String cmd = null; | |||
if (state.walk != null) { | |||
cmd = state.walk | |||
.getFilterCommand(Constants.ATTR_FILTER_TYPE_CLEAN); | |||
} | |||
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 cleanFilterCommand; | |||
return eolStreamTypeHolder.get(); | |||
} | |||
} |
@@ -48,6 +48,7 @@ import org.eclipse.jgit.lib.ConfigConstants; | |||
import org.eclipse.jgit.lib.Config.SectionParser; | |||
import org.eclipse.jgit.lib.CoreConfig.AutoCRLF; | |||
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.SymLinks; | |||
@@ -64,6 +65,8 @@ public class WorkingTreeOptions { | |||
private final AutoCRLF autoCRLF; | |||
private final EOL eol; | |||
private final CheckStat checkStat; | |||
private final SymLinks symlinks; | |||
@@ -75,6 +78,8 @@ public class WorkingTreeOptions { | |||
ConfigConstants.CONFIG_KEY_FILEMODE, true); | |||
autoCRLF = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null, | |||
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, | |||
ConfigConstants.CONFIG_KEY_CHECKSTAT, CheckStat.DEFAULT); | |||
symlinks = rc.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null, | |||
@@ -94,6 +99,15 @@ public class WorkingTreeOptions { | |||
return autoCRLF; | |||
} | |||
/** | |||
* @return how text line endings should be normalized. | |||
* | |||
* @since 4.3 | |||
*/ | |||
public EOL getEOL() { | |||
return eol; | |||
} | |||
/** | |||
* @return how stat data is compared | |||
* @since 3.0 |
@@ -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; | |||
} | |||
} |
@@ -50,7 +50,7 @@ import java.io.InputStream; | |||
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. | |||
* |
@@ -50,8 +50,11 @@ import org.eclipse.jgit.diff.RawText; | |||
/** | |||
* An OutputStream that expands LF to CRLF. | |||
* <p> | |||
* | |||
* 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 { | |||
@@ -67,13 +70,26 @@ public class AutoCRLFOutputStream extends OutputStream { | |||
private int binbufcnt = 0; | |||
private boolean detectBinary; | |||
private boolean isBinary; | |||
/** | |||
* @param 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.detectBinary = detectBinary; | |||
} | |||
@Override | |||
@@ -141,7 +157,10 @@ public class AutoCRLFOutputStream extends OutputStream { | |||
} | |||
private void decideMode() throws IOException { | |||
isBinary = RawText.isBinary(binbuf, binbufcnt); | |||
if (detectBinary) { | |||
isBinary = RawText.isBinary(binbuf, binbufcnt); | |||
detectBinary = false; | |||
} | |||
int cachedLen = binbufcnt; | |||
binbufcnt = binbuf.length + 1; // full! | |||
write(binbuf, 0, cachedLen); |
@@ -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; | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -46,46 +46,16 @@ package org.eclipse.jgit.util.io; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import org.eclipse.jgit.diff.RawText; | |||
/** | |||
* An input stream which canonicalizes EOLs bytes on the fly to '\n'. | |||
* | |||
* 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). | |||
* 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). | |||
* | |||
* @deprecated use {@link AutoLFInputStream} instead | |||
*/ | |||
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 | |||
*/ | |||
public static class IsBinaryException extends IOException { | |||
private static final long serialVersionUID = 1L; | |||
IsBinaryException() { | |||
super(); | |||
} | |||
} | |||
@Deprecated | |||
public class EolCanonicalizingInputStream extends AutoLFInputStream { | |||
/** | |||
* Creates a new InputStream, wrapping the specified stream | |||
@@ -94,10 +64,9 @@ public class EolCanonicalizingInputStream extends InputStream { | |||
* raw input stream | |||
* @param detectBinary | |||
* whether binaries should be detected | |||
* @since 2.0 | |||
*/ | |||
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 | |||
* @param abortIfBinary | |||
* throw an IOException if the file is binary | |||
* @since 3.3 | |||
*/ | |||
public EolCanonicalizingInputStream(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; | |||
super(in, detectBinary, abortIfBinary); | |||
} | |||
/** | |||
* @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 | |||
*/ | |||
public boolean isBinary() { | |||
return isBinary; | |||
} | |||
@Override | |||
public void close() throws IOException { | |||
in.close(); | |||
} | |||
public static class IsBinaryException extends IOException { | |||
private static final long serialVersionUID = 1L; | |||
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(); | |||
IsBinaryException() { | |||
super(); | |||
} | |||
ptr = 0; | |||
return true; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |