fix(gsd): deduplicate resolveGitHeadPath function (#1015)
Move resolveGitHeadPath() and nudgeGitBranchCache() to worktree.ts as the canonical shared location. Both auto-worktree.ts and worktree-command.ts now import from worktree.ts instead of defining their own copies. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f8b16beddb
commit
ea0dcef576
3 changed files with 47 additions and 88 deletions
|
|
@ -6,8 +6,8 @@
|
|||
* manages create, enter, detect, and teardown for auto-mode worktrees.
|
||||
*/
|
||||
|
||||
import { existsSync, cpSync, readFileSync, writeFileSync, readdirSync, mkdirSync, realpathSync, utimesSync, unlinkSync } from "node:fs";
|
||||
import { isAbsolute, join, resolve } from "node:path";
|
||||
import { existsSync, cpSync, readFileSync, writeFileSync, readdirSync, mkdirSync, realpathSync, unlinkSync } from "node:fs";
|
||||
import { isAbsolute, join } from "node:path";
|
||||
import { GSDError, GSD_IO_ERROR, GSD_GIT_ERROR } from "./errors.js";
|
||||
import { copyWorktreeDb, reconcileWorktreeDb, isDbAvailable } from "./gsd-db.js";
|
||||
import { execSync, execFileSync } from "node:child_process";
|
||||
|
|
@ -16,7 +16,7 @@ import {
|
|||
removeWorktree,
|
||||
worktreePath,
|
||||
} from "./worktree-manager.js";
|
||||
import { detectWorktreeName } from "./worktree.js";
|
||||
import { detectWorktreeName, resolveGitHeadPath, nudgeGitBranchCache } from "./worktree.js";
|
||||
import {
|
||||
MergeConflictError,
|
||||
readIntegrationBranch,
|
||||
|
|
@ -43,41 +43,6 @@ import {
|
|||
/** Original project root before chdir into auto-worktree. */
|
||||
let originalBase: string | null = null;
|
||||
|
||||
// ─── Git Helpers (local, mirrors worktree-command.ts pattern) ──────────────
|
||||
|
||||
function resolveGitHeadPath(dir: string): string | null {
|
||||
const gitPath = join(dir, ".git");
|
||||
if (!existsSync(gitPath)) return null;
|
||||
try {
|
||||
const content = readFileSync(gitPath, "utf8").trim();
|
||||
if (content.startsWith("gitdir: ")) {
|
||||
const gitDir = resolve(dir, content.slice(8));
|
||||
const headPath = join(gitDir, "HEAD");
|
||||
return existsSync(headPath) ? headPath : null;
|
||||
}
|
||||
const headPath = join(dir, ".git", "HEAD");
|
||||
return existsSync(headPath) ? headPath : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Nudge pi's FooterDataProvider to re-read the git branch after chdir.
|
||||
* Touches HEAD in both old and new cwd to fire the fs watcher.
|
||||
*/
|
||||
function nudgeGitBranchCache(previousCwd: string): void {
|
||||
const now = new Date();
|
||||
for (const dir of [previousCwd, process.cwd()]) {
|
||||
try {
|
||||
const headPath = resolveGitHeadPath(dir);
|
||||
if (headPath) utimesSync(headPath, now, now);
|
||||
} catch {
|
||||
// Best-effort
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Worktree Post-Create Hook (#597) ────────────────────────────────────────
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
import type { ExtensionAPI, ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
||||
import { loadPrompt } from "./prompt-loader.js";
|
||||
import { autoCommitCurrentBranch, getMainBranch } from "./worktree.js";
|
||||
import { autoCommitCurrentBranch, getMainBranch, resolveGitHeadPath, nudgeGitBranchCache } from "./worktree.js";
|
||||
import { runWorktreePostCreateHook } from "./auto-worktree.js";
|
||||
import { showConfirm } from "../shared/confirm-ui.js";
|
||||
import { gsdRoot, milestonesDir } from "./paths.js";
|
||||
|
|
@ -31,9 +31,9 @@ import {
|
|||
} from "./worktree-manager.js";
|
||||
import { inferCommitType } from "./git-service.js";
|
||||
import type { FileLineStat } from "./worktree-manager.js";
|
||||
import { existsSync, realpathSync, readFileSync, readdirSync, rmSync, unlinkSync, utimesSync } from "node:fs";
|
||||
import { existsSync, realpathSync, readdirSync, rmSync, unlinkSync } from "node:fs";
|
||||
import { nativeMergeAbort } from "./native-git-bridge.js";
|
||||
import { join, resolve, sep } from "node:path";
|
||||
import { join, sep } from "node:path";
|
||||
|
||||
/**
|
||||
* Tracks the original project root so we can switch back.
|
||||
|
|
@ -46,52 +46,6 @@ export function getWorktreeOriginalCwd(): string | null {
|
|||
return originalCwd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the git HEAD file path for a given directory.
|
||||
* Handles both normal repos (.git is a directory) and worktrees (.git is a file).
|
||||
*/
|
||||
function resolveGitHeadPath(dir: string): string | null {
|
||||
const gitPath = join(dir, ".git");
|
||||
if (!existsSync(gitPath)) return null;
|
||||
|
||||
try {
|
||||
const content = readFileSync(gitPath, "utf8").trim();
|
||||
if (content.startsWith("gitdir: ")) {
|
||||
// Worktree — .git is a file pointing to the real gitdir
|
||||
const gitDir = resolve(dir, content.slice(8));
|
||||
const headPath = join(gitDir, "HEAD");
|
||||
return existsSync(headPath) ? headPath : null;
|
||||
}
|
||||
// Normal repo — .git is a directory
|
||||
const headPath = join(dir, ".git", "HEAD");
|
||||
return existsSync(headPath) ? headPath : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Nudge pi's FooterDataProvider to re-read the git branch.
|
||||
*
|
||||
* The footer caches the branch and watches a single .git dir for changes.
|
||||
* After process.chdir() into a worktree (or back), the watcher is stale —
|
||||
* it's still watching the old git dir. We touch HEAD in both the old and
|
||||
* new git dirs to ensure the watcher fires regardless of which one it's
|
||||
* monitoring. This clears cachedBranch; the next getGitBranch() call uses
|
||||
* the new process.cwd() and picks up the correct branch.
|
||||
*/
|
||||
function nudgeGitBranchCache(previousCwd: string): void {
|
||||
const now = new Date();
|
||||
for (const dir of [previousCwd, process.cwd()]) {
|
||||
try {
|
||||
const headPath = resolveGitHeadPath(dir);
|
||||
if (headPath) utimesSync(headPath, now, now);
|
||||
} catch {
|
||||
// Best-effort — branch display may be stale
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the name of the active worktree, or null if not in one. */
|
||||
export function getActiveWorktreeName(): string | null {
|
||||
if (!originalCwd) return null;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@
|
|||
* SLICE_BRANCH_RE) remain for backwards compatibility with legacy branches.
|
||||
*/
|
||||
|
||||
import { sep } from "node:path";
|
||||
import { existsSync, readFileSync, utimesSync } from "node:fs";
|
||||
import { join, resolve, sep } from "node:path";
|
||||
|
||||
import { GitServiceImpl, writeIntegrationBranch, type TaskCommitContext } from "./git-service.js";
|
||||
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
||||
|
|
@ -177,4 +178,43 @@ export function autoCommitCurrentBranch(
|
|||
return getService(basePath).autoCommit(unitType, unitId, [], taskContext);
|
||||
}
|
||||
|
||||
// ─── Git HEAD Resolution ────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Resolve the git HEAD file path for a given directory.
|
||||
* Handles both normal repos (.git is a directory) and worktrees (.git is a file
|
||||
* containing a `gitdir:` pointer to the real gitdir).
|
||||
*/
|
||||
export function resolveGitHeadPath(dir: string): string | null {
|
||||
const gitPath = join(dir, ".git");
|
||||
if (!existsSync(gitPath)) return null;
|
||||
|
||||
try {
|
||||
const content = readFileSync(gitPath, "utf8").trim();
|
||||
if (content.startsWith("gitdir: ")) {
|
||||
const gitDir = resolve(dir, content.slice(8));
|
||||
const headPath = join(gitDir, "HEAD");
|
||||
return existsSync(headPath) ? headPath : null;
|
||||
}
|
||||
const headPath = join(dir, ".git", "HEAD");
|
||||
return existsSync(headPath) ? headPath : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Nudge pi's FooterDataProvider to re-read the git branch after chdir.
|
||||
* Touches HEAD in both old and new cwd to fire the fs watcher.
|
||||
*/
|
||||
export function nudgeGitBranchCache(previousCwd: string): void {
|
||||
const now = new Date();
|
||||
for (const dir of [previousCwd, process.cwd()]) {
|
||||
try {
|
||||
const headPath = resolveGitHeadPath(dir);
|
||||
if (headPath) utimesSync(headPath, now, now);
|
||||
} catch {
|
||||
// Best-effort
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue