diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d0a1a9666b..4cb4e02e505 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Git hooks: skip ignored staged paths when formatting and restaging pre-commit files, so merge commits no longer abort when `.gitignore` newly ignores staged merged content. Fixes #72744. Thanks @100yenadmin. - Memory-core/dreaming: add a supported `dreaming.model` knob for Dream Diary narrative subagents, wired through phase config and the existing plugin subagent model-override trust gate. Refs #65963. Thanks @esqandil and @mjamiv. - Memory-core/dreaming: treat request-scoped narrative fallback as expected, skip session cleanup when no subagent run was created, and remove duplicate phase-level cleanup so fallback no longer emits warning noise. Fixes #67152. Thanks @jsompis. - Agents/exec: apply configured `tools.exec.timeoutSec` to background and `yieldMs` commands when no per-call timeout is set, preventing auto-backgrounded commands from running indefinitely. Fixes #67600; supersedes #67603. Thanks @dlmpx and @kagura-agent. diff --git a/git-hooks/pre-commit b/git-hooks/pre-commit index b7a1f25ba9a..88c73412bac 100755 --- a/git-hooks/pre-commit +++ b/git-hooks/pre-commit @@ -28,13 +28,22 @@ if [ "${#files[@]}" -eq 0 ]; then exit 0 fi +restage_files=() +for file in "${files[@]}"; do + if ! git check-ignore --no-index -q -- "$file"; then + restage_files+=("$file") + fi +done + format_files=() while IFS= read -r -d '' file; do format_files+=("$file") -done < <(node "$FILTER_FILES" format -- "${files[@]}") +done < <(node "$FILTER_FILES" format -- "${restage_files[@]}") if [ "${#format_files[@]}" -gt 0 ]; then "$RUN_NODE_TOOL" oxfmt --write --no-error-on-unmatched-pattern "${format_files[@]}" fi -git add -- "${files[@]}" +if [ "${#restage_files[@]}" -gt 0 ]; then + git add -- "${restage_files[@]}" +fi diff --git a/test/git-hooks-pre-commit.test.ts b/test/git-hooks-pre-commit.test.ts index 4d9bc743b51..2a9ec9fe53f 100644 --- a/test/git-hooks-pre-commit.test.ts +++ b/test/git-hooks-pre-commit.test.ts @@ -103,6 +103,30 @@ describe("git-hooks/pre-commit (integration)", () => { expect(run(dir, "git", ["diff", "--cached", "--name-only"])).toBe("tracked.txt"); }); + it("does not re-add staged paths that are ignored by the current .gitignore", () => { + const dir = makeTempRepoRoot(tempDirs, "openclaw-pre-commit-ignored-staged-"); + run(dir, "git", ["init", "-q", "--initial-branch=main"]); + + const fakeBinDir = installPreCommitFixture(dir); + mkdirSync(path.join(dir, ".agents", "skills", "discord-clawd"), { recursive: true }); + writeFileSync(path.join(dir, ".gitignore"), ".agents/skills/discord-clawd/\n", "utf8"); + writeFileSync( + path.join(dir, ".agents", "skills", "discord-clawd", "SKILL.md"), + "# Discord Clawd\n", + "utf8", + ); + + run(dir, "git", ["add", "--", ".gitignore"]); + run(dir, "git", ["add", "-f", "--", ".agents/skills/discord-clawd/SKILL.md"]); + + run(dir, "bash", ["git-hooks/pre-commit"], { + PATH: `${fakeBinDir}:${process.env.PATH ?? ""}`, + }); + + const staged = run(dir, "git", ["diff", "--cached", "--name-only"]).split("\n").filter(Boolean); + expect(staged).toEqual([".agents/skills/discord-clawd/SKILL.md", ".gitignore"]); + }); + it("ignores FAST_COMMIT because the hook is already formatting-only", () => { const dir = makeTempRepoRoot(tempDirs, "openclaw-pre-commit-fast-"); run(dir, "git", ["init", "-q", "--initial-branch=main"]);