Blaming with core.autocrlf set to 'true' - even for freshly checked out files - showed all lines as being locally modified. For autocrlf = true the line breaks of the local file will be converted to LF for blaming. This results in useful diffs and therefor in the desired blame annotations. For autocrlf = input no conversion takes place to cope with CRLF line breaks in the repository, in addition to the usual LF. For autocrlf = true CRLF line breaks in the repo can't be supported without additional effort. In that case the whole local file will be blamed as being locally modified. Change-Id: If020dcca54d16b2fb79210a070b8480aec82e58e Signed-off-by: Konrad Kügler <swamblumat-eclipsebugs@yahoo.de> Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>tags/v3.4.0.201405051725-m7
@@ -45,9 +45,15 @@ package org.eclipse.jgit.api; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertNotNull; | |||
import java.io.File; | |||
import org.eclipse.jgit.api.ResetCommand.ResetType; | |||
import org.eclipse.jgit.blame.BlameResult; | |||
import org.eclipse.jgit.junit.RepositoryTestCase; | |||
import org.eclipse.jgit.lib.ConfigConstants; | |||
import org.eclipse.jgit.lib.CoreConfig.AutoCRLF; | |||
import org.eclipse.jgit.revwalk.RevCommit; | |||
import org.eclipse.jgit.storage.file.FileBasedConfig; | |||
import org.junit.Test; | |||
/** | |||
@@ -325,4 +331,59 @@ public class BlameCommandTest extends RepositoryTestCase { | |||
assertEquals(commit3, lines.getSourceCommit(1)); | |||
assertEquals(commit3, lines.getSourceCommit(2)); | |||
} | |||
@Test | |||
public void testCoreAutoCrlf1() throws Exception { | |||
testCoreAutoCrlf(AutoCRLF.INPUT, AutoCRLF.FALSE); | |||
} | |||
@Test | |||
public void testCoreAutoCrlf2() throws Exception { | |||
testCoreAutoCrlf(AutoCRLF.FALSE, AutoCRLF.FALSE); | |||
} | |||
@Test | |||
public void testCoreAutoCrlf3() throws Exception { | |||
testCoreAutoCrlf(AutoCRLF.INPUT, AutoCRLF.INPUT); | |||
} | |||
@Test | |||
public void testCoreAutoCrlf4() throws Exception { | |||
testCoreAutoCrlf(AutoCRLF.FALSE, AutoCRLF.INPUT); | |||
} | |||
@Test | |||
public void testCoreAutoCrlf5() throws Exception { | |||
testCoreAutoCrlf(AutoCRLF.INPUT, AutoCRLF.TRUE); | |||
} | |||
private void testCoreAutoCrlf(AutoCRLF modeForCommitting, | |||
AutoCRLF modeForReset) throws Exception { | |||
Git git = new Git(db); | |||
FileBasedConfig config = db.getConfig(); | |||
config.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null, | |||
ConfigConstants.CONFIG_KEY_AUTOCRLF, modeForCommitting); | |||
config.save(); | |||
String joinedCrlf = "a\r\nb\r\nc\r\n"; | |||
File trashFile = writeTrashFile("file.txt", joinedCrlf); | |||
git.add().addFilepattern("file.txt").call(); | |||
RevCommit commit = git.commit().setMessage("create file").call(); | |||
// re-create file from the repo | |||
trashFile.delete(); | |||
config.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null, | |||
ConfigConstants.CONFIG_KEY_AUTOCRLF, modeForReset); | |||
config.save(); | |||
git.reset().setMode(ResetType.HARD).call(); | |||
BlameCommand command = new BlameCommand(db); | |||
command.setFilePath("file.txt"); | |||
BlameResult lines = command.call(); | |||
assertEquals(3, lines.getResultContents().size()); | |||
assertEquals(commit, lines.getSourceCommit(0)); | |||
assertEquals(commit, lines.getSourceCommit(1)); | |||
assertEquals(commit, lines.getSourceCommit(2)); | |||
} | |||
} |
@@ -43,7 +43,10 @@ | |||
package org.eclipse.jgit.api; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.FileNotFoundException; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
@@ -60,6 +63,10 @@ import org.eclipse.jgit.lib.AnyObjectId; | |||
import org.eclipse.jgit.lib.Constants; | |||
import org.eclipse.jgit.lib.ObjectId; | |||
import org.eclipse.jgit.lib.Repository; | |||
import org.eclipse.jgit.lib.CoreConfig.AutoCRLF; | |||
import org.eclipse.jgit.treewalk.WorkingTreeOptions; | |||
import org.eclipse.jgit.util.IO; | |||
import org.eclipse.jgit.util.io.EolCanonicalizingInputStream; | |||
/** | |||
* Blame command for building a {@link BlameResult} for a file path. | |||
@@ -215,8 +222,10 @@ public class BlameCommand extends GitCommand<BlameResult> { | |||
gen.push(null, dc.getEntry(entry).getObjectId()); | |||
File inTree = new File(repo.getWorkTree(), path); | |||
if (repo.getFS().isFile(inTree)) | |||
gen.push(null, new RawText(inTree)); | |||
if (repo.getFS().isFile(inTree)) { | |||
RawText rawText = getRawText(inTree); | |||
gen.push(null, rawText); | |||
} | |||
} | |||
} | |||
return gen.computeBlameResult(); | |||
@@ -226,4 +235,49 @@ public class BlameCommand extends GitCommand<BlameResult> { | |||
gen.release(); | |||
} | |||
} | |||
private RawText getRawText(File inTree) throws IOException, | |||
FileNotFoundException { | |||
RawText rawText; | |||
WorkingTreeOptions workingTreeOptions = getRepository().getConfig() | |||
.get(WorkingTreeOptions.KEY); | |||
AutoCRLF autoCRLF = workingTreeOptions.getAutoCRLF(); | |||
switch (autoCRLF) { | |||
case FALSE: | |||
case INPUT: | |||
// Git used the repo format on checkout, but other tools | |||
// may change the format to CRLF. We ignore that here. | |||
rawText = new RawText(inTree); | |||
break; | |||
case TRUE: | |||
EolCanonicalizingInputStream in = new EolCanonicalizingInputStream( | |||
new FileInputStream(inTree), true); | |||
// Canonicalization should lead to same or shorter length | |||
// (CRLF to LF), so the file size on disk is an upper size bound | |||
rawText = new RawText(toByteArray(in, (int) inTree.length())); | |||
break; | |||
default: | |||
throw new IllegalArgumentException( | |||
"Unknown autocrlf option " + autoCRLF); //$NON-NLS-1$ | |||
} | |||
return rawText; | |||
} | |||
private static byte[] toByteArray(InputStream source, int upperSizeLimit) | |||
throws IOException { | |||
byte[] buffer = new byte[upperSizeLimit]; | |||
try { | |||
int read = IO.readFully(source, buffer, 0); | |||
if (read == upperSizeLimit) | |||
return buffer; | |||
else { | |||
byte[] copy = new byte[read]; | |||
System.arraycopy(buffer, 0, copy, 0, read); | |||
return copy; | |||
} | |||
} finally { | |||
source.close(); | |||
} | |||
} | |||
} |