Move discardChanges git method to apply.ts

We use the first argument of the git command as the name of the file
This commit is contained in:
Rafael Oleza 2020-05-20 14:57:12 +02:00
parent f5086a9b71
commit 93a7f3269d
5 changed files with 269 additions and 274 deletions

View file

@ -4,10 +4,10 @@ import {
WorkingDirectoryFileChange,
AppFileStatusKind,
} from '../../models/status'
import { DiffType } from '../../models/diff'
import { DiffType, ITextDiff, DiffSelection } from '../../models/diff'
import { Repository, WorkingTree } from '../../models/repository'
import { getWorkingDirectoryDiff } from './diff'
import { formatPatch } from '../patch-formatter'
import { formatPatch, formatPatchToDiscardChanges } from '../patch-formatter'
export async function applyPatchToIndex(
repository: Repository,
@ -100,3 +100,39 @@ export async function checkPatch(
return true
}
/**
* Discards the local changes for the specified file based on the passed diff
* and a selection of lines from it.
*
* When passed an empty selection, this method won't do anything. When passed a
* full selection, all changes from the file will be discarded.
*
* @param repository The repository in which to update the working directory
* with information from the index
*
* @param filePath The relative path in the working directory of the file to use
*
* @param diff The diff containing the file local changes
*
* @param selection The selection of changes from the diff to discard
*/
export async function discardChangesFromSelection(
repository: Repository,
filePath: string,
diff: ITextDiff,
selection: DiffSelection
) {
const patch = formatPatchToDiscardChanges(filePath, diff, selection)
if (patch === null) {
// When the patch is null we don't need to apply it since it will be a noop.
return
}
const args = ['apply', '--unidiff-zero', '--whitespace=nowarn', '-']
await git(args, repository.path, 'discardChangesFromSelection', {
stdin: patch,
})
}

View file

@ -1,40 +0,0 @@
import { git } from './core'
import { Repository } from '../../models/repository'
import { DiffSelection, ITextDiff } from '../../models/diff'
import { formatPatchToDiscardChanges } from '../patch-formatter'
/**
* Discards the local changes for the specified file based on the passed diff
* and a selection of lines from it.
*
* When passed an empty selection, this method won't do anything. When passed a
* full selection, all changes from the file will be discarded.
*
* @param repository The repository in which to update the working directory
* with information from the index
*
* @param filePath The relative path in the working directory of the file to use
*
* @param diff The diff containing the file local changes
*
* @param selection The selection of changes from the diff to discard
*/
export async function discardChangesFromSelection(
repository: Repository,
filePath: string,
diff: ITextDiff,
selection: DiffSelection
) {
const patch = formatPatchToDiscardChanges(filePath, diff, selection)
if (patch === null) {
// When the patch is null we don't need to apply it since it will be a noop.
return
}
const args = ['apply', '--unidiff-zero', '--whitespace=nowarn', '-']
await git(args, repository.path, 'discardChangesFromSelection', {
stdin: patch,
})
}

View file

@ -35,4 +35,3 @@ export * from './rebase'
export * from './format-patch'
export * from './worktree'
export * from './tag'
export * from './discard-changes'

View file

@ -1,14 +1,34 @@
import { GitProcess } from 'dugite'
import { setupTwoCommitRepo } from '../../helpers/repositories'
import {
setupTwoCommitRepo,
setupFixtureRepository,
} from '../../helpers/repositories'
import { Repository } from '../../../src/models/repository'
import { checkPatch } from '../../../src/lib/git'
import {
checkPatch,
getWorkingDirectoryDiff,
discardChangesFromSelection,
} from '../../../src/lib/git'
import {
cloneLocalRepository,
makeCommit,
} from '../../helpers/repository-scaffolding'
import {
WorkingDirectoryFileChange,
AppFileStatusKind,
} from '../../../src/models/status'
import {
DiffSelection,
DiffSelectionType,
ITextDiff,
} from '../../../src/models/diff'
import { findInteractiveDiffRange } from '../../../src/ui/diff/diff-explorer'
import { diffStringsUnified } from 'jest-diff'
import * as FSE from 'fs-extra'
import * as Path from 'path'
describe('git/apply', () => {
describe('checkPatch', () => {
describe('checkPatch()', () => {
describe('on related repository without conflicts', () => {
let repository: Repository
let patch: string
@ -48,4 +68,212 @@ describe('git/apply', () => {
})
})
})
describe('discardChangesFromSelection()', () => {
let repository: Repository
let testRepoPath: string
async function getDiff(filePath: string) {
const file = new WorkingDirectoryFileChange(
filePath,
{ kind: AppFileStatusKind.Modified },
DiffSelection.fromInitialSelection(DiffSelectionType.None)
)
return (await getWorkingDirectoryDiff(repository, file)) as ITextDiff
}
beforeEach(async () => {
testRepoPath = await setupFixtureRepository('repo-with-changes')
repository = new Repository(testRepoPath, -1, null, false)
})
it('does not change the file when an empty selection is passed', async () => {
const filePath = 'modified-file.md'
const previousDiff = await getDiff(filePath)
await discardChangesFromSelection(
repository,
filePath,
previousDiff,
DiffSelection.fromInitialSelection(DiffSelectionType.None)
)
const diff = await getDiff(filePath)
expect(diff.text).toEqual(previousDiff.text)
})
it('discards all file changes when a full selection is passed', async () => {
const filePath = 'modified-file.md'
await discardChangesFromSelection(
repository,
filePath,
await getDiff(filePath),
DiffSelection.fromInitialSelection(DiffSelectionType.All)
)
const diff = await getDiff(filePath)
// Check that the file has no local changes.
expect(diff.text).toEqual('')
expect(diff.hunks).toEqual([])
})
it('re-adds a single removed line', async () => {
const filePath = 'modified-file.md'
const selection = DiffSelection.fromInitialSelection(
DiffSelectionType.None
).withLineSelection(4, true)
const previousContents = await FSE.readFile(
Path.join(repository.path, filePath),
'utf8'
)
await discardChangesFromSelection(
repository,
filePath,
await getDiff(filePath),
selection
)
const fileContents = await FSE.readFile(
Path.join(repository.path, filePath),
'utf8'
)
expect(getDifference(previousContents, fileContents))
.toMatchInlineSnapshot(`
"@@ -7,0 +7,1 @@
+ Aliquam leo ipsum, laoreet sed libero at, mollis pulvinar arcu. Nullam porttitor"
`)
})
it('re-adds a removed hunk', async () => {
const filePath = 'modified-file.md'
const diff = await getDiff(filePath)
const hunkRange = findInteractiveDiffRange(diff.hunks, 4)
const selection = DiffSelection.fromInitialSelection(
DiffSelectionType.None
).withRangeSelection(
hunkRange!.from,
hunkRange!.to - hunkRange!.from + 1,
true
)
const previousContents = await FSE.readFile(
Path.join(repository.path, filePath),
'utf8'
)
await discardChangesFromSelection(repository, filePath, diff, selection)
const fileContents = await FSE.readFile(
Path.join(repository.path, filePath),
'utf8'
)
expect(getDifference(previousContents, fileContents))
.toMatchInlineSnapshot(`
"@@ -7,0 +7,4 @@
+ Aliquam leo ipsum, laoreet sed libero at, mollis pulvinar arcu. Nullam porttitor
+ nisl eget hendrerit vestibulum. Curabitur ornare id neque ac tristique. Cras in
+ eleifend mi.
+"
`)
})
it('removes an added line', async () => {
const filePath = 'modified-file.md'
const selection = DiffSelection.fromInitialSelection(
DiffSelectionType.None
).withLineSelection(16, true)
const previousContents = await FSE.readFile(
Path.join(repository.path, filePath),
'utf8'
)
await discardChangesFromSelection(
repository,
filePath,
await getDiff(filePath),
selection
)
const fileContents = await FSE.readFile(
Path.join(repository.path, filePath),
'utf8'
)
expect(getDifference(previousContents, fileContents))
.toMatchInlineSnapshot(`
"@@ -21,1 +21,0 @@
- nisl eget hendrerit vestibulum. Curabitur ornare id neque ac tristique. Cras in"
`)
})
it('removes an added hunk', async () => {
const filePath = 'modified-file.md'
const diff = await getDiff(filePath)
const hunkRange = findInteractiveDiffRange(diff.hunks, 16)
const selection = DiffSelection.fromInitialSelection(
DiffSelectionType.None
).withRangeSelection(
hunkRange!.from,
hunkRange!.to - hunkRange!.from + 1,
true
)
console.log('diff', hunkRange)
const previousContents = await FSE.readFile(
Path.join(repository.path, filePath),
'utf8'
)
await discardChangesFromSelection(
repository,
filePath,
await getDiff(filePath),
selection
)
const fileContents = await FSE.readFile(
Path.join(repository.path, filePath),
'utf8'
)
expect(getDifference(previousContents, fileContents))
.toMatchInlineSnapshot(`
"@@ -20,4 +20,0 @@
- Aliquam leo ipsum, laoreet sed libero at, mollis pulvinar arcu. Nullam porttitor
- nisl eget hendrerit vestibulum. Curabitur ornare id neque ac tristique. Cras in
- eleifend mi.
-"
`)
})
})
})
const noColor = (str: string) => str
/**
* Returns a diff-style string with the line differences between two strings.
*/
function getDifference(before: string, after: string) {
return diffStringsUnified(
before.replace(/\r\n/g, '\n'),
after.replace(/\r\n/g, '\n'),
{
omitAnnotationLines: true,
contextLines: 0,
expand: false,
aColor: noColor,
bColor: noColor,
changeColor: noColor,
commonColor: noColor,
patchColor: noColor,
}
)
}

View file

@ -1,228 +0,0 @@
/* eslint-disable no-sync */
import { Repository } from '../../../src/models/repository'
import { setupFixtureRepository } from '../../helpers/repositories'
import {
DiffSelection,
DiffSelectionType,
ITextDiff,
} from '../../../src/models/diff'
import {
WorkingDirectoryFileChange,
AppFileStatusKind,
} from '../../../src/models/status'
import {
getWorkingDirectoryDiff,
discardChangesFromSelection,
} from '../../../src/lib/git'
import * as FSE from 'fs-extra'
import * as Path from 'path'
import { findInteractiveDiffRange } from '../../../src/ui/diff/diff-explorer'
import { diffStringsUnified } from 'jest-diff'
describe('discardChangesFromSelection()', () => {
let repository: Repository
let testRepoPath: string
async function getDiff(filePath: string) {
const file = new WorkingDirectoryFileChange(
filePath,
{ kind: AppFileStatusKind.Modified },
DiffSelection.fromInitialSelection(DiffSelectionType.None)
)
return (await getWorkingDirectoryDiff(repository, file)) as ITextDiff
}
beforeEach(async () => {
testRepoPath = await setupFixtureRepository('repo-with-changes')
repository = new Repository(testRepoPath, -1, null, false)
})
it('does not change the file when an empty selection is passed', async () => {
const filePath = 'modified-file.md'
const previousDiff = await getDiff(filePath)
await discardChangesFromSelection(
repository,
filePath,
previousDiff,
DiffSelection.fromInitialSelection(DiffSelectionType.None)
)
const diff = await getDiff(filePath)
expect(diff.text).toEqual(previousDiff.text)
})
it('discards all file changes when a full selection is passed', async () => {
const filePath = 'modified-file.md'
await discardChangesFromSelection(
repository,
filePath,
await getDiff(filePath),
DiffSelection.fromInitialSelection(DiffSelectionType.All)
)
const diff = await getDiff(filePath)
// Check that the file has no local changes.
expect(diff.text).toEqual('')
expect(diff.hunks).toEqual([])
})
it('re-adds a single removed line', async () => {
const filePath = 'modified-file.md'
const selection = DiffSelection.fromInitialSelection(
DiffSelectionType.None
).withLineSelection(4, true)
const previousContents = await FSE.readFile(
Path.join(repository.path, filePath),
'utf8'
)
await discardChangesFromSelection(
repository,
filePath,
await getDiff(filePath),
selection
)
const fileContents = await FSE.readFile(
Path.join(repository.path, filePath),
'utf8'
)
expect(getDifference(previousContents, fileContents))
.toMatchInlineSnapshot(`
"@@ -7,0 +7,1 @@
+ Aliquam leo ipsum, laoreet sed libero at, mollis pulvinar arcu. Nullam porttitor"
`)
})
it('re-adds a removed hunk', async () => {
const filePath = 'modified-file.md'
const diff = await getDiff(filePath)
const hunkRange = findInteractiveDiffRange(diff.hunks, 4)
const selection = DiffSelection.fromInitialSelection(
DiffSelectionType.None
).withRangeSelection(
hunkRange!.from,
hunkRange!.to - hunkRange!.from + 1,
true
)
const previousContents = await FSE.readFile(
Path.join(repository.path, filePath),
'utf8'
)
await discardChangesFromSelection(repository, filePath, diff, selection)
const fileContents = await FSE.readFile(
Path.join(repository.path, filePath),
'utf8'
)
expect(getDifference(previousContents, fileContents))
.toMatchInlineSnapshot(`
"@@ -7,0 +7,4 @@
+ Aliquam leo ipsum, laoreet sed libero at, mollis pulvinar arcu. Nullam porttitor
+ nisl eget hendrerit vestibulum. Curabitur ornare id neque ac tristique. Cras in
+ eleifend mi.
+"
`)
})
it('removes an added line', async () => {
const filePath = 'modified-file.md'
const selection = DiffSelection.fromInitialSelection(
DiffSelectionType.None
).withLineSelection(16, true)
const previousContents = await FSE.readFile(
Path.join(repository.path, filePath),
'utf8'
)
await discardChangesFromSelection(
repository,
filePath,
await getDiff(filePath),
selection
)
const fileContents = await FSE.readFile(
Path.join(repository.path, filePath),
'utf8'
)
expect(getDifference(previousContents, fileContents))
.toMatchInlineSnapshot(`
"@@ -21,1 +21,0 @@
- nisl eget hendrerit vestibulum. Curabitur ornare id neque ac tristique. Cras in"
`)
})
it('removes an added hunk', async () => {
const filePath = 'modified-file.md'
const diff = await getDiff(filePath)
const hunkRange = findInteractiveDiffRange(diff.hunks, 16)
const selection = DiffSelection.fromInitialSelection(
DiffSelectionType.None
).withRangeSelection(
hunkRange!.from,
hunkRange!.to - hunkRange!.from + 1,
true
)
console.log('diff', hunkRange)
const previousContents = await FSE.readFile(
Path.join(repository.path, filePath),
'utf8'
)
await discardChangesFromSelection(
repository,
filePath,
await getDiff(filePath),
selection
)
const fileContents = await FSE.readFile(
Path.join(repository.path, filePath),
'utf8'
)
expect(getDifference(previousContents, fileContents))
.toMatchInlineSnapshot(`
"@@ -20,4 +20,0 @@
- Aliquam leo ipsum, laoreet sed libero at, mollis pulvinar arcu. Nullam porttitor
- nisl eget hendrerit vestibulum. Curabitur ornare id neque ac tristique. Cras in
- eleifend mi.
-"
`)
})
})
const noColor = (str: string) => str
/**
* Returns a diff-style string with the line differences between two strings.
*/
function getDifference(before: string, after: string) {
return diffStringsUnified(
before.replace(/\r\n/g, '\n'),
after.replace(/\r\n/g, '\n'),
{
omitAnnotationLines: true,
contextLines: 0,
expand: false,
aColor: noColor,
bColor: noColor,
changeColor: noColor,
commonColor: noColor,
patchColor: noColor,
}
)
}