summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java48
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PathCheckoutCommandTest.java33
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java41
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFOutputStreamTest.java123
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/EolCanonicalizingInputStreamTest.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java17
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java11
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java33
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFOutputStream.java187
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolCanonicalizingInputStream.java16
11 files changed, 506 insertions, 13 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
index 632732db8f..632288ec64 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/AddCommandTest.java
@@ -111,6 +111,54 @@ public class AddCommandTest extends RepositoryTestCase {
}
@Test
+ public void testAddExistingSingleFileWithNewLine() throws IOException,
+ NoFilepatternException {
+ File file = new File(db.getWorkTree(), "a.txt");
+ FileUtils.createNewFile(file);
+ PrintWriter writer = new PrintWriter(file);
+ writer.print("row1\r\nrow2");
+ writer.close();
+
+ Git git = new Git(db);
+ db.getConfig().setString("core", null, "autocrlf", "false");
+ git.add().addFilepattern("a.txt").call();
+ assertEquals("[a.txt, mode:100644, content:row1\r\nrow2]",
+ indexState(CONTENT));
+ db.getConfig().setString("core", null, "autocrlf", "true");
+ git.add().addFilepattern("a.txt").call();
+ assertEquals("[a.txt, mode:100644, content:row1\nrow2]",
+ indexState(CONTENT));
+ db.getConfig().setString("core", null, "autocrlf", "input");
+ git.add().addFilepattern("a.txt").call();
+ assertEquals("[a.txt, mode:100644, content:row1\nrow2]",
+ indexState(CONTENT));
+ }
+
+ @Test
+ public void testAddExistingSingleBinaryFile() throws IOException,
+ NoFilepatternException {
+ File file = new File(db.getWorkTree(), "a.txt");
+ FileUtils.createNewFile(file);
+ PrintWriter writer = new PrintWriter(file);
+ writer.print("row1\r\nrow2\u0000");
+ writer.close();
+
+ Git git = new Git(db);
+ db.getConfig().setString("core", null, "autocrlf", "false");
+ git.add().addFilepattern("a.txt").call();
+ assertEquals("[a.txt, mode:100644, content:row1\r\nrow2\u0000]",
+ indexState(CONTENT));
+ db.getConfig().setString("core", null, "autocrlf", "true");
+ git.add().addFilepattern("a.txt").call();
+ assertEquals("[a.txt, mode:100644, content:row1\r\nrow2\u0000]",
+ indexState(CONTENT));
+ db.getConfig().setString("core", null, "autocrlf", "input");
+ git.add().addFilepattern("a.txt").call();
+ assertEquals("[a.txt, mode:100644, content:row1\r\nrow2\u0000]",
+ indexState(CONTENT));
+ }
+
+ @Test
public void testAddExistingSingleFileInSubDir() throws IOException, NoFilepatternException {
FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
File file = new File(db.getWorkTree(), "sub/a.txt");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PathCheckoutCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PathCheckoutCommandTest.java
index 81b6908dc7..20a1acbd99 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PathCheckoutCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PathCheckoutCommandTest.java
@@ -50,8 +50,10 @@ import java.io.IOException;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.errors.NoWorkTreeException;
+import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.RepositoryTestCase;
+import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Before;
import org.junit.Test;
@@ -200,4 +202,35 @@ public class PathCheckoutCommandTest extends RepositoryTestCase {
r.release();
}
}
+
+ public void testCheckoutMixedNewlines() throws Exception {
+ // "git config core.autocrlf true"
+ StoredConfig config = git.getRepository().getConfig();
+ config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_AUTOCRLF, true);
+ config.save();
+ // edit <FILE1>
+ File written = writeTrashFile(FILE1, "4\r\n4");
+ assertEquals("4\r\n4", read(written));
+ // "git add <FILE1>"
+ git.add().addFilepattern(FILE1).call();
+ // "git commit -m 'CRLF'"
+ git.commit().setMessage("CRLF").call();
+ // edit <FILE1>
+ written = writeTrashFile(FILE1, "4\n4");
+ assertEquals("4\n4", read(written));
+ // "git add <FILE1>"
+ git.add().addFilepattern(FILE1).call();
+ // "git checkout -- <FILE1>
+ git.checkout().addPath(FILE1).call();
+ // "git status" => clean
+ Status status = git.status().call();
+ assertEquals(0, status.getAdded().size());
+ assertEquals(0, status.getChanged().size());
+ assertEquals(0, status.getConflicting().size());
+ assertEquals(0, status.getMissing().size());
+ assertEquals(0, status.getModified().size());
+ assertEquals(0, status.getRemoved().size());
+ assertEquals(0, status.getUntracked().size());
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
index f06b37c842..fafd745b5a 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/DirCacheCheckoutTest.java
@@ -40,6 +40,7 @@
*/
package org.eclipse.jgit.lib;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -879,6 +880,41 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
}
@Test
+ public void testCheckoutOutChangesAutoCRLFfalse() throws IOException {
+ setupCase(mk("foo"), mkmap("foo/bar", "foo\nbar"), mk("foo"));
+ checkout();
+ assertIndex(mkmap("foo/bar", "foo\nbar"));
+ assertWorkDir(mkmap("foo/bar", "foo\nbar"));
+ }
+
+ @Test
+ public void testCheckoutOutChangesAutoCRLFInput() throws IOException {
+ setupCase(mk("foo"), mkmap("foo/bar", "foo\nbar"), mk("foo"));
+ db.getConfig().setString("core", null, "autocrlf", "input");
+ checkout();
+ assertIndex(mkmap("foo/bar", "foo\nbar"));
+ assertWorkDir(mkmap("foo/bar", "foo\nbar"));
+ }
+
+ @Test
+ public void testCheckoutOutChangesAutoCRLFtrue() throws IOException {
+ setupCase(mk("foo"), mkmap("foo/bar", "foo\nbar"), mk("foo"));
+ db.getConfig().setString("core", null, "autocrlf", "true");
+ checkout();
+ assertIndex(mkmap("foo/bar", "foo\nbar"));
+ assertWorkDir(mkmap("foo/bar", "foo\r\nbar"));
+ }
+
+ @Test
+ public void testCheckoutOutChangesAutoCRLFtrueBinary() throws IOException {
+ setupCase(mk("foo"), mkmap("foo/bar", "foo\nb\u0000ar"), mk("foo"));
+ db.getConfig().setString("core", null, "autocrlf", "true");
+ checkout();
+ assertIndex(mkmap("foo/bar", "foo\nb\u0000ar"));
+ assertWorkDir(mkmap("foo/bar", "foo\nb\u0000ar"));
+ }
+
+ @Test
public void testCheckoutUncachedChanges() throws IOException {
setupCase(mk("foo"), mk("foo"), mk("foo"));
writeTrashFile("foo", "otherData");
@@ -931,9 +967,8 @@ public class DirCacheCheckoutTest extends RepositoryTestCase {
offset += numRead;
}
is.close();
- assertTrue("unexpected content for path " + path
- + " in workDir. Expected: <" + expectedValue + ">",
- Arrays.equals(buffer, i.get(path).getBytes()));
+ assertArrayEquals("unexpected content for path " + path
+ + " in workDir. ", buffer, i.get(path).getBytes());
nrFiles++;
}
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFOutputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFOutputStreamTest.java
new file mode 100644
index 0000000000..b370468f6b
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/AutoCRLFOutputStreamTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2011, Robin Rosenberg
+ * 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.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class AutoCRLFOutputStreamTest {
+
+ @Test
+ public void test() throws IOException {
+ assertNoCrLf("", "");
+ assertNoCrLf("\r", "\r");
+ assertNoCrLf("\r\n", "\n");
+ assertNoCrLf("\r\n", "\r\n");
+ assertNoCrLf("\r\r", "\r\r");
+ assertNoCrLf("\r\n\r", "\n\r");
+ assertNoCrLf("\r\n\r\r", "\r\n\r\r");
+ assertNoCrLf("\r\n\r\n", "\r\n\r\n");
+ assertNoCrLf("\r\n\r\n\r", "\n\r\n\r");
+ }
+
+ private void assertNoCrLf(String string, String string2) throws IOException {
+ assertNoCrLfHelper(string, string2);
+ // \u00e5 = LATIN SMALL LETTER A WITH RING ABOVE
+ // the byte value is negative
+ assertNoCrLfHelper("\u00e5" + string, "\u00e5" + string2);
+ assertNoCrLfHelper("\u00e5" + string + "\u00e5", "\u00e5" + string2
+ + "\u00e5");
+ assertNoCrLfHelper(string + "\u00e5", string2 + "\u00e5");
+ }
+
+ private void assertNoCrLfHelper(String expect, String input)
+ throws IOException {
+ byte[] inbytes = input.getBytes();
+ byte[] expectBytes = expect.getBytes();
+ for (int i = 0; i < 5; ++i) {
+ byte[] buf = new byte[i];
+ InputStream in = new ByteArrayInputStream(inbytes);
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ OutputStream out = new AutoCRLFOutputStream(bos);
+ if (i > 0) {
+ int n;
+ while ((n = in.read(buf)) >= 0) {
+ out.write(buf, 0, n);
+ }
+ } else {
+ int c;
+ while ((c = in.read()) != -1)
+ out.write(c);
+ }
+ out.flush();
+ in.close();
+ out.close();
+ byte[] actualBytes = bos.toByteArray();
+ Assert.assertEquals("bufsize=" + i, encode(expectBytes),
+ encode(actualBytes));
+ }
+ }
+
+ String encode(byte[] in) {
+ StringBuilder str = new StringBuilder();
+ for (byte b : in) {
+ if (b < 32)
+ str.append(0xFF & b);
+ else {
+ str.append("'");
+ str.append((char) b);
+ str.append("'");
+ }
+ str.append(' ');
+ }
+ return str.toString();
+ }
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/EolCanonicalizingInputStreamTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/EolCanonicalizingInputStreamTest.java
index 37660ce720..960ca62411 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/EolCanonicalizingInputStreamTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/io/EolCanonicalizingInputStreamTest.java
@@ -77,6 +77,12 @@ public class EolCanonicalizingInputStreamTest {
test(bytes, bytes);
}
+ @Test
+ public void testEmpty() throws IOException {
+ final byte[] bytes = asBytes("");
+ test(bytes, bytes);
+ }
+
private void test(byte[] input, byte[] expected) throws IOException {
final InputStream bis1 = new ByteArrayInputStream(input);
final InputStream cis1 = new EolCanonicalizingInputStream(bis1);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
index 465f01ada9..9b397e88c9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheCheckout.java
@@ -45,6 +45,7 @@ package org.eclipse.jgit.dircache;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
@@ -62,6 +63,7 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.EmptyTreeIterator;
@@ -73,6 +75,7 @@ import org.eclipse.jgit.treewalk.WorkingTreeOptions;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils;
+import org.eclipse.jgit.util.io.AutoCRLFOutputStream;
/**
* This class handles checking out one or two trees merging with the index.
@@ -926,14 +929,19 @@ public class DirCacheCheckout {
ObjectLoader ol = or.open(entry.getObjectId());
File parentDir = f.getParentFile();
File tmpFile = File.createTempFile("._" + f.getName(), null, parentDir);
- FileOutputStream channel = new FileOutputStream(tmpFile);
+ WorkingTreeOptions opt = repo.getConfig().get(WorkingTreeOptions.KEY);
+ FileOutputStream rawChannel = new FileOutputStream(tmpFile);
+ OutputStream channel;
+ if (opt.getAutoCRLF() == AutoCRLF.TRUE)
+ channel = new AutoCRLFOutputStream(rawChannel);
+ else
+ channel = rawChannel;
try {
ol.copyTo(channel);
} finally {
channel.close();
}
FS fs = repo.getFS();
- WorkingTreeOptions opt = repo.getConfig().get(WorkingTreeOptions.KEY);
if (opt.isFileMode() && fs.supportsExecute()) {
if (FileMode.EXECUTABLE_FILE.equals(entry.getRawMode())) {
if (!fs.canExecute(tmpFile))
@@ -954,6 +962,9 @@ public class DirCacheCheckout {
}
}
entry.setLastModified(f.lastModified());
- entry.setLength((int) ol.getSize());
+ if (opt.getAutoCRLF() != AutoCRLF.FALSE)
+ entry.setLength(f.length()); // AutoCRLF wants on-disk-size
+ else
+ entry.setLength((int) ol.getSize());
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java
index 77902dfb40..ffd5c4149f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/file/ObjectDirectoryInserter.java
@@ -97,8 +97,8 @@ class ObjectDirectoryInserter extends ObjectInserter {
throws IOException {
if (len <= buffer().length) {
byte[] buf = buffer();
- IO.readFully(is, buf, 0, (int) len);
- return insert(type, buf, 0, (int) len);
+ int actLen = IO.readFully(is, buf, 0);
+ return insert(type, buf, 0, actLen);
} else {
MessageDigest md = digest();
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
index 2a42e05d39..fa57b71ad1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -72,6 +72,7 @@ import org.eclipse.jgit.ignore.IgnoreNode;
import org.eclipse.jgit.ignore.IgnoreRule;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.CoreConfig;
+import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
@@ -402,7 +403,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
}
}
- private InputStream filterClean(InputStream in) {
+ private InputStream filterClean(InputStream in) throws IOException {
return new EolCanonicalizingInputStream(in);
}
@@ -498,7 +499,13 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
* the file could not be opened for reading.
*/
public InputStream openEntryStream() throws IOException {
- return current().openInputStream();
+ InputStream rawis = current().openInputStream();
+ InputStream is;
+ if (getOptions().getAutoCRLF() != AutoCRLF.FALSE)
+ is = new EolCanonicalizingInputStream(rawis);
+ else
+ is = rawis;
+ return is;
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java
index 439fe09b5e..cafc94057b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/IO.java
@@ -142,7 +142,13 @@ public class IO {
throw new IOException(MessageFormat.format(
JGitText.get().fileIsTooLarge, path));
final byte[] buf = new byte[(int) sz];
- IO.readFully(in, buf, 0, buf.length);
+ int actSz = IO.readFully(in, buf, 0);
+
+ if (actSz == sz) {
+ byte[] ret = new byte[actSz];
+ System.arraycopy(buf, 0, ret, 0, actSz);
+ return ret;
+ }
return buf;
} finally {
try {
@@ -254,6 +260,31 @@ public class IO {
}
/**
+ * Read the entire byte array into memory, unless input is shorter
+ *
+ * @param fd
+ * input stream to read the data from.
+ * @param dst
+ * buffer that must be fully populated, [off, off+len).
+ * @param off
+ * position within the buffer to start writing to.
+ * @return number of bytes in buffer or stream, whichever is shortest
+ * @throws IOException
+ * there was an error reading from the stream.
+ */
+ public static int readFully(InputStream fd, byte[] dst, int off)
+ throws IOException {
+ int r;
+ int len = 0;
+ while ((r = fd.read(dst, off, dst.length - off)) >= 0
+ && len < dst.length) {
+ off += r;
+ len += r;
+ }
+ return len;
+ }
+
+ /**
* Skip an entire region of an input stream.
* <p>
* The input stream's position is moved forward by the number of requested
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFOutputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFOutputStream.java
new file mode 100644
index 0000000000..a4c1573909
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/AutoCRLFOutputStream.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2011, Robin Rosenberg
+ * 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.OutputStream;
+
+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.
+ */
+public class AutoCRLFOutputStream extends OutputStream {
+
+ private final OutputStream out;
+
+ private int buf = -1;
+
+ private byte[] binbuf = new byte[8000];
+
+ private int binbufcnt = 0;
+
+ private boolean isBinary;
+
+ /**
+ * @param out
+ */
+ public AutoCRLFOutputStream(OutputStream out) {
+ this.out = out;
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ int overflow = buffer((byte) b);
+ if (overflow >= 0)
+ return;
+ if (isBinary) {
+ out.write(b);
+ return;
+ }
+ if (b == '\n') {
+ if (buf == '\r') {
+ out.write('\n');
+ buf = -1;
+ } else if (buf == -1) {
+ out.write('\r');
+ out.write('\n');
+ buf = -1;
+ }
+ } else if (b == '\r') {
+ out.write(b);
+ buf = '\r';
+ } else {
+ out.write(b);
+ buf = -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, int off, int len) throws IOException {
+ int overflow = buffer(b, off, len);
+ if (overflow < 0)
+ return;
+ off = off + len - overflow;
+ 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) {
+ byte c = b[i];
+ if (c == '\r') {
+ buf = '\r';
+ } else if (c == '\n') {
+ if (buf != '\r') {
+ if (lastw < i) {
+ out.write(b, lastw, i - lastw);
+ }
+ out.write('\r');
+ lastw = i;
+ }
+ buf = -1;
+ } else {
+ buf = -1;
+ }
+ }
+ if (lastw < off + len) {
+ out.write(b, lastw, off + len - lastw);
+ }
+ if (b[off + len - 1] == '\r')
+ buf = '\r';
+ }
+
+ private int buffer(byte b) throws IOException {
+ if (binbufcnt > binbuf.length)
+ return 1;
+ binbuf[binbufcnt++] = b;
+ if (binbufcnt == binbuf.length)
+ decideMode();
+ return 0;
+ }
+
+ 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 {
+ isBinary = RawText.isBinary(binbuf, binbufcnt);
+ int cachedLen = binbufcnt;
+ binbufcnt = binbuf.length + 1; // full!
+ write(binbuf, 0, cachedLen);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ if (binbufcnt < binbuf.length)
+ decideMode();
+ buf = -1;
+ }
+
+ @Override
+ public void close() throws IOException {
+ flush();
+ super.close();
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolCanonicalizingInputStream.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolCanonicalizingInputStream.java
index 4bdd2b3e55..387d9b82bf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolCanonicalizingInputStream.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/io/EolCanonicalizingInputStream.java
@@ -46,8 +46,11 @@ 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'.
+ * An input stream which canonicalizes EOLs bytes on the fly to '\n', unless the
+ * first 8000 bytes indicate the stream is binary.
*
* Note: Make sure to apply this InputStream only to text files!
*/
@@ -62,6 +65,10 @@ public class EolCanonicalizingInputStream extends InputStream {
private int ptr;
+ private boolean isBinary;
+
+ private boolean modeDetected;
+
/**
* Creates a new InputStream, wrapping the specified stream
*
@@ -95,7 +102,8 @@ public class EolCanonicalizingInputStream extends InputStream {
}
byte b = buf[ptr++];
- if (b != '\r') {
+ if (isBinary || b != '\r') {
+ // Logic for binary files ends here
bs[off++] = b;
continue;
}
@@ -124,6 +132,10 @@ public class EolCanonicalizingInputStream extends InputStream {
cnt = in.read(buf, 0, buf.length);
if (cnt < 1)
return false;
+ if (!modeDetected) {
+ isBinary = RawText.isBinary(buf, cnt);
+ modeDetected = true;
+ }
ptr = 0;
return true;
}