Skip to content

Commit e63f693

Browse files
authored
fix: Show both local and remote repo name when they differ (#1547)
## Problem Folder display names only used path.basename(), so when a local directory name differed from the GitHub repo name, the dropdown showed one name and the sidebar showed another. Closes #1283 ## Changes 1. Add getDisplayName that appends remote repo name when it differs from the local dir (e.g. ph-tour-demo (hogotchi)) 2. Extract shared normalizeRepoKey helper to strip .git suffixes consistently 3. Use normalizeRepoKey in sidebar folder matching to fix .git suffix mismatches 4. Use [folder.name](http://folder.name) instead of [group.name](http://group.name) in sidebar labels for consistency 5. Add four unit tests covering suffix stripping, mismatch, match and case-insensitive match ## How did you test this? Manually
1 parent 8c8b6a2 commit e63f693

4 files changed

Lines changed: 105 additions & 7 deletions

File tree

apps/code/src/main/services/folders/service.test.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,82 @@ describe("FoldersService", () => {
296296
]);
297297
});
298298

299+
it("strips .git suffix from remote repo name in display name (defensive against legacy data)", async () => {
300+
const repos = [
301+
{
302+
id: "folder-1",
303+
path: "/home/user/my-billing-fork",
304+
remoteUrl: "PostHog/billing.git",
305+
lastAccessedAt: "2024-01-01T00:00:00.000Z",
306+
createdAt: "2024-01-01T00:00:00.000Z",
307+
updatedAt: "2024-01-01T00:00:00.000Z",
308+
},
309+
];
310+
mockRepositoryRepo.findAll.mockReturnValue(repos);
311+
mockExistsSync.mockReturnValue(true);
312+
313+
const result = await service.getFolders();
314+
315+
expect(result[0].name).toBe("my-billing-fork (billing)");
316+
});
317+
318+
it("uses remote repo name in display name when it differs from local dir", async () => {
319+
const repos = [
320+
{
321+
id: "folder-1",
322+
path: "/home/user/ph-tour-demo",
323+
remoteUrl: "PostHog/hogotchi",
324+
lastAccessedAt: "2024-01-01T00:00:00.000Z",
325+
createdAt: "2024-01-01T00:00:00.000Z",
326+
updatedAt: "2024-01-01T00:00:00.000Z",
327+
},
328+
];
329+
mockRepositoryRepo.findAll.mockReturnValue(repos);
330+
mockExistsSync.mockReturnValue(true);
331+
332+
const result = await service.getFolders();
333+
334+
expect(result[0].name).toBe("ph-tour-demo (hogotchi)");
335+
});
336+
337+
it("uses local dir name when it matches remote repo name", async () => {
338+
const repos = [
339+
{
340+
id: "folder-1",
341+
path: "/home/user/hogotchi",
342+
remoteUrl: "PostHog/hogotchi",
343+
lastAccessedAt: "2024-01-01T00:00:00.000Z",
344+
createdAt: "2024-01-01T00:00:00.000Z",
345+
updatedAt: "2024-01-01T00:00:00.000Z",
346+
},
347+
];
348+
mockRepositoryRepo.findAll.mockReturnValue(repos);
349+
mockExistsSync.mockReturnValue(true);
350+
351+
const result = await service.getFolders();
352+
353+
expect(result[0].name).toBe("hogotchi");
354+
});
355+
356+
it("uses local dir name when it matches remote repo name case-insensitively", async () => {
357+
const repos = [
358+
{
359+
id: "folder-1",
360+
path: "/home/user/Hogotchi",
361+
remoteUrl: "PostHog/hogotchi",
362+
lastAccessedAt: "2024-01-01T00:00:00.000Z",
363+
createdAt: "2024-01-01T00:00:00.000Z",
364+
updatedAt: "2024-01-01T00:00:00.000Z",
365+
},
366+
];
367+
mockRepositoryRepo.findAll.mockReturnValue(repos);
368+
mockExistsSync.mockReturnValue(true);
369+
370+
const result = await service.getFolders();
371+
372+
expect(result[0].name).toBe("Hogotchi");
373+
});
374+
299375
it("marks non-existent folders", async () => {
300376
const repos = [
301377
{

apps/code/src/main/services/folders/service.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ import path from "node:path";
33
import { getRemoteUrl, isGitRepository } from "@posthog/git/queries";
44
import { InitRepositorySaga } from "@posthog/git/sagas/init";
55

6+
import { normalizeRepoKey } from "@shared/utils/repo";
7+
68
function extractRepoKey(url: string): string | null {
79
const httpsMatch = url.match(/github\.com\/([^/]+\/[^/]+)/);
8-
if (httpsMatch) return httpsMatch[1].replace(/\.git$/, "");
10+
if (httpsMatch) return normalizeRepoKey(httpsMatch[1]);
911

1012
const sshMatch = url.match(/github\.com:([^/]+\/[^/]+)/);
11-
if (sshMatch) return sshMatch[1].replace(/\.git$/, "");
13+
if (sshMatch) return normalizeRepoKey(sshMatch[1]);
1214

1315
return null;
1416
}
@@ -80,14 +82,28 @@ export class FoldersService {
8082
}
8183
}
8284

85+
private getDisplayName(
86+
repoPath: string,
87+
remoteUrl: string | null | undefined,
88+
): string {
89+
const localName = path.basename(repoPath);
90+
if (remoteUrl) {
91+
const repoName = normalizeRepoKey(remoteUrl).split("/").pop();
92+
if (repoName && repoName.toLowerCase() !== localName.toLowerCase()) {
93+
return `${localName} (${repoName})`;
94+
}
95+
}
96+
return localName;
97+
}
98+
8399
async getFolders(): Promise<(RegisteredFolder & { exists: boolean })[]> {
84100
const repos = this.repositoryRepo.findAll();
85101
return repos
86102
.filter((r) => r.path)
87103
.map((r) => ({
88104
id: r.id,
89105
path: r.path,
90-
name: path.basename(r.path),
106+
name: this.getDisplayName(r.path, r.remoteUrl),
91107
remoteUrl: r.remoteUrl ?? null,
92108
lastAccessed: r.lastAccessedAt ?? r.createdAt,
93109
createdAt: r.createdAt,
@@ -177,7 +193,7 @@ export class FoldersService {
177193
return {
178194
id: repo.id,
179195
path: repo.path,
180-
name: path.basename(repo.path),
196+
name: this.getDisplayName(repo.path, repo.remoteUrl),
181197
remoteUrl: repo.remoteUrl ?? null,
182198
lastAccessed: repo.lastAccessedAt ?? repo.createdAt,
183199
createdAt: repo.createdAt,

apps/code/src/renderer/features/sidebar/components/TaskListView.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
} from "@phosphor-icons/react";
1616
import { Box, Flex, Popover, Text } from "@radix-ui/themes";
1717
import { useWorkspace } from "@renderer/features/workspace/hooks/useWorkspace";
18+
import { normalizeRepoKey } from "@shared/utils/repo";
1819
import { useNavigationStore } from "@stores/navigationStore";
1920
import { useCallback, useEffect } from "react";
2021
import type { TaskData, TaskGroup } from "../hooks/useSidebarData";
@@ -378,7 +379,9 @@ export function TaskListView({
378379
const isExpanded = !collapsedSections.has(group.id);
379380
const folder = folders.find(
380381
(f) =>
381-
f.remoteUrl?.toLowerCase() === group.id.toLowerCase() ||
382+
(f.remoteUrl &&
383+
normalizeRepoKey(f.remoteUrl).toLowerCase() ===
384+
normalizeRepoKey(group.id).toLowerCase()) ||
382385
f.path === group.id,
383386
);
384387
const groupFolderId =
@@ -387,7 +390,7 @@ export function TaskListView({
387390
<DraggableFolder key={group.id} id={group.id} index={index}>
388391
<SidebarSection
389392
id={group.id}
390-
label={group.name}
393+
label={folder?.name ?? group.name}
391394
icon={
392395
isExpanded ? (
393396
<FolderOpenIcon size={14} className="text-gray-10" />
@@ -406,7 +409,7 @@ export function TaskListView({
406409
navigateToTaskInput();
407410
}
408411
}}
409-
newTaskTooltip={`Start new task in ${group.name}`}
412+
newTaskTooltip={`Start new task in ${folder?.name ?? group.name}`}
410413
>
411414
{group.tasks.map((task) => (
412415
<TaskRow

apps/code/src/shared/utils/repo.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function normalizeRepoKey(key: string): string {
2+
return key.trim().replace(/\.git$/, "");
3+
}

0 commit comments

Comments
 (0)