From 5c55aade25a8d15b8100b98637069195e6d020fc Mon Sep 17 00:00:00 2001 From: Kevin Vlaanderen Date: Thu, 14 Jun 2018 21:45:35 +0200 Subject: [PATCH 1/6] Only copy over local files that match the inclusion/exclusion criteria This way, can avoid the reset mechanism. This mechanism is very expensive, as a new process is spawned to reset every ignored file (one process per file) --- src/makePatch.ts | 34 ++++++++++----------- src/patch/reverse.ts | 70 +++++++++++++++++++++++--------------------- 2 files changed, 51 insertions(+), 53 deletions(-) diff --git a/src/makePatch.ts b/src/makePatch.ts index 7763c3f1..e9eddc16 100644 --- a/src/makePatch.ts +++ b/src/makePatch.ts @@ -127,35 +127,31 @@ export default function makePatch( // replace package with user's version rimraf.sync(tmpRepoPackagePath) - fsExtra.copySync(packagePath, tmpRepoPackagePath, { recursive: true }) + + fsExtra.copySync(packagePath, tmpRepoPackagePath, { + recursive: true, + filter: src => { + const scopedFileName = src.slice(`${packagePath}/`.length) + const matchingInclude = scopedFileName.match(includePaths) + const matchingExclude = scopedFileName.match(excludePaths) + return ( + matchingInclude !== null && + matchingInclude.length > 0 && + !(matchingExclude !== null && matchingExclude.length > 0) + ) + }, + }) // stage all files tmpExec("git", ["add", "-f", slash(path.join("node_modules", packageName))]) - // unstage any ignored files so they don't show up in the diff - tmpExec("git", ["diff", "--cached", "--name-only"]) - .stdout.toString() - .split(/\r?\n/) - .filter(Boolean) - .forEach(fileName => { - const scopedFileName = fileName.slice( - `node_modules/${packageName}/`.length, - ) - if ( - !scopedFileName.match(includePaths) || - scopedFileName.match(excludePaths) - ) { - tmpExec("git", ["reset", "HEAD", fileName]) - } - }) - // get diff of changes const patch = tmpExec("git", [ "diff", "--cached", "--no-color", "--ignore-space-at-eol", - "--no-ext-diff" + "--no-ext-diff", ]).stdout.toString() if (patch.trim() === "") { diff --git a/src/patch/reverse.ts b/src/patch/reverse.ts index 0471e7c2..536a81d3 100644 --- a/src/patch/reverse.ts +++ b/src/patch/reverse.ts @@ -44,39 +44,41 @@ function reverseHunks(hunks: PatchHunk[]): PatchHunk[] { export function reversePatch(patch: ParsedPatchFile): ParsedPatchFile { return patch - .map((part: PatchFilePart): PatchFilePart => { - switch (part.type) { - case "file creation": - return { - type: "file deletion", - path: part.path, - lines: part.lines, - mode: part.mode, - noNewlineAtEndOfFile: part.noNewlineAtEndOfFile, - } - case "file deletion": - return { - type: "file creation", - path: part.path, - lines: part.lines, - mode: part.mode, - noNewlineAtEndOfFile: part.noNewlineAtEndOfFile, - } - case "rename": - return { - type: "rename", - fromPath: part.toPath, - toPath: part.fromPath, - } - case "patch": - return { - type: "patch", - path: part.path, - parts: reverseHunks(part.parts), - } - default: - throw new Error() - } - }) + .map( + (part: PatchFilePart): PatchFilePart => { + switch (part.type) { + case "file creation": + return { + type: "file deletion", + path: part.path, + lines: part.lines, + mode: part.mode, + noNewlineAtEndOfFile: part.noNewlineAtEndOfFile, + } + case "file deletion": + return { + type: "file creation", + path: part.path, + lines: part.lines, + mode: part.mode, + noNewlineAtEndOfFile: part.noNewlineAtEndOfFile, + } + case "rename": + return { + type: "rename", + fromPath: part.toPath, + toPath: part.fromPath, + } + case "patch": + return { + type: "patch", + path: part.path, + parts: reverseHunks(part.parts), + } + default: + throw new Error() + } + }, + ) .reverse() } From ddb745d15ba5b391028e33b5e98f8386610be5f9 Mon Sep 17 00:00:00 2001 From: Kevin Vlaanderen Date: Fri, 15 Jun 2018 12:15:25 +0200 Subject: [PATCH 2/6] Add klaw-sync, for recursive directory traversal --- package.json | 1 + yarn.lock | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/package.json b/package.json index 4354df3a..ae261d97 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "chalk": "^1.1.3", "cross-spawn": "^5.1.0", "fs-extra": "^4.0.1", + "klaw-sync": "^4.0.0", "minimist": "^1.2.0", "rimraf": "^2.6.2", "slash": "^1.0.0", diff --git a/yarn.lock b/yarn.lock index a197e4c4..7babcc24 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1949,6 +1949,12 @@ kind-of@^3.0.2: dependencies: is-buffer "^1.1.5" +klaw-sync@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/klaw-sync/-/klaw-sync-4.0.0.tgz#7785692ea1a320ac3dda7a6c0c22b33a30aa3b3f" + dependencies: + graceful-fs "^4.1.11" + latest-version@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15" From 7cbe1f52255c62fdcea14a0704e00393165f8134 Mon Sep 17 00:00:00 2001 From: Kevin Vlaanderen Date: Fri, 15 Jun 2018 12:16:33 +0200 Subject: [PATCH 3/6] Add custom typings for klaw-sync --- typings/klaw-sync.d.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 typings/klaw-sync.d.ts diff --git a/typings/klaw-sync.d.ts b/typings/klaw-sync.d.ts new file mode 100644 index 00000000..281904be --- /dev/null +++ b/typings/klaw-sync.d.ts @@ -0,0 +1,17 @@ +interface Options { + nodir?: boolean + nofile?: boolean + depthLimit?: number + fs?: object + filter?: (item: Item) => boolean +} + +interface Item { + path: string + stats: object +} + +declare module "klaw-sync" { + const klawSync: (dir: string, opts?: Options, ls?: Item[]) => Item[] + export = klawSync +} From 79230f99dbd8261f05fcb62b43fca4a3f1fec303 Mon Sep 17 00:00:00 2001 From: Kevin Vlaanderen Date: Fri, 15 Jun 2018 12:20:44 +0200 Subject: [PATCH 4/6] Only process files that match our criteria --- src/makePatch.ts | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/src/makePatch.ts b/src/makePatch.ts index e9eddc16..599d6293 100644 --- a/src/makePatch.ts +++ b/src/makePatch.ts @@ -12,6 +12,7 @@ import { getPatchFiles } from "./patchFs" import * as fsExtra from "fs-extra" import { PackageManager } from "./detectPackageManager" import * as slash from "slash" +import * as klawSync from "klaw-sync" function deleteScripts(json: any) { delete json.scripts @@ -120,27 +121,35 @@ export default function makePatch( "!/node_modules\n\n", ) tmpExec("git", ["init"]) - // don't commit package.json though - tmpExec("git", ["add", "-f", slash(path.join("node_modules", packageName))]) + klawSync(tmpRepoPackagePath, { nodir: true }) + .map(item => item.path.slice(`${tmpRepoPackagePath}/`.length)) + .filter( + relativePath => + !relativePath.match(includePaths) || relativePath.match(excludePaths), + ) + .forEach(relativePath => + fsExtra.removeSync(slash(path.join(tmpRepoPackagePath, relativePath))), + ) + + tmpExec("git", ["add", "-f", slash(path.join("node_modules", packageName))]) tmpExec("git", ["commit", "-m", "init"]) // replace package with user's version rimraf.sync(tmpRepoPackagePath) - fsExtra.copySync(packagePath, tmpRepoPackagePath, { - recursive: true, - filter: src => { - const scopedFileName = src.slice(`${packagePath}/`.length) - const matchingInclude = scopedFileName.match(includePaths) - const matchingExclude = scopedFileName.match(excludePaths) - return ( - matchingInclude !== null && - matchingInclude.length > 0 && - !(matchingExclude !== null && matchingExclude.length > 0) - ) - }, - }) + klawSync(packagePath, { nodir: true }) + .map(item => item.path.slice(`${packagePath}/`.length)) + .filter( + relativePath => + relativePath.match(includePaths) && !relativePath.match(excludePaths), + ) + .forEach(relativePath => + fsExtra.copySync( + slash(path.join(packagePath, relativePath)), + slash(path.join(tmpRepoPackagePath, relativePath)), + ), + ) // stage all files tmpExec("git", ["add", "-f", slash(path.join("node_modules", packageName))]) From 87aec76bac2eb8fb73de0e4b7e679a458798bbd5 Mon Sep 17 00:00:00 2001 From: Kevin Vlaanderen Date: Fri, 15 Jun 2018 12:21:09 +0200 Subject: [PATCH 5/6] Formatting --- src/patch/reverse.ts | 70 +++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/src/patch/reverse.ts b/src/patch/reverse.ts index 536a81d3..0471e7c2 100644 --- a/src/patch/reverse.ts +++ b/src/patch/reverse.ts @@ -44,41 +44,39 @@ function reverseHunks(hunks: PatchHunk[]): PatchHunk[] { export function reversePatch(patch: ParsedPatchFile): ParsedPatchFile { return patch - .map( - (part: PatchFilePart): PatchFilePart => { - switch (part.type) { - case "file creation": - return { - type: "file deletion", - path: part.path, - lines: part.lines, - mode: part.mode, - noNewlineAtEndOfFile: part.noNewlineAtEndOfFile, - } - case "file deletion": - return { - type: "file creation", - path: part.path, - lines: part.lines, - mode: part.mode, - noNewlineAtEndOfFile: part.noNewlineAtEndOfFile, - } - case "rename": - return { - type: "rename", - fromPath: part.toPath, - toPath: part.fromPath, - } - case "patch": - return { - type: "patch", - path: part.path, - parts: reverseHunks(part.parts), - } - default: - throw new Error() - } - }, - ) + .map((part: PatchFilePart): PatchFilePart => { + switch (part.type) { + case "file creation": + return { + type: "file deletion", + path: part.path, + lines: part.lines, + mode: part.mode, + noNewlineAtEndOfFile: part.noNewlineAtEndOfFile, + } + case "file deletion": + return { + type: "file creation", + path: part.path, + lines: part.lines, + mode: part.mode, + noNewlineAtEndOfFile: part.noNewlineAtEndOfFile, + } + case "rename": + return { + type: "rename", + fromPath: part.toPath, + toPath: part.fromPath, + } + case "patch": + return { + type: "patch", + path: part.path, + parts: reverseHunks(part.parts), + } + default: + throw new Error() + } + }) .reverse() } From 7832d164362195fb00cefa56de22429d7ffbe70b Mon Sep 17 00:00:00 2001 From: Kevin Vlaanderen Date: Fri, 15 Jun 2018 14:47:34 +0200 Subject: [PATCH 6/6] Allow empty initial commit, to prevent a crash when passing very strict include/exclude rules --- src/makePatch.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/makePatch.ts b/src/makePatch.ts index 599d6293..cce0deee 100644 --- a/src/makePatch.ts +++ b/src/makePatch.ts @@ -133,7 +133,7 @@ export default function makePatch( ) tmpExec("git", ["add", "-f", slash(path.join("node_modules", packageName))]) - tmpExec("git", ["commit", "-m", "init"]) + tmpExec("git", ["commit", "--allow-empty", "-m", "init"]) // replace package with user's version rimraf.sync(tmpRepoPackagePath)