Compare commits
2 commits
dev
...
fix/snapsh
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ab0d6a6c9 | ||
|
|
1f279cd2c8 |
2 changed files with 53 additions and 102 deletions
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"nodeModules": {
|
"nodeModules": {
|
||||||
"x86_64-linux": "sha256-3VYF84QGROFpwBCYDEWHDpRFkSHwmiVWQJR81/bqjYo=",
|
"x86_64-linux": "sha256-gdS7MkWGeVO0qLs0HKD156YE0uCk5vWeYjKu4JR1Apw=",
|
||||||
"aarch64-linux": "sha256-k12n4GqrjBqKqBvLjzXQhDxbc8ZMZ/1TenDp2pKh888=",
|
"aarch64-linux": "sha256-tF4pyVqzbrvdkRG23Fot37FCg8guRZkcU738fHPr/OQ=",
|
||||||
"aarch64-darwin": "sha256-OCRX1VC5SJmrXk7whl6bsdmlRwjARGw+4RSk8c59N10=",
|
"aarch64-darwin": "sha256-FugTWzGMb2ktAbNwQvWRM3GWOb5RTR++8EocDDrQMLc=",
|
||||||
"x86_64-darwin": "sha256-l+g/cMREarOVIK3a01+csC3Mk3ZfMVWAiAosSA5/U6Y="
|
"x86_64-darwin": "sha256-jpe6EiwKr+CS00cn0eHwcDluO4LvO3t/5l/LcFBBKP0="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,11 +91,15 @@ export namespace Snapshot {
|
||||||
const args = (cmd: string[]) => ["--git-dir", state.gitdir, "--work-tree", state.worktree, ...cmd]
|
const args = (cmd: string[]) => ["--git-dir", state.gitdir, "--work-tree", state.worktree, ...cmd]
|
||||||
|
|
||||||
const git = Effect.fnUntraced(
|
const git = Effect.fnUntraced(
|
||||||
function* (cmd: string[], opts?: { cwd?: string; env?: Record<string, string> }) {
|
function* (
|
||||||
|
cmd: string[],
|
||||||
|
opts?: { cwd?: string; env?: Record<string, string>; stdin?: Stream.Stream<Uint8Array> },
|
||||||
|
) {
|
||||||
const proc = ChildProcess.make("git", cmd, {
|
const proc = ChildProcess.make("git", cmd, {
|
||||||
cwd: opts?.cwd,
|
cwd: opts?.cwd,
|
||||||
env: opts?.env,
|
env: opts?.env,
|
||||||
extendEnv: true,
|
extendEnv: true,
|
||||||
|
stdin: opts?.stdin ?? "ignore",
|
||||||
})
|
})
|
||||||
const handle = yield* spawner.spawn(proc)
|
const handle = yield* spawner.spawn(proc)
|
||||||
const [text, stderr] = yield* Effect.all(
|
const [text, stderr] = yield* Effect.all(
|
||||||
|
|
@ -150,65 +154,26 @@ export namespace Snapshot {
|
||||||
|
|
||||||
const add = Effect.fnUntraced(function* () {
|
const add = Effect.fnUntraced(function* () {
|
||||||
yield* sync()
|
yield* sync()
|
||||||
const [diff, other] = yield* Effect.all(
|
const list = yield* git(
|
||||||
[
|
[
|
||||||
git([...quote, ...args(["diff-files", "--name-only", "-z", "--", "."])], {
|
...quote,
|
||||||
cwd: state.directory,
|
...args(["ls-files", "--modified", "--deleted", "--others", "--exclude-standard", "-z", "--", "."]),
|
||||||
}),
|
|
||||||
git([...quote, ...args(["ls-files", "--others", "--exclude-standard", "-z", "--", "."])], {
|
|
||||||
cwd: state.directory,
|
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
{ concurrency: 2 },
|
{ cwd: state.directory },
|
||||||
)
|
)
|
||||||
if (diff.code !== 0 || other.code !== 0) {
|
if (list.code !== 0) {
|
||||||
log.warn("failed to list snapshot files", {
|
log.warn("failed to list snapshot files", {
|
||||||
diffCode: diff.code,
|
exitCode: list.code,
|
||||||
diffStderr: diff.stderr,
|
stderr: list.stderr,
|
||||||
otherCode: other.code,
|
|
||||||
otherStderr: other.stderr,
|
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const tracked = diff.text.split("\0").filter(Boolean)
|
const files = list.text.split("\0").filter(Boolean)
|
||||||
const untracked = other.text.split("\0").filter(Boolean)
|
if (!files.length) return
|
||||||
const all = Array.from(new Set([...tracked, ...untracked]))
|
|
||||||
if (!all.length) return
|
|
||||||
|
|
||||||
// Filter out files that are now gitignored even if previously tracked
|
|
||||||
// Files may have been tracked before being gitignored, so we need to check
|
|
||||||
// against the source project's current gitignore rules
|
|
||||||
// Use --no-index to check purely against patterns (ignoring whether file is tracked)
|
|
||||||
const checkArgs = [
|
|
||||||
...quote,
|
|
||||||
"--git-dir",
|
|
||||||
path.join(state.worktree, ".git"),
|
|
||||||
"--work-tree",
|
|
||||||
state.worktree,
|
|
||||||
"check-ignore",
|
|
||||||
"--no-index",
|
|
||||||
"--",
|
|
||||||
...all,
|
|
||||||
]
|
|
||||||
const check = yield* git(checkArgs, { cwd: state.directory })
|
|
||||||
const ignored =
|
|
||||||
check.code === 0 ? new Set(check.text.trim().split("\n").filter(Boolean)) : new Set<string>()
|
|
||||||
const filtered = all.filter((item) => !ignored.has(item))
|
|
||||||
|
|
||||||
// Remove newly-ignored files from snapshot index to prevent re-adding
|
|
||||||
if (ignored.size > 0) {
|
|
||||||
const ignoredFiles = Array.from(ignored)
|
|
||||||
log.info("removing gitignored files from snapshot", { count: ignoredFiles.length })
|
|
||||||
yield* git([...cfg, ...args(["rm", "--cached", "-f", "--", ...ignoredFiles])], {
|
|
||||||
cwd: state.directory,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!filtered.length) return
|
|
||||||
|
|
||||||
const large = (yield* Effect.all(
|
const large = (yield* Effect.all(
|
||||||
filtered.map((item) =>
|
files.map((item) =>
|
||||||
fs
|
fs
|
||||||
.stat(path.join(state.directory, item))
|
.stat(path.join(state.directory, item))
|
||||||
.pipe(Effect.catch(() => Effect.void))
|
.pipe(Effect.catch(() => Effect.void))
|
||||||
|
|
@ -223,12 +188,45 @@ export namespace Snapshot {
|
||||||
{ concurrency: 8 },
|
{ concurrency: 8 },
|
||||||
)).filter((item): item is string => Boolean(item))
|
)).filter((item): item is string => Boolean(item))
|
||||||
yield* sync(large)
|
yield* sync(large)
|
||||||
const result = yield* git([...cfg, ...args(["add", "--sparse", "."])], { cwd: state.directory })
|
|
||||||
|
// Snapshot the current index before git add mutates it.
|
||||||
|
// Tracked files that are now ignored still get staged by git add;
|
||||||
|
// we reset just those entries back to this tree afterward.
|
||||||
|
const [prev, keep] = yield* Effect.all(
|
||||||
|
[
|
||||||
|
git(args(["write-tree"]), { cwd: state.directory }),
|
||||||
|
git([...quote, ...args(["ls-files", "-ci", "--exclude-standard", "-z", "--", "."])], {
|
||||||
|
cwd: state.directory,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
{ concurrency: 2 },
|
||||||
|
)
|
||||||
|
|
||||||
|
const result = yield* git([...cfg, ...args(["add", "--all", "--sparse", "."])], { cwd: state.directory })
|
||||||
if (result.code !== 0) {
|
if (result.code !== 0) {
|
||||||
log.warn("failed to add snapshot files", {
|
log.warn("failed to add snapshot files", {
|
||||||
exitCode: result.code,
|
exitCode: result.code,
|
||||||
stderr: result.stderr,
|
stderr: result.stderr,
|
||||||
})
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const base = prev.code === 0 ? prev.text.trim() : ""
|
||||||
|
const cached = keep.code === 0 ? keep.text : ""
|
||||||
|
if (!base || !cached) return
|
||||||
|
|
||||||
|
const reset = yield* git(
|
||||||
|
[...cfg, ...args(["reset", "-q", "--pathspec-from-file=-", "--pathspec-file-nul", base])],
|
||||||
|
{
|
||||||
|
cwd: state.directory,
|
||||||
|
stdin: Stream.make(new TextEncoder().encode(cached)),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if (reset.code !== 0) {
|
||||||
|
log.warn("failed to reset ignored snapshot files", {
|
||||||
|
exitCode: reset.code,
|
||||||
|
stderr: reset.stderr,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -295,30 +293,6 @@ export namespace Snapshot {
|
||||||
.map((x) => x.trim())
|
.map((x) => x.trim())
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
|
|
||||||
// Filter out files that are now gitignored
|
|
||||||
if (files.length > 0) {
|
|
||||||
const checkArgs = [
|
|
||||||
...quote,
|
|
||||||
"--git-dir",
|
|
||||||
path.join(state.worktree, ".git"),
|
|
||||||
"--work-tree",
|
|
||||||
state.worktree,
|
|
||||||
"check-ignore",
|
|
||||||
"--no-index",
|
|
||||||
"--",
|
|
||||||
...files,
|
|
||||||
]
|
|
||||||
const check = yield* git(checkArgs, { cwd: state.directory })
|
|
||||||
if (check.code === 0) {
|
|
||||||
const ignored = new Set(check.text.trim().split("\n").filter(Boolean))
|
|
||||||
const filtered = files.filter((item) => !ignored.has(item))
|
|
||||||
return {
|
|
||||||
hash,
|
|
||||||
files: filtered.map((x) => path.join(state.worktree, x).replaceAll("\\", "/")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hash,
|
hash,
|
||||||
files: files.map((x) => path.join(state.worktree, x).replaceAll("\\", "/")),
|
files: files.map((x) => path.join(state.worktree, x).replaceAll("\\", "/")),
|
||||||
|
|
@ -672,29 +646,6 @@ export namespace Snapshot {
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
// Filter out files that are now gitignored
|
|
||||||
if (rows.length > 0) {
|
|
||||||
const files = rows.map((r) => r.file)
|
|
||||||
const checkArgs = [
|
|
||||||
...quote,
|
|
||||||
"--git-dir",
|
|
||||||
path.join(state.worktree, ".git"),
|
|
||||||
"--work-tree",
|
|
||||||
state.worktree,
|
|
||||||
"check-ignore",
|
|
||||||
"--no-index",
|
|
||||||
"--",
|
|
||||||
...files,
|
|
||||||
]
|
|
||||||
const check = yield* git(checkArgs, { cwd: state.directory })
|
|
||||||
if (check.code === 0) {
|
|
||||||
const ignored = new Set(check.text.trim().split("\n").filter(Boolean))
|
|
||||||
const filtered = rows.filter((r) => !ignored.has(r.file))
|
|
||||||
rows.length = 0
|
|
||||||
rows.push(...filtered)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const step = 100
|
const step = 100
|
||||||
const patch = (file: string, before: string, after: string) =>
|
const patch = (file: string, before: string, after: string) =>
|
||||||
formatPatch(structuredPatch(file, file, before, after, "", "", { context: Number.MAX_SAFE_INTEGER }))
|
formatPatch(structuredPatch(file, file, before, after, "", "", { context: Number.MAX_SAFE_INTEGER }))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue