Browse Source
TreeWalk provides the new method getEolStreamType. This new method can be used with EolStreamTypeUtil in order to create a wrapped InputStream or OutputStream when reading / writing files. The implementation implements support for the git configuration options core.crlf, core.eol and the .gitattributes "text", "eol" and "binary" CQ: 10896 Bug: 486563 Change-Id: Ie4f6367afc2a6aec1de56faf95120fff0339a358 Signed-off-by: Ivan Motsch <ivan.motsch@bsiag.com> Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>stable-4.3
Ivan Motsch
9 years ago
committed by
Christian Halstrick
23 changed files with 2160 additions and 255 deletions
@ -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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
} |
@ -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(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,255 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com> |
||||||
|
* |
||||||
|
* This program and the accompanying materials are made available |
||||||
|
* under the terms of the Eclipse Distribution License v1.0 which |
||||||
|
* accompanies this distribution, is reproduced below, and is |
||||||
|
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||||
|
* |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or |
||||||
|
* without modification, are permitted provided that the following |
||||||
|
* conditions are met: |
||||||
|
* |
||||||
|
* - Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* - Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following |
||||||
|
* disclaimer in the documentation and/or other materials provided |
||||||
|
* with the distribution. |
||||||
|
* |
||||||
|
* - Neither the name of the Eclipse Foundation, Inc. nor the |
||||||
|
* names of its contributors may be used to endorse or promote |
||||||
|
* products derived from this software without specific prior |
||||||
|
* written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
||||||
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
||||||
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
|
||||||
|
package org.eclipse.jgit.util.io; |
||||||
|
|
||||||
|
import java.io.InputStream; |
||||||
|
import java.io.OutputStream; |
||||||
|
|
||||||
|
import org.eclipse.jgit.attributes.Attributes; |
||||||
|
import org.eclipse.jgit.lib.Config; |
||||||
|
import org.eclipse.jgit.lib.CoreConfig.EolStreamType; |
||||||
|
import org.eclipse.jgit.treewalk.TreeWalk.OperationType; |
||||||
|
import org.eclipse.jgit.treewalk.WorkingTreeOptions; |
||||||
|
|
||||||
|
/** |
||||||
|
* Utility used to create input and output stream wrappers for |
||||||
|
* {@link EolStreamType} |
||||||
|
* |
||||||
|
* @since 4.3 |
||||||
|
*/ |
||||||
|
public final class EolStreamTypeUtil { |
||||||
|
private static final boolean FORCE_EOL_LF_ON_CHECKOUT = false; |
||||||
|
|
||||||
|
private EolStreamTypeUtil() { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Convenience method used to detect if CRLF conversion has been configured |
||||||
|
* using the |
||||||
|
* <ul> |
||||||
|
* <li>global repo options</li> |
||||||
|
* <li>global attributes</li> |
||||||
|
* <li>info attributes</li> |
||||||
|
* <li>working tree .gitattributes</li> |
||||||
|
* |
||||||
|
* @param op |
||||||
|
* is the {@link OperationType} of the current traversal |
||||||
|
* @param options |
||||||
|
* are the {@link Config} options with key |
||||||
|
* {@link WorkingTreeOptions#KEY} |
||||||
|
* @param attrs |
||||||
|
* are the {@link Attributes} of the file for which the |
||||||
|
* {@link EolStreamType} is to be detected |
||||||
|
* |
||||||
|
* @return the stream conversion {@link EolStreamType} to be performed for |
||||||
|
* the selected {@link OperationType} |
||||||
|
*/ |
||||||
|
public static EolStreamType detectStreamType(OperationType op, |
||||||
|
WorkingTreeOptions options, Attributes attrs) { |
||||||
|
switch (op) { |
||||||
|
case CHECKIN_OP: |
||||||
|
return checkInStreamType(options, attrs); |
||||||
|
case CHECKOUT_OP: |
||||||
|
return checkOutStreamType(options, attrs); |
||||||
|
default: |
||||||
|
throw new IllegalArgumentException("unknown OperationType " + op); //$NON-NLS-1$
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param in |
||||||
|
* original stream |
||||||
|
* @param conversion |
||||||
|
* to be performed |
||||||
|
* @return the converted stream depending on {@link EolStreamType} |
||||||
|
*/ |
||||||
|
public static InputStream wrapInputStream(InputStream in, |
||||||
|
EolStreamType conversion) { |
||||||
|
switch (conversion) { |
||||||
|
case TEXT_CRLF: |
||||||
|
return new AutoCRLFInputStream(in, false); |
||||||
|
case TEXT_LF: |
||||||
|
return new AutoLFInputStream(in, false); |
||||||
|
case AUTO_CRLF: |
||||||
|
return new AutoCRLFInputStream(in, true); |
||||||
|
case AUTO_LF: |
||||||
|
return new AutoLFInputStream(in, true); |
||||||
|
default: |
||||||
|
return in; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param out |
||||||
|
* original stream |
||||||
|
* @param conversion |
||||||
|
* to be performed |
||||||
|
* @return the converted stream depending on {@link EolStreamType} |
||||||
|
*/ |
||||||
|
public static OutputStream wrapOutputStream(OutputStream out, |
||||||
|
EolStreamType conversion) { |
||||||
|
switch (conversion) { |
||||||
|
case TEXT_CRLF: |
||||||
|
return new AutoCRLFOutputStream(out, false); |
||||||
|
case AUTO_CRLF: |
||||||
|
return new AutoCRLFOutputStream(out, true); |
||||||
|
case TEXT_LF: |
||||||
|
return new AutoLFOutputStream(out, false); |
||||||
|
case AUTO_LF: |
||||||
|
return new AutoLFOutputStream(out, true); |
||||||
|
default: |
||||||
|
return out; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static EolStreamType checkInStreamType(WorkingTreeOptions options, |
||||||
|
Attributes attrs) { |
||||||
|
// old git system
|
||||||
|
if (attrs.isSet("crlf")) {//$NON-NLS-1$
|
||||||
|
return EolStreamType.TEXT_LF; |
||||||
|
} else if (attrs.isUnset("crlf")) {//$NON-NLS-1$
|
||||||
|
return EolStreamType.DIRECT; |
||||||
|
} else if ("input".equals(attrs.getValue("crlf"))) {//$NON-NLS-1$ //$NON-NLS-2$
|
||||||
|
return EolStreamType.TEXT_LF; |
||||||
|
} |
||||||
|
|
||||||
|
// new git system
|
||||||
|
if (attrs.isUnset("text")) {//$NON-NLS-1$
|
||||||
|
return EolStreamType.DIRECT; |
||||||
|
} |
||||||
|
String eol = attrs.getValue("eol"); //$NON-NLS-1$
|
||||||
|
if (eol != null) |
||||||
|
// check-in is always normalized to LF
|
||||||
|
return EolStreamType.TEXT_LF; |
||||||
|
|
||||||
|
if (attrs.isSet("text")) { //$NON-NLS-1$
|
||||||
|
return EolStreamType.TEXT_LF; |
||||||
|
} |
||||||
|
|
||||||
|
if ("auto".equals(attrs.getValue("text"))) { //$NON-NLS-1$ //$NON-NLS-2$
|
||||||
|
return EolStreamType.AUTO_LF; |
||||||
|
} |
||||||
|
|
||||||
|
switch (options.getAutoCRLF()) { |
||||||
|
case TRUE: |
||||||
|
case INPUT: |
||||||
|
return EolStreamType.AUTO_LF; |
||||||
|
case FALSE: |
||||||
|
return EolStreamType.DIRECT; |
||||||
|
} |
||||||
|
|
||||||
|
return EolStreamType.DIRECT; |
||||||
|
} |
||||||
|
|
||||||
|
private static EolStreamType checkOutStreamType(WorkingTreeOptions options, |
||||||
|
Attributes attrs) { |
||||||
|
// old git system
|
||||||
|
if (attrs.isSet("crlf")) {//$NON-NLS-1$
|
||||||
|
return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF |
||||||
|
: EolStreamType.DIRECT; |
||||||
|
} else if (attrs.isUnset("crlf")) {//$NON-NLS-1$
|
||||||
|
return EolStreamType.DIRECT; |
||||||
|
} else if ("input".equals(attrs.getValue("crlf"))) {//$NON-NLS-1$ //$NON-NLS-2$
|
||||||
|
return EolStreamType.DIRECT; |
||||||
|
} |
||||||
|
|
||||||
|
// new git system
|
||||||
|
if (attrs.isUnset("text")) {//$NON-NLS-1$
|
||||||
|
return EolStreamType.DIRECT; |
||||||
|
} |
||||||
|
String eol = attrs.getValue("eol"); //$NON-NLS-1$
|
||||||
|
if (eol != null && "crlf".equals(eol)) //$NON-NLS-1$
|
||||||
|
return EolStreamType.TEXT_CRLF; |
||||||
|
if (eol != null && "lf".equals(eol)) //$NON-NLS-1$
|
||||||
|
return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF |
||||||
|
: EolStreamType.DIRECT; |
||||||
|
|
||||||
|
if (attrs.isSet("text")) { //$NON-NLS-1$
|
||||||
|
switch (options.getAutoCRLF()) { |
||||||
|
case TRUE: |
||||||
|
return EolStreamType.TEXT_CRLF; |
||||||
|
default: |
||||||
|
// no decision
|
||||||
|
} |
||||||
|
switch (options.getEOL()) { |
||||||
|
case CRLF: |
||||||
|
return EolStreamType.TEXT_CRLF; |
||||||
|
case LF: |
||||||
|
return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF |
||||||
|
: EolStreamType.DIRECT; |
||||||
|
case NATIVE: |
||||||
|
default: |
||||||
|
return EolStreamType.DIRECT; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if ("auto".equals(attrs.getValue("text"))) { //$NON-NLS-1$ //$NON-NLS-2$
|
||||||
|
switch (options.getAutoCRLF()) { |
||||||
|
case TRUE: |
||||||
|
return EolStreamType.AUTO_CRLF; |
||||||
|
default: |
||||||
|
// no decision
|
||||||
|
} |
||||||
|
switch (options.getEOL()) { |
||||||
|
case CRLF: |
||||||
|
return EolStreamType.AUTO_CRLF; |
||||||
|
case LF: |
||||||
|
return FORCE_EOL_LF_ON_CHECKOUT ? EolStreamType.TEXT_LF |
||||||
|
: EolStreamType.DIRECT; |
||||||
|
case NATIVE: |
||||||
|
default: |
||||||
|
return EolStreamType.DIRECT; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
switch (options.getAutoCRLF()) { |
||||||
|
case TRUE: |
||||||
|
return EolStreamType.AUTO_CRLF; |
||||||
|
default: |
||||||
|
// no decision
|
||||||
|
} |
||||||
|
|
||||||
|
return EolStreamType.DIRECT; |
||||||
|
} |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue