mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-06 08:07:49 +00:00
feat(vcs): mark the current worktree in the Worktrees list (#1506)
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:
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
@@ -2707,6 +2707,7 @@
|
||||
"New Worktree…": "新建工作树…",
|
||||
"Worktrees…": "工作树…",
|
||||
"Worktrees": "工作树",
|
||||
"Current worktree": "当前工作树",
|
||||
"New Worktree": "新建工作树",
|
||||
"Git Remotes": "Git 远程",
|
||||
"Define Remote": "定义远程",
|
||||
|
||||
Reference in New Issue
Block a user