From b61a2ceef67ecbed9b06752bacceb9b0112c2f64 Mon Sep 17 00:00:00 2001 From: Ash Wilson Date: Sun, 24 Apr 2016 20:02:32 -0400 Subject: [PATCH] Fail gracefully for unterminated conflicts. --- lib/conflict.js | 28 ++++++++++++++++++++------- spec/conflict-spec.coffee | 16 +++++++++++++++ spec/fixtures/corrupted-2way-diff.txt | 6 ++++++ spec/fixtures/corrupted-3way-diff.txt | 21 ++++++++++++++++++++ 4 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 spec/fixtures/corrupted-2way-diff.txt create mode 100644 spec/fixtures/corrupted-3way-diff.txt diff --git a/lib/conflict.js b/lib/conflict.js index 9895450..795f176 100644 --- a/lib/conflict.js +++ b/lib/conflict.js @@ -121,14 +121,22 @@ export class Conflict { } const visitor = new ConflictVisitor(merge, editor) - lastRow = parseConflict(merge, editor, conflictStartRow, visitor) - const conflict = visitor.conflict() - if (conflicts.length > 0) { - conflict.navigator.linkToPrevious(conflicts[conflicts.length - 1]) - } + try { + lastRow = parseConflict(merge, editor, conflictStartRow, visitor) + const conflict = visitor.conflict() + + if (conflicts.length > 0) { + conflict.navigator.linkToPrevious(conflicts[conflicts.length - 1]) + } + conflicts.push(conflict) + } catch (e) { + if (!e.parserState) throw e - conflicts.push(conflict) + if (!atom.inSpecMode()) { + console.error(`Unable to parse conflict: ${e.message}\n${e.stack}`) + } + } }) return conflicts @@ -349,10 +357,16 @@ const parseConflict = function (merge, editor, row, visitor) { // the editor. const advanceToBoundary = (boundaryKinds = '<|=>') => { let b = isAtBoundary(boundaryKinds) - while (b === null && row < editor.getLineCount()) { + while (b === null) { row += 1 + if (row > editor.getLastBufferRow()) { + const e = new Error('Unterminated conflict side') + e.parserState = true + throw e + } b = isAtBoundary(boundaryKinds) } + lastBoundary = b return b } diff --git a/spec/conflict-spec.coffee b/spec/conflict-spec.coffee index 0c719eb..9213ccc 100644 --- a/spec/conflict-spec.coffee +++ b/spec/conflict-spec.coffee @@ -87,6 +87,22 @@ describe "Conflict", -> expect(util.rowRangeFrom cs[1].ours.marker).toEqual([14, 15]) expect(util.rowRangeFrom cs[1].theirs.marker).toEqual([16, 17]) + describe 'with corrupted diffs', -> + + it 'handles corrupted diff output', -> + util.openPath 'corrupted-2way-diff.txt', (editorView) -> + cs = Conflict.all({}, editorView.getModel()) + expect(cs.length).toBe(0) + + it 'handles corrupted diff3 output', -> + util.openPath 'corrupted-3way-diff.txt', (editorView) -> + cs = Conflict.all({}, editorView.getModel()) + + expect(cs.length).toBe(1) + expect(util.rowRangeFrom cs[0].ours.marker).toEqual([13, 14]) + expect(util.rowRangeFrom cs[0].base.marker).toEqual([15, 16]) + expect(util.rowRangeFrom cs[0].theirs.marker).toEqual([17, 18]) + describe 'when rebasing', -> [conflict] = [] diff --git a/spec/fixtures/corrupted-2way-diff.txt b/spec/fixtures/corrupted-2way-diff.txt new file mode 100644 index 0000000..bbd1682 --- /dev/null +++ b/spec/fixtures/corrupted-2way-diff.txt @@ -0,0 +1,6 @@ +<<<<<<< HEAD +These are my changes +======= +These are your changes + +Oops, deleted the end marker. diff --git a/spec/fixtures/corrupted-3way-diff.txt b/spec/fixtures/corrupted-3way-diff.txt new file mode 100644 index 0000000..c257546 --- /dev/null +++ b/spec/fixtures/corrupted-3way-diff.txt @@ -0,0 +1,21 @@ +This is a file containing a corrupted diff3 patch. + +<<<<<<< HEAD +These are my changes +||||||| merged common ancestors +These are original texts +Oops, we never get a separator +These are your changes +>>>>>>> master + +It also contains a valid one. + +<<<<<<< HEAD +These are my changes +||||||| merged common ancestors +These are original texts +======= +These are your changes +>>>>>>> master + +And some text after the end.