From f081be2143467ecbdd2b2d931a91002f6eefbddd Mon Sep 17 00:00:00 2001 From: yeonjuan Date: Tue, 26 Oct 2021 12:15:13 +0900 Subject: [PATCH 1/2] fix: handle conflict diff --- src/__fixtures__/conflict-file | 17 +++ .../__snapshots__/conflict-file.test.ts.snap | 100 ++++++++++++++++++ src/__tests__/conflict-file.test.ts | 10 ++ src/parse-git-diff.ts | 88 +++++++++++---- src/types.ts | 21 ++-- 5 files changed, 208 insertions(+), 28 deletions(-) create mode 100644 src/__fixtures__/conflict-file create mode 100644 src/__tests__/__snapshots__/conflict-file.test.ts.snap create mode 100644 src/__tests__/conflict-file.test.ts diff --git a/src/__fixtures__/conflict-file b/src/__fixtures__/conflict-file new file mode 100644 index 0000000..04b45ca --- /dev/null +++ b/src/__fixtures__/conflict-file @@ -0,0 +1,17 @@ +diff --cc README.md +index 2445f65,f4b8569..0000000 +--- a/README.md ++++ b/README.md +@@@ -8,7 -8,7 +8,11 @@@ + npm install parse-git-diff + ``` + +++<<<<<<< HEAD + +## a +++======= ++ ## b +++>>>>>>> branch-b + + - [demo](https://yeonjuan.github.io/parse-git-diff/) + + \ No newline at end of file diff --git a/src/__tests__/__snapshots__/conflict-file.test.ts.snap b/src/__tests__/__snapshots__/conflict-file.test.ts.snap new file mode 100644 index 0000000..b9aca30 --- /dev/null +++ b/src/__tests__/__snapshots__/conflict-file.test.ts.snap @@ -0,0 +1,100 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`conflict-file parse \`conflict-file\` 1`] = ` +Object { + "files": Array [ + Object { + "chunks": Array [ + Object { + "changes": Array [ + Object { + "content": " npm install parse-git-diff", + "lineAfter": 8, + "lineBefore": 8, + "type": "UnchangedLine", + }, + Object { + "content": " \`\`\`", + "lineAfter": 9, + "lineBefore": 9, + "type": "UnchangedLine", + }, + Object { + "content": " ", + "lineAfter": 10, + "lineBefore": 10, + "type": "UnchangedLine", + }, + Object { + "content": "+<<<<<<< HEAD", + "lineAfter": 11, + "type": "AddedLine", + }, + Object { + "content": "+## a", + "lineAfter": 12, + "lineBefore": 11, + "type": "UnchangedLine", + }, + Object { + "content": "+=======", + "lineAfter": 13, + "type": "AddedLine", + }, + Object { + "content": " ## b", + "lineAfter": 14, + "type": "AddedLine", + }, + Object { + "content": "+>>>>>>> branch-b", + "lineAfter": 15, + "type": "AddedLine", + }, + Object { + "content": " ", + "lineAfter": 16, + "lineBefore": 12, + "type": "UnchangedLine", + }, + Object { + "content": " - [demo](https://yeonjuan.github.io/parse-git-diff/)", + "lineAfter": 17, + "lineBefore": 13, + "type": "UnchangedLine", + }, + Object { + "content": " ", + "lineAfter": 18, + "lineBefore": 14, + "type": "UnchangedLine", + }, + Object { + "content": " ", + "lineAfter": 19, + "lineBefore": 15, + "type": "UnchangedLine", + }, + ], + "rangeAfter": Object { + "lines": 11, + "start": 8, + }, + "rangeBeforeA": Object { + "lines": 7, + "start": 8, + }, + "rangeBeforeB": Object { + "lines": 7, + "start": 8, + }, + "type": "CombinedChunk", + }, + ], + "path": "README.md", + "type": "ChangedFile", + }, + ], + "type": "GitDiff", +} +`; diff --git a/src/__tests__/conflict-file.test.ts b/src/__tests__/conflict-file.test.ts new file mode 100644 index 0000000..4a46c76 --- /dev/null +++ b/src/__tests__/conflict-file.test.ts @@ -0,0 +1,10 @@ +import { getFixture } from './test-utils'; +import parseGitDiff from '../parse-git-diff'; + +describe('conflict-file', () => { + const fixture = getFixture('conflict-file'); + + it('parse `conflict-file`', () => { + expect(parseGitDiff(fixture)).toMatchSnapshot(); + }); +}); diff --git a/src/parse-git-diff.ts b/src/parse-git-diff.ts index a92884b..03fa2aa 100644 --- a/src/parse-git-diff.ts +++ b/src/parse-git-diff.ts @@ -5,6 +5,8 @@ import type { AnyLineChange, Chunk, ChunkRange, + CombinedChunk, + AnyChunk, } from './types'; import { ExtendedHeader, @@ -96,11 +98,12 @@ function parseFileChange(ctx: Context): AnyFileChange | undefined { } function isComparisonInputLine(line: string): boolean { - return line.indexOf('diff --git') === 0; + console.log(line); + return line.indexOf('diff') === 0; } -function parseChunks(context: Context): Chunk[] { - const chunks: Chunk[] = []; +function parseChunks(context: Context): AnyChunk[] { + const chunks: AnyChunk[] = []; while (!context.isEof()) { const chunk = parseChunk(context); @@ -112,23 +115,41 @@ function parseChunks(context: Context): Chunk[] { return chunks; } -function parseChunk(context: Context): Chunk | undefined { +function parseChunk(context: Context): AnyChunk | undefined { const chunkHeader = parseChunkHeader(context); if (!chunkHeader) { return; } - const changes: AnyLineChange[] = parseChanges( - context, - chunkHeader.rangeBefore, - chunkHeader.rangeAfter - ); - - return { - type: 'Chunk', - ...chunkHeader, - changes, - }; + if (chunkHeader.type === 'Normal') { + const changes = parseChanges( + context, + chunkHeader.rangeBefore, + chunkHeader.rangeAfter + ); + return { + ...chunkHeader, + type: 'Chunk', + changes, + }; + } else if ( + chunkHeader.type === 'Combined' && + chunkHeader.rangeBeforeA && + chunkHeader.rangeBeforeB + ) { + const changes = parseChanges( + context, + chunkHeader.rangeBeforeA.start < chunkHeader.rangeBeforeB.start + ? chunkHeader.rangeBeforeA + : chunkHeader.rangeBeforeB, + chunkHeader.rangeAfter + ); + return { + ...chunkHeader, + type: 'CombinedChunk', + changes, + }; + } } function parseExtendedHeader(ctx: Context) { @@ -153,17 +174,40 @@ function parseExtendedHeader(ctx: Context) { return null; } -function parseChunkHeader( - ctx: Context -): Pick | null { +function parseChunkHeader(ctx: Context) { const line = ctx.getCurLine(); - const exec = /^@@\s\-(\d+),?(\d+)?\s\+(\d+),?(\d+)?\s@@/.exec(line); - if (!exec) { - return null; + const normalChunkExec = /^@@\s\-(\d+),?(\d+)?\s\+(\d+),?(\d+)?\s@@/.exec( + line + ); + if (!normalChunkExec) { + const combinedChunkExec = + /^@@@\s\-(\d+),?(\d+)?\s\-(\d+),?(\d+)?\s\+(\d+),?(\d+)?\s@@@/.exec(line); + + if (!combinedChunkExec) { + return null; + } + + const [ + all, + delStartA, + delLinesA, + delStartB, + delLinesB, + addStart, + addLines, + ] = combinedChunkExec; + ctx.nextLine(); + return { + type: 'Combined', + rangeBeforeA: getRange(delStartA, delLinesA), + rangeBeforeB: getRange(delStartB, delLinesB), + rangeAfter: getRange(addStart, addLines), + } as const; } - const [all, delStart, delLines, addStart, addLines] = exec; + const [all, delStart, delLines, addStart, addLines] = normalChunkExec; ctx.nextLine(); return { + type: 'Normal', rangeAfter: getRange(addStart, addLines), rangeBefore: getRange(delStart, delLines), }; diff --git a/src/types.ts b/src/types.ts index 8694b6d..d485ce4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -34,25 +34,34 @@ export interface Chunk extends Base<'Chunk'> { changes: AnyLineChange[]; } -interface BaseFileChange extends Base { - chunks: Chunk[]; +export interface CombinedChunk extends Base<'CombinedChunk'> { + rangeBeforeA: ChunkRange; + rangeBeforeB: ChunkRange; + rangeAfter: ChunkRange; + changes: AnyLineChange[]; } -export interface ChangedFile extends BaseFileChange { +export type AnyChunk = Chunk | CombinedChunk; + +export interface ChangedFile extends Base { path: string; + chunks: AnyChunk[]; } -export interface AddedFile extends BaseFileChange { +export interface AddedFile extends Base { path: string; + chunks: AnyChunk[]; } -export interface DeletedFile extends BaseFileChange { +export interface DeletedFile extends Base { path: string; + chunks: AnyChunk[]; } -export interface RenamedFile extends BaseFileChange { +export interface RenamedFile extends Base { pathBefore: string; pathAfter: string; + chunks: AnyChunk[]; } export type AnyFileChange = ChangedFile | AddedFile | DeletedFile | RenamedFile; From 8a1e6358fff209d1de3f15c77ee0c5c398ed30a9 Mon Sep 17 00:00:00 2001 From: yeonjuan Date: Tue, 26 Oct 2021 21:36:25 +0900 Subject: [PATCH 2/2] feat: change range prop name --- src/__tests__/__snapshots__/all.test.ts.snap | 36 +++++++++---------- .../__snapshots__/conflict-file.test.ts.snap | 10 +++--- .../__snapshots__/deleted-file.test.ts.snap | 10 +++--- .../__snapshots__/deleted-line.test.ts.snap | 8 ++--- .../__snapshots__/new-file.test.ts.snap | 10 +++--- .../__snapshots__/new-line.test.ts.snap | 8 ++--- src/parse-git-diff.ts | 26 +++++++------- src/types.ts | 10 +++--- 8 files changed, 59 insertions(+), 59 deletions(-) diff --git a/src/__tests__/__snapshots__/all.test.ts.snap b/src/__tests__/__snapshots__/all.test.ts.snap index e9c3cfa..c3e96f2 100644 --- a/src/__tests__/__snapshots__/all.test.ts.snap +++ b/src/__tests__/__snapshots__/all.test.ts.snap @@ -19,12 +19,12 @@ Object { "type": "DeletedLine", }, ], - "rangeAfter": Object { - "lines": 1, + "fromFileRange": Object { + "lines": 2, "start": 1, }, - "rangeBefore": Object { - "lines": 2, + "toFileRange": Object { + "lines": 1, "start": 1, }, "type": "Chunk", @@ -43,14 +43,14 @@ Object { "type": "DeletedLine", }, ], - "rangeAfter": Object { - "lines": 0, - "start": 0, - }, - "rangeBefore": Object { + "fromFileRange": Object { "lines": 1, "start": 1, }, + "toFileRange": Object { + "lines": 0, + "start": 0, + }, "type": "Chunk", }, ], @@ -67,14 +67,14 @@ Object { "type": "AddedLine", }, ], - "rangeAfter": Object { - "lines": 1, - "start": 1, - }, - "rangeBefore": Object { + "fromFileRange": Object { "lines": 0, "start": 0, }, + "toFileRange": Object { + "lines": 1, + "start": 1, + }, "type": "Chunk", }, ], @@ -97,12 +97,12 @@ Object { "type": "AddedLine", }, ], - "rangeAfter": Object { - "lines": 2, + "fromFileRange": Object { + "lines": 1, "start": 1, }, - "rangeBefore": Object { - "lines": 1, + "toFileRange": Object { + "lines": 2, "start": 1, }, "type": "Chunk", diff --git a/src/__tests__/__snapshots__/conflict-file.test.ts.snap b/src/__tests__/__snapshots__/conflict-file.test.ts.snap index b9aca30..dce5192 100644 --- a/src/__tests__/__snapshots__/conflict-file.test.ts.snap +++ b/src/__tests__/__snapshots__/conflict-file.test.ts.snap @@ -76,16 +76,16 @@ Object { "type": "UnchangedLine", }, ], - "rangeAfter": Object { - "lines": 11, + "fromFileRangeA": Object { + "lines": 7, "start": 8, }, - "rangeBeforeA": Object { + "fromFileRangeB": Object { "lines": 7, "start": 8, }, - "rangeBeforeB": Object { - "lines": 7, + "toFileRange": Object { + "lines": 11, "start": 8, }, "type": "CombinedChunk", diff --git a/src/__tests__/__snapshots__/deleted-file.test.ts.snap b/src/__tests__/__snapshots__/deleted-file.test.ts.snap index 3020cc8..021c0a1 100644 --- a/src/__tests__/__snapshots__/deleted-file.test.ts.snap +++ b/src/__tests__/__snapshots__/deleted-file.test.ts.snap @@ -13,14 +13,14 @@ Object { "type": "DeletedLine", }, ], - "rangeAfter": Object { - "lines": 0, - "start": 0, - }, - "rangeBefore": Object { + "fromFileRange": Object { "lines": 1, "start": 1, }, + "toFileRange": Object { + "lines": 0, + "start": 0, + }, "type": "Chunk", }, ], diff --git a/src/__tests__/__snapshots__/deleted-line.test.ts.snap b/src/__tests__/__snapshots__/deleted-line.test.ts.snap index 8fc3982..f571c86 100644 --- a/src/__tests__/__snapshots__/deleted-line.test.ts.snap +++ b/src/__tests__/__snapshots__/deleted-line.test.ts.snap @@ -19,12 +19,12 @@ Object { "type": "DeletedLine", }, ], - "rangeAfter": Object { - "lines": 1, + "fromFileRange": Object { + "lines": 2, "start": 1, }, - "rangeBefore": Object { - "lines": 2, + "toFileRange": Object { + "lines": 1, "start": 1, }, "type": "Chunk", diff --git a/src/__tests__/__snapshots__/new-file.test.ts.snap b/src/__tests__/__snapshots__/new-file.test.ts.snap index b114b55..be8e21a 100644 --- a/src/__tests__/__snapshots__/new-file.test.ts.snap +++ b/src/__tests__/__snapshots__/new-file.test.ts.snap @@ -13,14 +13,14 @@ Object { "type": "AddedLine", }, ], - "rangeAfter": Object { - "lines": 1, - "start": 1, - }, - "rangeBefore": Object { + "fromFileRange": Object { "lines": 0, "start": 0, }, + "toFileRange": Object { + "lines": 1, + "start": 1, + }, "type": "Chunk", }, ], diff --git a/src/__tests__/__snapshots__/new-line.test.ts.snap b/src/__tests__/__snapshots__/new-line.test.ts.snap index 3a622b2..e3081ef 100644 --- a/src/__tests__/__snapshots__/new-line.test.ts.snap +++ b/src/__tests__/__snapshots__/new-line.test.ts.snap @@ -19,12 +19,12 @@ Object { "type": "AddedLine", }, ], - "rangeAfter": Object { - "lines": 2, + "fromFileRange": Object { + "lines": 1, "start": 1, }, - "rangeBefore": Object { - "lines": 1, + "toFileRange": Object { + "lines": 2, "start": 1, }, "type": "Chunk", diff --git a/src/parse-git-diff.ts b/src/parse-git-diff.ts index 03fa2aa..f5d0e15 100644 --- a/src/parse-git-diff.ts +++ b/src/parse-git-diff.ts @@ -124,8 +124,8 @@ function parseChunk(context: Context): AnyChunk | undefined { if (chunkHeader.type === 'Normal') { const changes = parseChanges( context, - chunkHeader.rangeBefore, - chunkHeader.rangeAfter + chunkHeader.fromFileRange, + chunkHeader.toFileRange ); return { ...chunkHeader, @@ -134,15 +134,15 @@ function parseChunk(context: Context): AnyChunk | undefined { }; } else if ( chunkHeader.type === 'Combined' && - chunkHeader.rangeBeforeA && - chunkHeader.rangeBeforeB + chunkHeader.fromFileRangeA && + chunkHeader.fromFileRangeB ) { const changes = parseChanges( context, - chunkHeader.rangeBeforeA.start < chunkHeader.rangeBeforeB.start - ? chunkHeader.rangeBeforeA - : chunkHeader.rangeBeforeB, - chunkHeader.rangeAfter + chunkHeader.fromFileRangeA.start < chunkHeader.fromFileRangeB.start + ? chunkHeader.fromFileRangeA + : chunkHeader.fromFileRangeB, + chunkHeader.toFileRange ); return { ...chunkHeader, @@ -199,17 +199,17 @@ function parseChunkHeader(ctx: Context) { ctx.nextLine(); return { type: 'Combined', - rangeBeforeA: getRange(delStartA, delLinesA), - rangeBeforeB: getRange(delStartB, delLinesB), - rangeAfter: getRange(addStart, addLines), + fromFileRangeA: getRange(delStartA, delLinesA), + fromFileRangeB: getRange(delStartB, delLinesB), + toFileRange: getRange(addStart, addLines), } as const; } const [all, delStart, delLines, addStart, addLines] = normalChunkExec; ctx.nextLine(); return { type: 'Normal', - rangeAfter: getRange(addStart, addLines), - rangeBefore: getRange(delStart, delLines), + toFileRange: getRange(addStart, addLines), + fromFileRange: getRange(delStart, delLines), }; } diff --git a/src/types.ts b/src/types.ts index d485ce4..326f2ec 100644 --- a/src/types.ts +++ b/src/types.ts @@ -29,15 +29,15 @@ export interface ChunkRange { } export interface Chunk extends Base<'Chunk'> { - rangeBefore: ChunkRange; - rangeAfter: ChunkRange; + fromFileRange: ChunkRange; + toFileRange: ChunkRange; changes: AnyLineChange[]; } export interface CombinedChunk extends Base<'CombinedChunk'> { - rangeBeforeA: ChunkRange; - rangeBeforeB: ChunkRange; - rangeAfter: ChunkRange; + fromFileRangeA: ChunkRange; + fromFileRangeB: ChunkRange; + toFileRange: ChunkRange; changes: AnyLineChange[]; }