Handle missing "ours" stage in WorkingTreeIterator.hasCrLfInIndex()
In a delete-modify conflict with the deletion as "ours" there may be no stage 2 in the index. Add appropriate null checks. Add a new test for this case, and verify that the file gets added with a single LF after conflict resolution with core.autocrlf=true. This matches the behavior of canonical git for this case. Bug: 547724 Change-Id: I1bafdb83d9b78bf85294c78325e818e72fae53bc Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
This commit is contained in:
parent
fd8d779b5e
commit
1cfcde4853
|
@ -661,6 +661,54 @@ public void commitWithAutoCrlfAndNonNormalizedIndex() throws Exception {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeletionConflictWithAutoCrlf() throws Exception {
|
||||||
|
try (Git git = new Git(db)) {
|
||||||
|
// Commit a file with CR/LF into the index
|
||||||
|
FileBasedConfig config = db.getConfig();
|
||||||
|
config.setString("core", null, "autocrlf", "false");
|
||||||
|
config.save();
|
||||||
|
File file = writeTrashFile("file.txt", "foo\r\n");
|
||||||
|
git.add().addFilepattern("file.txt").call();
|
||||||
|
git.commit().setMessage("Initial").call();
|
||||||
|
// Switch to side branch
|
||||||
|
git.checkout().setCreateBranch(true).setName("side").call();
|
||||||
|
assertTrue(file.delete());
|
||||||
|
git.rm().addFilepattern("file.txt").call();
|
||||||
|
git.commit().setMessage("Side").call();
|
||||||
|
// Switch on autocrlf=true
|
||||||
|
config.setString("core", null, "autocrlf", "true");
|
||||||
|
config.save();
|
||||||
|
// Switch back to master and commit a conflict
|
||||||
|
git.checkout().setName("master").call();
|
||||||
|
writeTrashFile("file.txt", "foob\r\n");
|
||||||
|
git.add().addFilepattern("file.txt").call();
|
||||||
|
assertEquals("[file.txt, mode:100644, content:foob\r\n]",
|
||||||
|
indexState(CONTENT));
|
||||||
|
writeTrashFile("g", "file2.txt", "anything");
|
||||||
|
git.add().addFilepattern("g/file2.txt");
|
||||||
|
RevCommit master = git.commit().setMessage("Second").call();
|
||||||
|
// Switch to side branch again so that the deletion is "ours"
|
||||||
|
git.checkout().setName("side").call();
|
||||||
|
// Cherry pick master: produces a delete-modify conflict.
|
||||||
|
CherryPickResult pick = git.cherryPick().include(master).call();
|
||||||
|
assertEquals("Expected a cherry-pick conflict",
|
||||||
|
CherryPickStatus.CONFLICTING, pick.getStatus());
|
||||||
|
// XXX: g/file2.txt should actually be staged already, but isn't.
|
||||||
|
git.add().addFilepattern("g/file2.txt").call();
|
||||||
|
// Resolve the conflict by taking the master version
|
||||||
|
writeTrashFile("file.txt", "foob\r\n");
|
||||||
|
git.add().addFilepattern("file.txt").call();
|
||||||
|
git.commit().setMessage("Cherry").call();
|
||||||
|
// We expect this to be committed with a single LF since there is no
|
||||||
|
// "ours" stage.
|
||||||
|
assertEquals(
|
||||||
|
"[file.txt, mode:100644, content:foob\n]"
|
||||||
|
+ "[g/file2.txt, mode:100644, content:anything]",
|
||||||
|
indexState(CONTENT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void testConflictWithAutoCrlf(String baseLf, String lf)
|
private void testConflictWithAutoCrlf(String baseLf, String lf)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
try (Git git = new Git(db)) {
|
try (Git git = new Git(db)) {
|
||||||
|
|
|
@ -1516,6 +1516,7 @@ private boolean hasCrLfInIndex(DirCacheIterator dirCache) {
|
||||||
ObjectId blobId = entry.getObjectId();
|
ObjectId blobId = entry.getObjectId();
|
||||||
if (entry.getStage() > 0
|
if (entry.getStage() > 0
|
||||||
&& entry.getStage() != DirCacheEntry.STAGE_2) {
|
&& entry.getStage() != DirCacheEntry.STAGE_2) {
|
||||||
|
blobId = null;
|
||||||
// Merge conflict: check ours (stage 2)
|
// Merge conflict: check ours (stage 2)
|
||||||
byte[] name = entry.getRawPath();
|
byte[] name = entry.getRawPath();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
@ -1523,7 +1524,8 @@ private boolean hasCrLfInIndex(DirCacheIterator dirCache) {
|
||||||
dirCache.next(1);
|
dirCache.next(1);
|
||||||
i++;
|
i++;
|
||||||
entry = dirCache.getDirCacheEntry();
|
entry = dirCache.getDirCacheEntry();
|
||||||
if (!Arrays.equals(name, entry.getRawPath())) {
|
if (entry == null
|
||||||
|
|| !Arrays.equals(name, entry.getRawPath())) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (entry.getStage() == DirCacheEntry.STAGE_2) {
|
if (entry.getStage() == DirCacheEntry.STAGE_2) {
|
||||||
|
@ -1533,17 +1535,20 @@ private boolean hasCrLfInIndex(DirCacheIterator dirCache) {
|
||||||
}
|
}
|
||||||
dirCache.back(i);
|
dirCache.back(i);
|
||||||
}
|
}
|
||||||
try (ObjectReader reader = repository.newObjectReader()) {
|
if (blobId != null) {
|
||||||
ObjectLoader loader = reader.open(blobId, Constants.OBJ_BLOB);
|
try (ObjectReader reader = repository.newObjectReader()) {
|
||||||
try {
|
ObjectLoader loader = reader.open(blobId,
|
||||||
return RawText.isCrLfText(loader.getCachedBytes());
|
Constants.OBJ_BLOB);
|
||||||
} catch (LargeObjectException e) {
|
try {
|
||||||
try (InputStream in = loader.openStream()) {
|
return RawText.isCrLfText(loader.getCachedBytes());
|
||||||
return RawText.isCrLfText(in);
|
} catch (LargeObjectException e) {
|
||||||
|
try (InputStream in = loader.openStream()) {
|
||||||
|
return RawText.isCrLfText(in);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Ignore and return false below
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
|
||||||
// Ignore and return false below
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
Loading…
Reference in New Issue