diff options
author | Thomas Wolf <thomas.wolf@paranor.ch> | 2018-08-12 19:30:36 +0200 |
---|---|---|
committer | David Pursehouse <david.pursehouse@gmail.com> | 2019-02-26 16:18:27 +0900 |
commit | 60cf85a4a39c34a426e52c4f1fa3f5ef5ba659c5 (patch) | |
tree | 47e943891f755cdfd2c169056d14292949033a8a /org.eclipse.jgit/src/org/eclipse/jgit/treewalk | |
parent | e54243278ff810ef95e26b8a15bf3aa8505a2161 (diff) | |
download | jgit-60cf85a4a39c34a426e52c4f1fa3f5ef5ba659c5.tar.gz jgit-60cf85a4a39c34a426e52c4f1fa3f5ef5ba659c5.zip |
Fix core.autocrlf for non-normalized index
With text=auto or core.autocrlf=true, git does not normalize upon
check-in if the file in the index contains already CR/LFs. The
documentation says: "When text is set to "auto", the path is
marked for automatic end-of-line conversion. If Git decides that
the content is text, its line endings are converted to LF on
checkin. When the file has been committed with CRLF, no conversion
is done."[1]
Implement the last bit as in canonical git: check the blob in the
index for CR/LFs. For very large files, we check only the first 8000
bytes, like RawText.isBinary() and AutoLFInputStream do.
In Auto(CR)LFInputStream, ensure that the buffer is filled as much as
possible for the isBinary() check.
Regarding these content checks, there are a number of inconsistencies:
* Canonical git considers files containing lone CRs as binary.
* RawText checks the first 8000 bytes.
* Auto(CR)LFInputStream checks the first 8096 (not 8192!) bytes.
None of these are changed with this commit. It appears that canonical
git will check the whole blob, not just the first 8k bytes. Also
note: the check for CR/LF text won't work with LFS (neither in JGit
nor in git) since the blob data is not run through the smudge filter.
C.f. [2].
Two tests in AddCommandTest actually tested that normalization was
done even if the file was already committed with CR/LF.These tests
had to be adapted. I find the git documentation unclear about the
case where core.autocrlf=input, but from [3] it looks as if this
non-normalization also applies in this case.
Add new tests in CommitCommandTest testing this for the case where
the index entry is for a merge conflict. In this case, canonical git
uses the "ours" version.[4] Do the same.
[1] https://git-scm.com/docs/gitattributes
[2] https://github.com/git/git/blob/3434569fc/convert.c#L225
[3] https://github.com/git/git/blob/3434569fc/convert.c#L529
[4] https://github.com/git/git/blob/f2b6aa98b/read-cache.c#L3281
Bug: 470643
Change-Id: Ie7310539fbe6c737d78b1dcc29e34735d4616b88
Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
Diffstat (limited to 'org.eclipse.jgit/src/org/eclipse/jgit/treewalk')
-rw-r--r-- | org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java | 65 |
1 files changed, 64 insertions, 1 deletions
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 1fa1db5847..b768acd050 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java @@ -74,6 +74,7 @@ import org.eclipse.jgit.diff.RawText; import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.errors.CorruptObjectException; +import org.eclipse.jgit.errors.LargeObjectException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.NoWorkTreeException; import org.eclipse.jgit.ignore.FastIgnoreRule; @@ -1471,9 +1472,18 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { private EolStreamType getEolStreamType(OperationType opType) throws IOException { if (eolStreamTypeHolder == null) { - EolStreamType type=null; + EolStreamType type = null; if (state.walk != null) { type = state.walk.getEolStreamType(opType); + OperationType operationType = opType != null ? opType + : state.walk.getOperationType(); + if (OperationType.CHECKIN_OP.equals(operationType) + && EolStreamType.AUTO_LF.equals(type) + && hasCrLfInIndex(getDirCacheIterator())) { + // If text=auto (or core.autocrlf=true) and the file has + // already been committed with CR/LF, then don't convert. + type = EolStreamType.DIRECT; + } } else { switch (getOptions().getAutoCRLF()) { case FALSE: @@ -1490,6 +1500,59 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { return eolStreamTypeHolder.get(); } + /** + * Determines whether the file was committed un-normalized. If the iterator + * points to a conflict entry, checks the "ours" version. + * + * @param dirCache + * iterator pointing to the current entry for the file in the + * index + * @return {@code true} if the file in the index is not binary and has CR/LF + * line endings, {@code false} otherwise + */ + private boolean hasCrLfInIndex(DirCacheIterator dirCache) { + if (dirCache == null) { + return false; + } + // Read blob from index and check for CR/LF-delimited text. + DirCacheEntry entry = dirCache.getDirCacheEntry(); + if (FileMode.REGULAR_FILE.equals(entry.getFileMode())) { + ObjectId blobId = entry.getObjectId(); + if (entry.getStage() > 0 + && entry.getStage() != DirCacheEntry.STAGE_2) { + // Merge conflict: check ours (stage 2) + byte[] name = entry.getRawPath(); + int i = 0; + while (!dirCache.eof()) { + dirCache.next(1); + i++; + entry = dirCache.getDirCacheEntry(); + if (!Arrays.equals(name, entry.getRawPath())) { + break; + } + if (entry.getStage() == DirCacheEntry.STAGE_2) { + blobId = entry.getObjectId(); + break; + } + } + dirCache.back(i); + } + try (ObjectReader reader = repository.newObjectReader()) { + ObjectLoader loader = reader.open(blobId, Constants.OBJ_BLOB); + try { + return RawText.isCrLfText(loader.getCachedBytes()); + } catch (LargeObjectException e) { + try (InputStream in = loader.openStream()) { + return RawText.isCrLfText(in); + } + } + } catch (IOException e) { + // Ignore and return false below + } + } + return false; + } + private boolean isDirectoryIgnored(String pathRel) throws IOException { final int pOff = 0 < pathOffset ? pathOffset - 1 : pathOffset; final String base = TreeWalk.pathOf(this.path, 0, pOff); |