From 3c2eabf7aac7ed3cdf3e132ca85804e9b5f6d323 Mon Sep 17 00:00:00 2001 From: Steve King Date: Sat, 23 Apr 2022 11:01:11 +0100 Subject: [PATCH 1/3] Resolve issue whereby renamed files no longer appear in response to `git.status` --- simple-git/src/lib/responses/StatusSummary.ts | 22 +++++++++---------- simple-git/test/integration/status.spec.ts | 9 ++++++++ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/simple-git/src/lib/responses/StatusSummary.ts b/simple-git/src/lib/responses/StatusSummary.ts index 54fa77d7..c85ba0d5 100644 --- a/simple-git/src/lib/responses/StatusSummary.ts +++ b/simple-git/src/lib/responses/StatusSummary.ts @@ -38,17 +38,11 @@ enum PorcelainFileStatus { } function renamedFile(line: string) { - const detail = /^(.+) -> (.+)$/.exec(line); - - if (!detail) { - return { - from: line, to: line - }; - } + const [to = line, from = line] = line.split(NULL); return { - from: String(detail[1]), - to: String(detail[2]), + from, + to, }; } @@ -123,8 +117,14 @@ export const parseStatusSummary = function (text: string): StatusResult { const lines = text.trim().split(NULL); const status = new StatusSummary(); - for (let i = 0, l = lines.length; i < l; i++) { - splitLine(status, lines[i]); + for (let i = 0, l = lines.length; i < l;) { + let line = lines[i++]; + + if (line.charAt(0) === PorcelainFileStatus.RENAMED) { + line += NULL + lines[i++]; + } + + splitLine(status, line); } return status; diff --git a/simple-git/test/integration/status.spec.ts b/simple-git/test/integration/status.spec.ts index 4f5f24c5..a76184ee 100644 --- a/simple-git/test/integration/status.spec.ts +++ b/simple-git/test/integration/status.spec.ts @@ -18,6 +18,15 @@ describe('status', () => { await setUpFilesAdded(context, ['alpha', 'beta'], ['alpha', 'beta', './clean-dir']); }); + it('detects renamed files', async () => { + await context.git.raw('mv', 'alpha', 'gamma'); + const status = await context.git.status(); + + expect(status.renamed).toEqual([ + {from: 'alpha', to: 'gamma'}, + ]); + }); + it('whole repo status', async () => { const status = await newSimpleGit(context.root).status(); expect(status).toHaveProperty('not_added', ['dirty-dir/dirty']); From adb4346cc23918da3416cd2abc8300a700c692f4 Mon Sep 17 00:00:00 2001 From: Steve King Date: Sat, 23 Apr 2022 11:04:01 +0100 Subject: [PATCH 2/3] Changeset --- .changeset/growing-off-carpet.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/growing-off-carpet.md diff --git a/.changeset/growing-off-carpet.md b/.changeset/growing-off-carpet.md new file mode 100644 index 00000000..33712525 --- /dev/null +++ b/.changeset/growing-off-carpet.md @@ -0,0 +1,5 @@ +--- +"simple-git": patch +--- + +Resolves issue whereby renamed files no longer appear correctly in the response to `git.status`. From 5939bd786f436eba33249a2628d6772ec9905e25 Mon Sep 17 00:00:00 2001 From: Steve King Date: Sat, 23 Apr 2022 16:50:54 +0100 Subject: [PATCH 3/3] Support renamed on work-tree, add test for `status.files` content of a renamed file --- .../src/lib/responses/FileStatusSummary.ts | 2 +- simple-git/src/lib/responses/StatusSummary.ts | 16 ++++++++++------ simple-git/test/integration/status.spec.ts | 12 +++++++++--- .../test/unit/__fixtures__/responses/status.ts | 2 +- simple-git/test/unit/status.spec.ts | 17 +++++++++-------- 5 files changed, 30 insertions(+), 19 deletions(-) diff --git a/simple-git/src/lib/responses/FileStatusSummary.ts b/simple-git/src/lib/responses/FileStatusSummary.ts index 4bf0f851..bb5faf50 100644 --- a/simple-git/src/lib/responses/FileStatusSummary.ts +++ b/simple-git/src/lib/responses/FileStatusSummary.ts @@ -1,4 +1,4 @@ -import { FileStatusResult } from '../../../typings/response'; +import { FileStatusResult } from '../../../typings'; export const fromPathRegex = /^(.+) -> (.+)$/; diff --git a/simple-git/src/lib/responses/StatusSummary.ts b/simple-git/src/lib/responses/StatusSummary.ts index c85ba0d5..56fdc9bb 100644 --- a/simple-git/src/lib/responses/StatusSummary.ts +++ b/simple-git/src/lib/responses/StatusSummary.ts @@ -38,10 +38,10 @@ enum PorcelainFileStatus { } function renamedFile(line: string) { - const [to = line, from = line] = line.split(NULL); + const [to, from] = line.split(NULL); return { - from, + from: from || to, to, }; } @@ -114,14 +114,18 @@ const parsers: Map = new Map([ ]); export const parseStatusSummary = function (text: string): StatusResult { - const lines = text.trim().split(NULL); + const lines = text.split(NULL); const status = new StatusSummary(); for (let i = 0, l = lines.length; i < l;) { - let line = lines[i++]; + let line = lines[i++].trim(); + + if (!line) { + continue; + } if (line.charAt(0) === PorcelainFileStatus.RENAMED) { - line += NULL + lines[i++]; + line += NULL + (lines[i++] || ''); } splitLine(status, line); @@ -150,7 +154,7 @@ function splitLine(result: StatusResult, lineStr: string) { } if (raw !== '##' && raw !== '!!') { - result.files.push(new FileStatusSummary(path, index, workingDir)); + result.files.push(new FileStatusSummary(path.replace(/\0.+$/, ''), index, workingDir)); } } } diff --git a/simple-git/test/integration/status.spec.ts b/simple-git/test/integration/status.spec.ts index a76184ee..54e514e0 100644 --- a/simple-git/test/integration/status.spec.ts +++ b/simple-git/test/integration/status.spec.ts @@ -22,9 +22,15 @@ describe('status', () => { await context.git.raw('mv', 'alpha', 'gamma'); const status = await context.git.status(); - expect(status.renamed).toEqual([ - {from: 'alpha', to: 'gamma'}, - ]); + expect(status).toEqual(like({ + files: [ + like({path: 'gamma'}), + like({path: 'dirty-dir/dirty'}), + ], + renamed: [ + {from: 'alpha', to: 'gamma'}, + ] + })); }); it('whole repo status', async () => { diff --git a/simple-git/test/unit/__fixtures__/responses/status.ts b/simple-git/test/unit/__fixtures__/responses/status.ts index 5a58692a..c2671ad5 100644 --- a/simple-git/test/unit/__fixtures__/responses/status.ts +++ b/simple-git/test/unit/__fixtures__/responses/status.ts @@ -2,7 +2,7 @@ import { createFixture } from '../create-fixture'; import { NULL } from '../../../../src/lib/utils'; export function stagedRenamed(from = 'from.ext', to = 'to.ext', workingDir = ' ') { - return `R${workingDir} ${from} -> ${to}`; + return `R${workingDir} ${to}${NULL}${from}`; } export function stagedRenamedWithModifications(from = 'from.ext', to = 'to.ext') { diff --git a/simple-git/test/unit/status.spec.ts b/simple-git/test/unit/status.spec.ts index dc085f70..47e268cd 100644 --- a/simple-git/test/unit/status.spec.ts +++ b/simple-git/test/unit/status.spec.ts @@ -229,7 +229,8 @@ describe('status', () => { const statusSummary = parseStatusSummary(statusResponse('master', ' M other.txt', 'A src/b.txt', - 'R src/a.txt -> src/c.txt').stdOut); + stagedRenamed('src/a.txt', 'src/c.txt'), + ).stdOut); expect(statusSummary).toEqual(like({ created: ['src/b.txt'], @@ -239,7 +240,7 @@ describe('status', () => { }); it('Handles renamed', () => { - expect(parseStatusSummary(' R src/file.js -> src/another-file.js')).toEqual(like({ + expect(parseStatusSummary(` R src/another-file.js${NULL}src/file.js`)).toEqual(like({ renamed: [{from: 'src/file.js', to: 'src/another-file.js'}], })); }); @@ -283,12 +284,12 @@ describe('status', () => { }); it.each<[string, any]>([ - ['?? Not tracked File', { not_added: ['Not tracked File'] }], - ['UU Conflicted', { conflicted: ['Conflicted'] }], - [' D Removed', { deleted: ['Removed'] }], - [' M Modified', { modified: ['Modified'] }], - [' A Added', { created: ['Added'] }], - ['AM Changed', { created: ['Changed'], modified: ['Changed'] }], + ['?? Not tracked File', {not_added: ['Not tracked File']}], + ['UU Conflicted', {conflicted: ['Conflicted']}], + [' D Removed', {deleted: ['Removed']}], + [' M Modified', {modified: ['Modified']}], + [' A Added', {created: ['Added']}], + ['AM Changed', {created: ['Changed'], modified: ['Changed']}], ])('parses file status - %s', (file, result) => { expect(parseStatusSummary(statusResponse('branch', file).stdOut)).toEqual(like({ modified: [],