feat(vcs): mark the current worktree in the Worktrees list

The worktree whose path is the window's repository root gets a check before its branch (Current worktree); drive paths compare case-insensitively. Branch names keep their width and the path is cut short instead.
This commit is contained in:
l0ng-ai
2026-11-05 16:14:54 +08:00
parent 26e3ecc3a0
commit db99fc9cd3
5 changed files with 53 additions and 2 deletions
+12
View File
@@ -305,6 +305,18 @@
.manageRowSelected:hover {
background: var(--bg-list-selected);
}
/* A fixed slot, so the names line up whether or not a row is marked. */
.worktreeMark {
display: flex;
flex: none;
width: 14px;
margin-right: -4px;
}
/* The branch keeps its width; the path is what gets cut short. */
.worktreeName {
flex: none;
max-width: 40%;
}
.comparisonDialog > [data-dialog-body] {
display: flex;
+9 -2
View File
@@ -5,12 +5,13 @@ import type { Branch, MergeOptions, Remote, Worktree } from "@/bindings";
import { executeAction } from "@/platform/actions";
import { call, commands } from "@/platform/ipc";
import { notifications } from "@/workbench/contributions";
import { Checkbox, Dialog, IconButton, Input, Select, T, type SelectOption } from "@/ui";
import { Checkbox, Dialog, Icon, IconButton, Input, Select, T, type SelectOption } from "@/ui";
import { sortLocal, sortRemote } from "./branches";
import { conflictActions, openConflictsInstead, validateBranchName } from "./branchOps";
import { confirmDialog, openDialog } from "./dialogs";
import { errorMessage, runOp, runRemote, saveAll } from "./ops";
import { currentRepo, loadBranches, refreshNow, repoName, selectRepo, useVcs } from "./store";
import { isCurrentWorktree } from "./worktrees";
import styles from "./common.module.css";
import { tx } from "@/platform/i18n";
@@ -660,7 +661,13 @@ export function WorktreesPanel({ className, listClassName }: { className?: strin
onClick={() => setSelected(w.path)}
onDoubleClick={() => void executeAction("file.openProjectPath", w.path)}
>
<span className={styles.fileName}>{w.branch ?? (w.head ? w.head.slice(0, 8) : <T>(bare)</T>)}</span>
{/* The worktree this window works in, as IDEA marks it. */}
<span className={styles.worktreeMark}>
{isCurrentWorktree(w.path, repo) && <Icon name="check" size={14} label={tx("Current worktree")} />}
</span>
<span className={clsx(styles.fileName, styles.worktreeName)}>
{w.branch ?? (w.head ? w.head.slice(0, 8) : <T>(bare)</T>)}
</span>
<span className={clsx(styles.fileDir, styles.mono)}>{w.path}</span>
{(w.main || w.locked || w.prunable) && (
<span className={styles.fileDir}>{w.main ? "main" : w.locked ? tx("locked") : tx("prunable")}</span>
+18
View File
@@ -0,0 +1,18 @@
import { describe, expect, it } from "vitest";
import { isCurrentWorktree } from "./worktrees";
describe("isCurrentWorktree", () => {
it("matches the repository root this window works in", () => {
expect(isCurrentWorktree("/work/app", "/work/app")).toBe(true);
expect(isCurrentWorktree("/work/app-wt", "/work/app")).toBe(false);
expect(isCurrentWorktree("/work/app/", "/work/app")).toBe(true);
expect(isCurrentWorktree("/work/app", null)).toBe(false);
});
it("ignores the drive letter's case and separators on Windows", () => {
expect(isCurrentWorktree("c:/Work/app", "C:/Work/app")).toBe(true);
expect(isCurrentWorktree("C:\\Work\\app", "C:/Work/app")).toBe(true);
// Only drive paths: elsewhere the case is part of the name.
expect(isCurrentWorktree("/Work/App", "/work/app")).toBe(false);
});
});
+13
View File
@@ -0,0 +1,13 @@
/**
* Whether worktree `path` (as `git worktree list` prints it) is the repository root `root`
* this window works in. Git prints `C:/…` with the drive in either case, so drive paths
* compare case-insensitively; a trailing slash doesn't count.
*/
export function isCurrentWorktree(path: string, root: string | null | undefined): boolean {
if (!root) return false;
const norm = (p: string) => {
const s = p.replace(/\\/g, "/").replace(/(.)\/+$/, "$1");
return /^[A-Za-z]:\//.test(s) ? s.toLowerCase() : s;
};
return norm(path) === norm(root);
}
+1
View File
@@ -2707,6 +2707,7 @@
"New Worktree…": "新建工作树…",
"Worktrees…": "工作树…",
"Worktrees": "工作树",
"Current worktree": "当前工作树",
"New Worktree": "新建工作树",
"Git Remotes": "Git 远程",
"Define Remote": "定义远程",