diff --git a/client/web-sveltekit/src/lib/Changelist.gql b/client/web-sveltekit/src/lib/Changelist.gql new file mode 100644 index 00000000000..2b655e66493 --- /dev/null +++ b/client/web-sveltekit/src/lib/Changelist.gql @@ -0,0 +1,29 @@ +fragment Changelist on PerforceChangelist { + cid + canonicalURL + commit { + message + oid + body + subject + author { + person { + ...Avatar_Person + } + date + } + parents { + id + oid + abbreviatedOID + parent: perforceChangelist { + cid + canonicalURL + } + } + perforceChangelist { + cid + canonicalURL + } + } +} diff --git a/client/web-sveltekit/src/lib/Changelist.svelte b/client/web-sveltekit/src/lib/Changelist.svelte new file mode 100644 index 00000000000..570a15eeabe --- /dev/null +++ b/client/web-sveltekit/src/lib/Changelist.svelte @@ -0,0 +1,157 @@ + + + + +
+
+ + + +
+
+ + {changelist.commit.subject} + {#if !alwaysExpanded && changelist.commit.body && !$isViewportMobile} + + {/if} +
+
+ submitted by {author.person.name} + +
+ {#if changelist.commit.body} +
+ {#if $isViewportMobile} + {#if expanded} + + {:else} + + {/if} + {/if} + +
{changelist.commit.body}
+
+ {/if} +
+ + diff --git a/client/web-sveltekit/src/lib/repo/Permalink.svelte b/client/web-sveltekit/src/lib/repo/Permalink.svelte index 818c0121eb8..c6318d33f1f 100644 --- a/client/web-sveltekit/src/lib/repo/Permalink.svelte +++ b/client/web-sveltekit/src/lib/repo/Permalink.svelte @@ -11,7 +11,8 @@ import Tooltip from '$lib/Tooltip.svelte' import { parseBrowserRepoURL } from '$lib/web' - export let commitID: string + export let revID: string + export let tooltip: string const hotkey = createHotkey({ keys: { key: 'y' }, @@ -19,7 +20,7 @@ handler: () => { const { revision } = parseBrowserRepoURL($page.url.pathname) // Only navigate if necessary. We don't want to add unnecessary history entries. - if (revision !== commitID) { + if (revision !== revID) { goto(href, { noScroll: true, keepFocus: true }).catch(() => { // TODO: log error with Sentry }) @@ -27,7 +28,7 @@ }, }) - $: href = commitID ? replaceRevisionInURL($page.url.toString(), commitID) : '' + $: href = revID ? replaceRevisionInURL($page.url.toString(), revID) : '' $: if (href) { hotkey.enable() } else { @@ -36,7 +37,7 @@ {#if href} - + Permalink {/if} diff --git a/client/web-sveltekit/src/lib/repo/RepositoryRevPicker.gql b/client/web-sveltekit/src/lib/repo/RepositoryRevPicker.gql index 09017ad7eda..fe041ed307f 100644 --- a/client/web-sveltekit/src/lib/repo/RepositoryRevPicker.gql +++ b/client/web-sveltekit/src/lib/repo/RepositoryRevPicker.gql @@ -40,3 +40,14 @@ fragment RepositoryGitRevAuthor on GitCommit { } } } + +fragment RevPickerChangelist on GitCommit { + __typename + id + subject + perforceChangelist { + cid + canonicalURL + } + ...DepotChangelistAuthor +} diff --git a/client/web-sveltekit/src/lib/repo/RepositoryRevPicker.svelte b/client/web-sveltekit/src/lib/repo/RepositoryRevPicker.svelte index 059e7845575..34ad2875090 100644 --- a/client/web-sveltekit/src/lib/repo/RepositoryRevPicker.svelte +++ b/client/web-sveltekit/src/lib/repo/RepositoryRevPicker.svelte @@ -1,7 +1,7 @@ + + + {pageTitle} + + +
+

+ Changelists + {#if data.path} + in {data.path} + {/if} +

+
+ +
+
+
+ + {#if changelists} +
    + {#each changelists as changelistCommit (changelistCommit.perforceChangelist?.canonicalURL)} + {@const changelist = changelistCommit.perforceChangelist} + {#if changelist !== null} +
  • +
    + +
    + +
  • + {/if} + {:else} +
  • + No changelists found +
  • + {/each} +
+ {/if} + {#if $changelistsQuery.fetching} + + {:else if !$changelistsQuery.fetching && $changelistsQuery.error} + + {/if} +
+
+ + diff --git a/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/-/changelists/[...path]/+page.ts b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/-/changelists/[...path]/+page.ts new file mode 100644 index 00000000000..52016af52f3 --- /dev/null +++ b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/-/changelists/[...path]/+page.ts @@ -0,0 +1,51 @@ +import { parseRepoRevision } from '@sourcegraph/shared/src/util/url' + +import { IncrementalRestoreStrategy, getGraphQLClient, infinityQuery } from '$lib/graphql' +import { resolveRevision } from '$lib/repo/utils' + +import type { PageLoad } from './$types' +import { ChangelistsPage_ChangelistsQuery } from './page.gql' + +const PAGE_SIZE = 20 + +export const load: PageLoad = ({ parent, params }) => { + const client = getGraphQLClient() + const { repoName, revision = '' } = parseRepoRevision(params.repo) + const path = params.path ? decodeURIComponent(params.path) : '' + const resolvedRevision = resolveRevision(parent, revision) + + const changelistsQuery = infinityQuery({ + client, + query: ChangelistsPage_ChangelistsQuery, + variables: resolvedRevision.then(revision => ({ + depotName: repoName, + revision, + first: PAGE_SIZE, + path, + afterCursor: null as string | null, + })), + map: result => { + const ancestors = result.data?.repository?.commit?.ancestors + return { + nextVariables: + ancestors?.pageInfo?.endCursor && ancestors?.pageInfo?.hasNextPage + ? { afterCursor: ancestors.pageInfo.endCursor } + : undefined, + data: ancestors?.nodes, + error: result.error, + } + }, + merge: (previous, next) => (previous ?? []).concat(next ?? []), + createRestoreStrategy: api => + new IncrementalRestoreStrategy( + api, + n => n.length, + n => ({ first: n.length }) + ), + }) + + return { + changelistsQuery, + path, + } +} diff --git a/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/-/changelists/[...path]/page.gql b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/-/changelists/[...path]/page.gql new file mode 100644 index 00000000000..0eeeba684cf --- /dev/null +++ b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/-/changelists/[...path]/page.gql @@ -0,0 +1,56 @@ +fragment ChangelistsPage_GitCommitConnection on GitCommitConnection { + nodes { + id + oid + abbreviatedOID + canonicalURL + subject + body + externalURLs { + serviceKind + url + } + + author { + date + person { + name + email + ...Avatar_Person + } + } + committer { + date + person { + name + email + ...Avatar_Person + } + } + perforceChangelist { + ...Changelist + } + } + pageInfo { + hasNextPage + endCursor + } +} + +query ChangelistsPage_ChangelistsQuery( + $depotName: String! + $revision: String! + $first: Int + $path: String + $afterCursor: String +) { + repository(name: $depotName) { + id + commit(rev: $revision) { + id + ancestors(first: $first, afterCursor: $afterCursor, path: $path) { + ...ChangelistsPage_GitCommitConnection + } + } + } +} diff --git a/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/-/commits/[...path]/+page.svelte b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/-/commits/[...path]/+page.svelte index cc8b32a29a8..48329207319 100644 --- a/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/-/commits/[...path]/+page.svelte +++ b/client/web-sveltekit/src/routes/[...repo=reporev]/(validrev)/-/commits/[...path]/+page.svelte @@ -5,13 +5,12 @@ import { navigating } from '$app/stores' import Commit from '$lib/Commit.svelte' import LoadingSpinner from '$lib/LoadingSpinner.svelte' + import RepositoryRevPicker from '$lib/repo/RepositoryRevPicker.svelte' import { getHumanNameForCodeHost } from '$lib/repo/shared/codehost' import Scroller, { type Capture as ScrollerCapture } from '$lib/Scroller.svelte' import CodeHostIcon from '$lib/search/CodeHostIcon.svelte' import { Alert, Badge } from '$lib/wildcard' - import RepositoryRevPicker from '$lib/repo/RepositoryRevPicker.svelte' - import type { PageData, Snapshot } from './$types' export let data: PageData diff --git a/client/web-sveltekit/src/routes/[...repo=reporev]/+layout.svelte b/client/web-sveltekit/src/routes/[...repo=reporev]/+layout.svelte index 3e6dd2fac06..47811eeef49 100644 --- a/client/web-sveltekit/src/routes/[...repo=reporev]/+layout.svelte +++ b/client/web-sveltekit/src/routes/[...repo=reporev]/+layout.svelte @@ -48,7 +48,7 @@ export let data: LayoutData const menuOpen = writable(false) - const navEntries: MenuEntry[] = [ + const gitNavEntries: MenuEntry[] = [ { path: '', icon: ILucideCode, label: 'Code', visibility: 'user', preserveRevision: true }, { path: '/-/commits', @@ -61,6 +61,22 @@ { path: '/-/tags', icon: ILucideTag, label: 'Tags', visibility: 'user' }, { path: '/-/stats/contributors', icon: ILucideUsers, label: 'Contributors', visibility: 'user' }, ] + const perforceNavEntries: MenuEntry[] = [ + { path: '', icon: ILucideCode, label: 'Code', visibility: 'user', preserveRevision: true }, + { + path: '/-/changelists', + icon: ILucideGitCommitVertical, + label: 'Changelists', + visibility: 'user', + preserveRevision: true, + }, + { + path: '/-/stats/contributors', + icon: ILucideUsers, + label: 'Contributors', + visibility: 'user', + }, + ] const menuEntries: MenuEntry[] = [ { path: '/-/compare', icon: ILucideGitCompare, label: 'Compare', visibility: 'user' }, { path: '/-/own', icon: ILucideUsers, label: 'Ownership', visibility: 'admin' }, @@ -85,7 +101,7 @@ setRepositoryPageContext(repositoryContext) - $: viewableNavEntries = navEntries.filter( + $: viewableNavEntries = (data.isPerforceDepot ? perforceNavEntries : gitNavEntries).filter( entry => entry.visibility === 'user' || (entry.visibility === 'admin' && data.user?.siteAdmin) ) $: visibleNavEntryCount = viewableNavEntries.length diff --git a/client/web-sveltekit/src/routes/[...repo=reporev]/+layout.ts b/client/web-sveltekit/src/routes/[...repo=reporev]/+layout.ts index 60d36c4e607..077b8b39fa7 100644 --- a/client/web-sveltekit/src/routes/[...repo=reporev]/+layout.ts +++ b/client/web-sveltekit/src/routes/[...repo=reporev]/+layout.ts @@ -7,6 +7,7 @@ import { CloneInProgressError, RepoNotFoundError, displayRepoName, parseRepoRevi import type { LayoutLoad } from './$types' import { + DepotChangelists, RepositoryGitCommits, RepositoryGitRefs, ResolveRepoRevision, @@ -27,6 +28,7 @@ export const load: LayoutLoad = async ({ params, url, depends }) => { // An empty revision means we are at the default branch const { repoName, revision = '' } = parseRepoRevision(params.repo) + const resolvedRepository = await resolveRepoRevision({ client, repoName, @@ -50,8 +52,11 @@ export const load: LayoutLoad = async ({ params, url, depends }) => { * - a symbolic revision (e.g. a branch or tag name) */ displayRevision: displayRevision(revision, resolvedRepository), - defaultBranch: resolvedRepository.defaultBranch?.abbrevName || 'HEAD', + defaultBranch: resolvedRepository.defaultBranch?.target.commit?.perforceChangelist?.cid + ? `changelist/${resolvedRepository.defaultBranch?.target.commit?.perforceChangelist?.cid}` + : resolvedRepository.defaultBranch?.abbrevName || 'HEAD', resolvedRepository: resolvedRepository, + isPerforceDepot: resolvedRepository.externalRepository.serviceType === 'perforce', // Repository pickers queries (branch, tags and commits) getRepoBranches: (searchTerm: string) => @@ -111,6 +116,26 @@ export const load: LayoutLoad = async ({ params, url, depends }) => { return { nodes } }) ), + + // Depot pickers queries (changelists, @TODO: labels) + getDepotChangelists: (searchTerm: string) => + client + .query(DepotChangelists, { + depotName: repoName, + query: searchTerm, + revision: resolvedRepository.commit?.oid || '', + }) + .then( + mapOrThrow(({ data, error }) => { + let nodes = data?.repository?.ancestorChangelists?.ancestors.nodes ?? [] + + if (error) { + throw new Error('Could not load depot changelists:', error) + } + + return { nodes } + }) + ), } } diff --git a/client/web-sveltekit/src/routes/[...repo=reporev]/-/changelist/[changelistID]/+page.svelte b/client/web-sveltekit/src/routes/[...repo=reporev]/-/changelist/[changelistID]/+page.svelte new file mode 100644 index 00000000000..6f250e00d9b --- /dev/null +++ b/client/web-sveltekit/src/routes/[...repo=reporev]/-/changelist/[changelistID]/+page.svelte @@ -0,0 +1,192 @@ + + + + Changelist: {data.changelist.commit.message ?? ''} - {data.displayRepoName} - Sourcegraph + + +
+ {#if data.changelist} + +
+
+ +
+ +
+
+ {#if diffs} +
    + {#each diffs as node, index (index)} +
  • + { + expandedDiffs.set(index, event.detail.expanded) + // This is needed to for Svelte to consider that expandedDiffs has changed + expandedDiffs = expandedDiffs + }} + /> +
  • + {/each} +
+ {/if} + {#if $diffQuery?.fetching} + + {:else if $diffQuery?.error} +
+ + Unable to fetch file diffs: {$diffQuery.error.message} + +
+ {/if} +
+ {/if} +
+ + diff --git a/client/web-sveltekit/src/routes/[...repo=reporev]/-/changelist/[changelistID]/+page.ts b/client/web-sveltekit/src/routes/[...repo=reporev]/-/changelist/[changelistID]/+page.ts new file mode 100644 index 00000000000..40f1b642453 --- /dev/null +++ b/client/web-sveltekit/src/routes/[...repo=reporev]/-/changelist/[changelistID]/+page.ts @@ -0,0 +1,65 @@ +import { error } from '@sveltejs/kit' + +import { IncrementalRestoreStrategy, getGraphQLClient, infinityQuery } from '$lib/graphql' + +import type { PageLoad } from './$types' +import { ChangelistPage_ChangelistQuery, ChangelistPage_DiffQuery } from './page.gql' + +const PAGE_SIZE = 20 + +export const load: PageLoad = async ({ params }) => { + const client = getGraphQLClient() + + const result = await client.query(ChangelistPage_ChangelistQuery, { + repoName: params.repo, + cid: params.changelistID, + }) + + if (result.error) { + error(500, `Unable to load commit data: ${result.error}`) + } + + const changelist = result.data?.repository?.changelist + + if (!changelist) { + error(404, 'Changelist not found') + } + + // parents is an empty array for the initial commit + // We currently don't support diffs for the initial commit on the backend + + const diff = + changelist.cid && changelist?.commit.parents[0]?.parent?.cid + ? infinityQuery({ + client, + query: ChangelistPage_DiffQuery, + variables: { + repoName: params.repo, + base: changelist.commit.parents[0].oid, + head: changelist.commit.oid, + first: PAGE_SIZE, + after: null as string | null, + }, + map: result => { + const diffs = result.data?.repository?.comparison.fileDiffs + return { + nextVariables: diffs?.pageInfo.hasNextPage ? { after: diffs?.pageInfo.endCursor } : undefined, + data: diffs?.nodes, + error: result.error, + } + }, + merge: (previous, next) => (previous ?? []).concat(next ?? []), + createRestoreStrategy: api => + new IncrementalRestoreStrategy( + api, + n => n.length, + n => ({ first: n.length }) + ), + }) + : null + + return { + changelist, + diff, + } +} diff --git a/client/web-sveltekit/src/routes/[...repo=reporev]/-/changelist/[changelistID]/page.gql b/client/web-sveltekit/src/routes/[...repo=reporev]/-/changelist/[changelistID]/page.gql new file mode 100644 index 00000000000..e299d66b71e --- /dev/null +++ b/client/web-sveltekit/src/routes/[...repo=reporev]/-/changelist/[changelistID]/page.gql @@ -0,0 +1,80 @@ +query ChangelistPage_ChangelistQuery($repoName: String!, $cid: String!) { + repository(name: $repoName) { + id + changelist(cid: $cid) { + cid + canonicalURL + commit { + message + oid + body + subject + author { + person { + ...Avatar_Person + } + date + } + parents { + id + oid + abbreviatedOID + parent: perforceChangelist { + cid + canonicalURL + } + } + perforceChangelist { + cid + canonicalURL + } + } + } + } +} + +query ChangelistPage_DiffQuery($repoName: String!, $base: String, $head: String, $first: Int, $after: String) { + repository(name: $repoName) { + id + comparison(base: $base, head: $head) { + fileDiffs(first: $first, after: $after) { + ...ChangelistPage_DiffConnection + } + } + } +} + +fragment ChangelistPage_DiffConnection on FileDiffConnection { + nodes { + ...ChangelistFileDiff_Diff + } + pageInfo { + endCursor + hasNextPage + } +} + +fragment ChangelistFileDiff_Diff on FileDiff { + mostRelevantFile { + canonicalURL # key field + url + path + } + newFile { + canonicalURL # key field + path + binary + } + oldFile { + canonicalURL # key field + path + binary + } + stat { + added + deleted + } + hunks { + ...FileDiffHunks_Hunk + } +} diff --git a/client/web-sveltekit/src/routes/[...repo=reporev]/-/commit/[...revspec]/+page.ts b/client/web-sveltekit/src/routes/[...repo=reporev]/-/commit/[...revspec]/+page.ts index 0ade7909774..6f3a42a2f4f 100644 --- a/client/web-sveltekit/src/routes/[...repo=reporev]/-/commit/[...revspec]/+page.ts +++ b/client/web-sveltekit/src/routes/[...repo=reporev]/-/commit/[...revspec]/+page.ts @@ -1,4 +1,4 @@ -import { error } from '@sveltejs/kit' +import { error, redirect } from '@sveltejs/kit' import { IncrementalRestoreStrategy, getGraphQLClient, infinityQuery } from '$lib/graphql' import { parseRepoRevision } from '$lib/shared' @@ -8,7 +8,7 @@ import { CommitPage_CommitQuery, CommitPage_DiffQuery } from './page.gql' const PAGE_SIZE = 20 -export const load: PageLoad = async ({ params }) => { +export const load: PageLoad = async ({ url, params }) => { const client = getGraphQLClient() const { repoName } = parseRepoRevision(params.repo) @@ -19,11 +19,16 @@ export const load: PageLoad = async ({ params }) => { } const commit = result.data?.repository?.commit - if (!commit) { error(404, 'Commit not found') } + if (commit.perforceChangelist !== null) { + const redirectURL = new URL(url) + redirectURL.pathname = `${params.repo}/-/changelist/${commit.perforceChangelist?.cid}` + redirect(301, redirectURL) + } + // parents is an empty array for the initial commit // We currently don't support diffs for the initial commit on the backend const diff = diff --git a/client/web-sveltekit/src/routes/[...repo=reporev]/-/commit/[...revspec]/page.gql b/client/web-sveltekit/src/routes/[...repo=reporev]/-/commit/[...revspec]/page.gql index fc41427dddb..c751f6e1643 100644 --- a/client/web-sveltekit/src/routes/[...repo=reporev]/-/commit/[...revspec]/page.gql +++ b/client/web-sveltekit/src/routes/[...repo=reporev]/-/commit/[...revspec]/page.gql @@ -10,6 +10,10 @@ query CommitPage_CommitQuery($repoName: String!, $revspec: String!) { abbreviatedOID canonicalURL } + perforceChangelist { + cid + canonicalURL + } externalURLs { url serviceKind diff --git a/client/web-sveltekit/src/routes/[...repo=reporev]/layout.gql b/client/web-sveltekit/src/routes/[...repo=reporev]/layout.gql index 3f5db1f0590..a018aca243b 100644 --- a/client/web-sveltekit/src/routes/[...repo=reporev]/layout.gql +++ b/client/web-sveltekit/src/routes/[...repo=reporev]/layout.gql @@ -18,6 +18,7 @@ query RepositoryGitCommits($repoName: String!, $query: String!, $revision: Strin } } } + query ResolveRepoRevision($repoName: String!, $revision: String!) { repositoryRedirect(name: $repoName) { __typename @@ -34,8 +35,14 @@ fragment ResolvedRepository on Repository { id commit(rev: $revision) { oid + perforceChangelist { + cid + canonicalURL + } } changelist(cid: $revision) { + cid + canonicalURL commit { oid } @@ -47,6 +54,14 @@ fragment ResolvedRepository on Repository { } defaultBranch { abbrevName + target { + commit { + perforceChangelist { + cid + canonicalURL + } + } + } } externalURLs { url @@ -56,3 +71,39 @@ fragment ResolvedRepository on Repository { ...BlobPage_ResolvedRevision ...CodySidebar_ResolvedRevision } + +query DepotChangelists($depotName: String!, $revision: String!) { + repository(name: $depotName) { + changelist: commit(rev: $revision) { + ...RevPickerChangelist + } + ancestorChangelists: commit(rev: $revision) { + ancestors(first: 15) { + nodes { + ...RevPickerChangelist + } + } + } + } +} + +query DepotChangelist($depotName: String!, $revision: String!) { + repository(name: $depotName) { + commit(rev: $revision) { + perforceChangelist { + cid + canonicalURL + } + } + } +} + +fragment DepotChangelistAuthor on GitCommit { + author { + date + person { + __typename + ...Avatar_Person + } + } +} diff --git a/client/web-sveltekit/src/testing/integration.ts b/client/web-sveltekit/src/testing/integration.ts index 24a91e1ad15..9ba5ac37fa2 100644 --- a/client/web-sveltekit/src/testing/integration.ts +++ b/client/web-sveltekit/src/testing/integration.ts @@ -63,6 +63,7 @@ const defaultMocks: TypeMocks = { GitCommit: () => ({ abbreviatedOID: faker.git.commitSha({ length: 7 }), subject: faker.git.commitMessage(), + perforceChangelist: null, }), JSONCString: () => '{}', } diff --git a/client/web/src/sveltekit/routes.ts b/client/web/src/sveltekit/routes.ts index 3502c14c1ba..130de93ab22 100644 --- a/client/web/src/sveltekit/routes.ts +++ b/client/web/src/sveltekit/routes.ts @@ -26,6 +26,11 @@ export const svelteKitRoutes: SvelteKitRoute[] = [ pattern: new RegExp('^/(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,})))(?:@(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,}))))?/-/tree(?:/.*)?/?$'), isRepoRoot: false, }, + { + id: '/[...repo=reporev]/(validrev)/-/changelists/[...path]', + pattern: new RegExp('^/(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,})))(?:@(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,}))))?/-/changelists(?:/.*)?/?$'), + isRepoRoot: false, + }, { id: '/[...repo=reporev]/(validrev)/-/commits/[...path]', pattern: new RegExp('^/(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,})))(?:@(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,}))))?/-/commits(?:/.*)?/?$'), @@ -41,6 +46,11 @@ export const svelteKitRoutes: SvelteKitRoute[] = [ pattern: new RegExp('^/(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,})))(?:@(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,}))))?/-/branches/all/?$'), isRepoRoot: false, }, + { + id: '/[...repo=reporev]/-/changelist/[changelistID]', + pattern: new RegExp('^/(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,})))(?:@(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,}))))?/-/changelist(?:/[^/]+)/?$'), + isRepoRoot: false, + }, { id: '/[...repo=reporev]/-/commit/[...revspec]', pattern: new RegExp('^/(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,})))(?:@(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,}))))?/-/commit(?:/.*)?/?$'), diff --git a/cmd/frontend/internal/app/ui/sveltekit/routes.go b/cmd/frontend/internal/app/ui/sveltekit/routes.go index a001d342264..c2ba77d257c 100644 --- a/cmd/frontend/internal/app/ui/sveltekit/routes.go +++ b/cmd/frontend/internal/app/ui/sveltekit/routes.go @@ -33,6 +33,11 @@ var svelteKitRoutes = []svelteKitRoute{ Pattern: regexp.MustCompile("^/(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,})))(?:@(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,}))))?/-/tree(?:/.*)?/?$"), Tag: tags.EnableOptIn | tags.EnableRollout, }, + { + Id: "/[...repo=reporev]/(validrev)/-/changelists/[...path]", + Pattern: regexp.MustCompile("^/(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,})))(?:@(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,}))))?/-/changelists(?:/.*)?/?$"), + Tag: tags.EnableOptIn | tags.EnableRollout, + }, { Id: "/[...repo=reporev]/(validrev)/-/commits/[...path]", Pattern: regexp.MustCompile("^/(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,})))(?:@(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,}))))?/-/commits(?:/.*)?/?$"), @@ -48,6 +53,11 @@ var svelteKitRoutes = []svelteKitRoute{ Pattern: regexp.MustCompile("^/(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,})))(?:@(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,}))))?/-/branches/all/?$"), Tag: tags.EnableOptIn | tags.EnableRollout, }, + { + Id: "/[...repo=reporev]/-/changelist/[changelistID]", + Pattern: regexp.MustCompile("^/(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,})))(?:@(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,}))))?/-/changelist(?:/[^/]+)/?$"), + Tag: tags.EnableOptIn, + }, { Id: "/[...repo=reporev]/-/commit/[...revspec]", Pattern: regexp.MustCompile("^/(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,})))(?:@(?:(?:(?:[^@/-]|(?:[^/@]{2,}))/)*(?:[^@/-]|(?:[^/@]{2,}))))?/-/commit(?:/.*)?/?$"),