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}
+
(expanded = !expanded)}
+ aria-label="{expanded ? 'Hide' : 'Show'} changelist message"
+ >
+
+
+ {/if}
+
+
+ submitted by {author.person.name}
+
+
+ {#if changelist.commit.body}
+
+ {#if $isViewportMobile}
+ {#if expanded}
+
(expanded = false)}>
+ Close
+
+ {:else}
+
(expanded = true)}>
+ Show changelist message
+
+ {/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(?:/.*)?/?$"),