@gitkraken/core-gitlens 0.4.101 → 0.5.101
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -6
- package/dist/git/models/graph.d.ts +63 -33
- package/dist/git/models/graph.d.ts.map +1 -1
- package/dist/git/models/graph.js.map +1 -1
- package/dist/git/models/graphSession.d.ts +3 -84
- package/dist/git/models/graphSession.d.ts.map +1 -1
- package/dist/git/models/graphSession.js +1 -6
- package/dist/git/models/graphSession.js.map +1 -1
- package/dist/git/models/issue.d.ts +4 -3
- package/dist/git/models/issue.d.ts.map +1 -1
- package/dist/git/models/issue.js.map +1 -1
- package/dist/git/providers/graph.d.ts +1 -15
- package/dist/git/providers/graph.d.ts.map +1 -1
- package/dist/git/providers/pausedOperations.d.ts +2 -0
- package/dist/git/providers/pausedOperations.d.ts.map +1 -1
- package/dist/git/utils/issue.utils.d.ts.map +1 -1
- package/dist/git/utils/issue.utils.js +8 -6
- package/dist/git/utils/issue.utils.js.map +1 -1
- package/dist/git/utils/reachability.utils.d.ts +1 -1
- package/dist/git/utils/reachability.utils.js +1 -1
- package/dist/git/utils/remote.utils.d.ts +3 -0
- package/dist/git/utils/remote.utils.d.ts.map +1 -1
- package/dist/git/utils/remote.utils.js +5 -0
- package/dist/git/utils/remote.utils.js.map +1 -1
- package/dist/git-cli/providers/graph.d.ts +1 -3
- package/dist/git-cli/providers/graph.d.ts.map +1 -1
- package/dist/git-cli/providers/graph.js +37 -214
- package/dist/git-cli/providers/graph.js.map +1 -1
- package/dist/git-cli/providers/pausedOperations.d.ts +1 -0
- package/dist/git-cli/providers/pausedOperations.d.ts.map +1 -1
- package/dist/git-cli/providers/pausedOperations.js +21 -1
- package/dist/git-cli/providers/pausedOperations.js.map +1 -1
- package/dist/git-cli/providers/staging.d.ts.map +1 -1
- package/dist/git-cli/providers/staging.js +10 -1
- package/dist/git-cli/providers/staging.js.map +1 -1
- package/dist/plus/git-github/api/github.d.ts.map +1 -1
- package/dist/plus/git-github/api/github.js +18 -5
- package/dist/plus/git-github/api/github.js.map +1 -1
- package/dist/plus/git-github/models.d.ts +6 -4
- package/dist/plus/git-github/models.d.ts.map +1 -1
- package/dist/plus/git-github/models.js +19 -26
- package/dist/plus/git-github/models.js.map +1 -1
- package/dist/plus/git-github/providers/github/graph.d.ts +1 -3
- package/dist/plus/git-github/providers/github/graph.d.ts.map +1 -1
- package/dist/plus/git-github/providers/github/graph.js +8 -10
- package/dist/plus/git-github/providers/github/graph.js.map +1 -1
- package/dist/plus/integrations/authentication/integrationAuthenticationProvider.d.ts +38 -48
- package/dist/plus/integrations/authentication/integrationAuthenticationProvider.d.ts.map +1 -1
- package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js +22 -5
- package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js.map +1 -1
- package/dist/plus/integrations/authentication/rejectedTokenTracker.d.ts +39 -0
- package/dist/plus/integrations/authentication/rejectedTokenTracker.d.ts.map +1 -0
- package/dist/plus/integrations/authentication/rejectedTokenTracker.js +53 -0
- package/dist/plus/integrations/authentication/rejectedTokenTracker.js.map +1 -0
- package/dist/plus/integrations/collectionMetadata.d.ts +21 -1
- package/dist/plus/integrations/collectionMetadata.d.ts.map +1 -1
- package/dist/plus/integrations/collectionMetadata.js +82 -6
- package/dist/plus/integrations/collectionMetadata.js.map +1 -1
- package/dist/plus/integrations/integrationService.d.ts +3 -3
- package/dist/plus/integrations/integrationService.d.ts.map +1 -1
- package/dist/plus/integrations/integrationService.js +19 -3
- package/dist/plus/integrations/integrationService.js.map +1 -1
- package/dist/plus/integrations/models/gitHostIntegration.d.ts.map +1 -1
- package/dist/plus/integrations/models/gitHostIntegration.js +19 -6
- package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
- package/dist/plus/integrations/models/integration.d.ts +20 -0
- package/dist/plus/integrations/models/integration.d.ts.map +1 -1
- package/dist/plus/integrations/models/integration.js +64 -6
- package/dist/plus/integrations/models/integration.js.map +1 -1
- package/dist/plus/integrations/models/issuesIntegration.js +5 -5
- package/dist/plus/integrations/models/issuesIntegration.js.map +1 -1
- package/dist/plus/integrations/providers/models.d.ts +1 -0
- package/dist/plus/integrations/providers/models.d.ts.map +1 -1
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/providers/providersApi.d.ts.map +1 -1
- package/dist/plus/integrations/providers/providersApi.js +4 -2
- package/dist/plus/integrations/providers/providersApi.js.map +1 -1
- package/dist/plus/integrations/providers/trello.d.ts +2 -0
- package/dist/plus/integrations/providers/trello.d.ts.map +1 -1
- package/dist/plus/integrations/providers/trello.js +1 -1
- package/dist/plus/integrations/providers/trello.js.map +1 -1
- package/dist/plus/integrations/providers/utils/providerPaging.d.ts +4 -2
- package/dist/plus/integrations/providers/utils/providerPaging.d.ts.map +1 -1
- package/dist/plus/integrations/providers/utils/providerPaging.js +34 -22
- package/dist/plus/integrations/providers/utils/providerPaging.js.map +1 -1
- package/dist/plus/integrations/reads/warnings.d.ts +4 -2
- package/dist/plus/integrations/reads/warnings.d.ts.map +1 -1
- package/dist/plus/integrations/reads/warnings.js +4 -2
- package/dist/plus/integrations/reads/warnings.js.map +1 -1
- package/dist/utils/date.d.ts +1 -1
- package/dist/utils/date.d.ts.map +1 -1
- package/dist/utils/date.js +5 -3
- package/dist/utils/date.js.map +1 -1
- package/package.json +2 -2
- package/src/git/models/graph.ts +59 -41
- package/src/git/models/graphSession.ts +3 -106
- package/src/git/models/issue.ts +3 -2
- package/src/git/providers/graph.ts +1 -15
- package/src/git/providers/pausedOperations.ts +8 -1
- package/src/git/utils/issue.utils.ts +9 -6
- package/src/git/utils/reachability.utils.ts +1 -1
- package/src/git/utils/remote.utils.ts +6 -0
- package/src/git-cli/providers/graph.ts +37 -250
- package/src/git-cli/providers/pausedOperations.ts +29 -2
- package/src/git-cli/providers/staging.ts +8 -1
- package/src/plus/git-github/api/github.ts +19 -9
- package/src/plus/git-github/models.ts +27 -31
- package/src/plus/git-github/providers/github/graph.ts +8 -15
- package/src/plus/integrations/authentication/integrationAuthenticationProvider.ts +62 -40
- package/src/plus/integrations/authentication/rejectedTokenTracker.ts +56 -0
- package/src/plus/integrations/collectionMetadata.ts +97 -7
- package/src/plus/integrations/integrationService.ts +37 -9
- package/src/plus/integrations/models/gitHostIntegration.ts +19 -6
- package/src/plus/integrations/models/integration.ts +71 -7
- package/src/plus/integrations/models/issuesIntegration.ts +5 -5
- package/src/plus/integrations/providers/models.ts +4 -1
- package/src/plus/integrations/providers/providersApi.ts +6 -10
- package/src/plus/integrations/providers/trello.ts +3 -2
- package/src/plus/integrations/providers/utils/providerPaging.ts +46 -24
- package/src/plus/integrations/reads/warnings.ts +4 -2
- package/src/utils/date.ts +6 -3
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
GitGraph,
|
|
3
|
-
GitGraphRow,
|
|
4
|
-
GitGraphRowStats,
|
|
5
|
-
GraphReachabilityTable,
|
|
6
|
-
IncrementalGraphFallbackReason,
|
|
7
|
-
} from './graph.js';
|
|
1
|
+
import type { GitGraph, GitGraphRow, IncrementalGraphFallbackReason } from './graph.js';
|
|
8
2
|
|
|
9
3
|
/**
|
|
10
4
|
* A provider-owned, stateful graph window for a single repo. It is the canonical accumulated window and
|
|
@@ -16,8 +10,8 @@ import type {
|
|
|
16
10
|
* `getGraph` incremental machinery (it internally builds the seed from the accumulated window, tips, and
|
|
17
11
|
* prior artifacts, with the same shape gating), {@link more} delegates to the prior result's `more()`,
|
|
18
12
|
* and {@link current} mirrors today's `GitGraph` result shape so the publisher/wire semantics stay
|
|
19
|
-
* byte-identical. R7b (refresh-result-driven channel marking)
|
|
20
|
-
*
|
|
13
|
+
* byte-identical. R7b (refresh-result-driven channel marking) extends this without breaking —
|
|
14
|
+
* {@link refresh} returns a result object, never a bare `GitGraph`.
|
|
21
15
|
*/
|
|
22
16
|
export interface GitGraphSession {
|
|
23
17
|
readonly repoPath: string;
|
|
@@ -47,106 +41,9 @@ export interface GitGraphSession {
|
|
|
47
41
|
*/
|
|
48
42
|
more(limit?: number, targetId?: string, cancellation?: AbortSignal): Promise<boolean>;
|
|
49
43
|
|
|
50
|
-
/**
|
|
51
|
-
* Snapshot the canonical window for restart persistence, or `undefined` when there's nothing worth
|
|
52
|
-
* persisting — an empty window, or a provider without an incremental restore path (e.g. GitHub, which
|
|
53
|
-
* stamps no ref tips for the restore's tip-diff gate). The host writes this into the repo's own git dir
|
|
54
|
-
* (`<commonGitDir>/gitlens/graph/` — a repo-derived cache, like git's commit-graph file) on an idle
|
|
55
|
-
* debounce / dispose; a later {@link GitGraphSubProvider.openGraphSession} `restore` reconstructs +
|
|
56
|
-
* refreshes it. Never trusted over git — restore always re-refreshes (see {@link GitGraphSessionSnapshot}).
|
|
57
|
-
*/
|
|
58
|
-
serialize(): GitGraphSessionSnapshot | undefined;
|
|
59
|
-
|
|
60
44
|
dispose(): void;
|
|
61
45
|
}
|
|
62
46
|
|
|
63
|
-
/**
|
|
64
|
-
* Schema version for {@link GitGraphSessionSnapshot}. Bump on ANY shape change (field add/remove/rename or a
|
|
65
|
-
* semantics change): a restore whose snapshot version doesn't match this EXACTLY is discarded (a normal
|
|
66
|
-
* initial walk), so an old-shaped cache written by a prior extension version can never be misread.
|
|
67
|
-
*/
|
|
68
|
-
export const graphSessionSnapshotVersion = 3;
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Restart-persistence snapshot of a {@link GitGraphSession}'s canonical window — everything a restore needs
|
|
72
|
-
* to reconstruct the session's prior generation as an R6 incremental SEED, so a cold open on an unchanged
|
|
73
|
-
* repo is ≈ deserialize + one enumeration instead of a full walk.
|
|
74
|
-
*
|
|
75
|
-
* NEVER trusted over git: on restore the session reconstructs this as its window/tips/builders WITHOUT any
|
|
76
|
-
* git, then IMMEDIATELY {@link GitGraphSession.refresh | refreshes} — the same enumeration + tip-diff + FF +
|
|
77
|
-
* stash gates reconcile a stale snapshot (fast when unchanged, a full walk on any structural change), and any
|
|
78
|
-
* parse/validation failure discards it entirely for a normal initial walk.
|
|
79
|
-
*
|
|
80
|
-
* Rows are persisted as-is: they're already the JSON-safe wire shapes (strings/numbers/plain objects — the
|
|
81
|
-
* graph's Maps live at the graph level, not on rows, and the transient per-row `reachability` is stripped
|
|
82
|
-
* before a row is emitted). Maps are persisted as entry arrays.
|
|
83
|
-
*/
|
|
84
|
-
export interface GitGraphSessionSnapshot {
|
|
85
|
-
/** Must equal {@link graphSessionSnapshotVersion}; any mismatch discards the snapshot. */
|
|
86
|
-
readonly v: number;
|
|
87
|
-
readonly repoPath: string;
|
|
88
|
-
/** Walk shape the window was produced under (`${ordering}|${onlyFollowFirstParent}`). A restore is
|
|
89
|
-
* discarded when this disagrees with the current config's shape — the cached rows can't be reused. */
|
|
90
|
-
readonly buildShape: string;
|
|
91
|
-
readonly ordering: 'date' | 'author-date' | 'topo';
|
|
92
|
-
readonly onlyFollowFirstParent: boolean;
|
|
93
|
-
/** The persisted window rows, in walk order — a TOP slice capped at a bounded row count (see the host
|
|
94
|
-
* store); a longer window persists its top slice with {@link hasMore} forced true so the bottom re-pages. */
|
|
95
|
-
readonly rows: GitGraphRow[];
|
|
96
|
-
/** Ref tips as of the persisted walk: `[canonical refname, peeled tip sha]` entries — the map the restore
|
|
97
|
-
* refresh diffs against current git to find the structural changes that force a full fallback. */
|
|
98
|
-
readonly refTips: [string, string][];
|
|
99
|
-
/** {@link GitGraph.decorationFingerprint} as of the persisted walk — the restore refresh diffs it against
|
|
100
|
-
* current git so a metadata-only change while closed (upstream/default/worktree/remote/user) still forces
|
|
101
|
-
* the full fallback that rebuilds row decorations. Absent ⇒ never matches ⇒ safe full fallback. */
|
|
102
|
-
readonly decorationFingerprint?: string;
|
|
103
|
-
/** Shared reachability table for the window (the primary reachability representation — rows carry only a
|
|
104
|
-
* `contexts.reachabilityIndex` into its `sets`). The restore refresh CONTINUES it (stable indices). */
|
|
105
|
-
readonly reachability?: GraphReachabilityTable;
|
|
106
|
-
/** Per-sha immutable stats `[sha, stats]` entries for the persisted rows (present only when stats were
|
|
107
|
-
* included in the walk). */
|
|
108
|
-
readonly rowsStats?: [string, GitGraphRowStats][];
|
|
109
|
-
/** Downstreams `[upstream name, tracking branch names]` entries (for the refresh's change diffing). */
|
|
110
|
-
readonly downstreams: [string, string[]][];
|
|
111
|
-
/** Whether the persisted window had more rows below it — genuinely paged OR truncated by the cap. */
|
|
112
|
-
readonly hasMore: boolean;
|
|
113
|
-
/** Whether the persisted walk included per-row stats. */
|
|
114
|
-
readonly includesStats: boolean;
|
|
115
|
-
/** Whether the repo was a shallow clone when the window was walked. The restore refresh diffs this
|
|
116
|
-
* against current git — an un-shallow (or re-shallow) while closed forces a full fallback. */
|
|
117
|
-
readonly shallow: boolean;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/** Why a restore discarded its snapshot (reported via {@link GraphSessionRestoreResult.reason}). */
|
|
121
|
-
export type GraphSessionRestoreDiscardReason =
|
|
122
|
-
| 'schema'
|
|
123
|
-
| 'repo-path'
|
|
124
|
-
| 'shape'
|
|
125
|
-
| 'empty'
|
|
126
|
-
| 'tips'
|
|
127
|
-
| 'rows'
|
|
128
|
-
| 'reachability'
|
|
129
|
-
| 'downstreams'
|
|
130
|
-
| 'rowsStats'
|
|
131
|
-
// A throw past structural validation (e.g. the restore refresh's enumerate chokes on a garbage rev).
|
|
132
|
-
| 'corrupt';
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Outcome of a restore attempt, reported via the {@link GitGraphSubProvider.openGraphSession} `onRestore`
|
|
136
|
-
* callback so the host can emit its single, assertable INFO line. `restored` is true only when the snapshot
|
|
137
|
-
* validated and seeded the (always-run) refresh — `refresh` then carries that refresh's path so the host can
|
|
138
|
-
* distinguish `→ refresh fast (+M)` from `→ refresh full (<reason>)`.
|
|
139
|
-
*/
|
|
140
|
-
export interface GraphSessionRestoreResult {
|
|
141
|
-
readonly restored: boolean;
|
|
142
|
-
/** Present when `!restored`: why the snapshot was discarded; the host logs `miss (<reason>)`. */
|
|
143
|
-
readonly reason?: GraphSessionRestoreDiscardReason;
|
|
144
|
-
/** Present when `restored`: how many window rows the snapshot carried. */
|
|
145
|
-
readonly rows?: number;
|
|
146
|
-
/** Present when `restored`: the post-restore refresh outcome (a restore ALWAYS refreshes to current truth). */
|
|
147
|
-
readonly refresh?: GitGraphSessionRefreshResult;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
47
|
export interface GitGraphSessionRefreshOptions {
|
|
151
48
|
/** Rebuild anchor / find target — the loaded window's bottom sha (pins the walk's bottom boundary),
|
|
152
49
|
* or a selection/centering hint. Mirrors the `rev` the host passed to `getGraph` today. */
|
package/src/git/models/issue.ts
CHANGED
|
@@ -8,7 +8,8 @@ import type { RepositoryIdentityDescriptor } from './repositoryIdentities.js';
|
|
|
8
8
|
export type IssueStateFilter = 'open' | 'closed' | 'all';
|
|
9
9
|
|
|
10
10
|
export interface IssueShape extends IssueOrPullRequest {
|
|
11
|
-
author
|
|
11
|
+
/** `undefined` when the provider can't resolve the author, e.g. a deleted GitHub account */
|
|
12
|
+
author: IssueMember | undefined;
|
|
12
13
|
assignees: IssueMember[];
|
|
13
14
|
repository?: IssueRepository;
|
|
14
15
|
labels?: IssueLabel[];
|
|
@@ -31,7 +32,7 @@ export class Issue implements IssueShape {
|
|
|
31
32
|
public readonly updatedDate: Date,
|
|
32
33
|
public readonly closed: boolean,
|
|
33
34
|
public readonly state: IssueOrPullRequestState,
|
|
34
|
-
public readonly author: IssueMember,
|
|
35
|
+
public readonly author: IssueMember | undefined,
|
|
35
36
|
public readonly assignees: IssueMember[],
|
|
36
37
|
public readonly repository?: IssueRepository,
|
|
37
38
|
public readonly closedDate?: Date,
|
|
@@ -12,7 +12,7 @@ import type {
|
|
|
12
12
|
GitGraphSearchProgress,
|
|
13
13
|
GitGraphSearchResults,
|
|
14
14
|
} from '../models/graphSearch.js';
|
|
15
|
-
import type { GitGraphSession
|
|
15
|
+
import type { GitGraphSession } from '../models/graphSession.js';
|
|
16
16
|
import type { SearchQuery } from '../models/search.js';
|
|
17
17
|
|
|
18
18
|
export interface GitGraphSubProvider {
|
|
@@ -30,20 +30,6 @@ export interface GitGraphSubProvider {
|
|
|
30
30
|
rev?: string;
|
|
31
31
|
limit?: number;
|
|
32
32
|
include?: { stats?: boolean };
|
|
33
|
-
/**
|
|
34
|
-
* Restart-persistence snapshot to seed the session from (see {@link GitGraphSessionSnapshot}). When
|
|
35
|
-
* present and structurally valid, the session reconstructs it as its prior generation WITHOUT any git,
|
|
36
|
-
* then immediately refreshes to current truth — so a cold open on an unchanged repo is ≈ deserialize +
|
|
37
|
-
* one enumeration. Any validation/parse failure is ignored (a normal initial walk). Never trusted over
|
|
38
|
-
* git. Providers without an incremental restore path (e.g. GitHub) ignore it.
|
|
39
|
-
*/
|
|
40
|
-
restore?: GitGraphSessionSnapshot;
|
|
41
|
-
/**
|
|
42
|
-
* Reports the restore outcome (validated + refreshed, or discarded with a reason) so the host can log
|
|
43
|
-
* a single assertable line. Called at most once, only when `restore` was provided. See
|
|
44
|
-
* {@link GraphSessionRestoreResult}.
|
|
45
|
-
*/
|
|
46
|
-
onRestore?: (result: GraphSessionRestoreResult) => void;
|
|
47
33
|
},
|
|
48
34
|
cancellation?: AbortSignal,
|
|
49
35
|
): Promise<GitGraphSession>;
|
|
@@ -7,5 +7,12 @@ export interface GitPausedOperationsSubProvider {
|
|
|
7
7
|
cancellation?: AbortSignal,
|
|
8
8
|
): Promise<GitPausedOperationStatus | undefined>;
|
|
9
9
|
abortPausedOperation(repoPath: string, options?: { quit?: boolean }): Promise<void>;
|
|
10
|
-
continuePausedOperation(
|
|
10
|
+
continuePausedOperation(
|
|
11
|
+
repoPath: string,
|
|
12
|
+
options?: {
|
|
13
|
+
skip?: boolean;
|
|
14
|
+
/** Editor command used if the operation needs to edit a commit message (e.g. `true` to keep the original message without opening an editor) */
|
|
15
|
+
messageEditor?: string;
|
|
16
|
+
},
|
|
17
|
+
): Promise<void>;
|
|
11
18
|
}
|
|
@@ -38,12 +38,15 @@ export function serializeIssue(value: IssueShape): IssueShape {
|
|
|
38
38
|
closedDate: value.closedDate,
|
|
39
39
|
closed: value.closed,
|
|
40
40
|
state: value.state,
|
|
41
|
-
author:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
author:
|
|
42
|
+
value.author == null
|
|
43
|
+
? undefined
|
|
44
|
+
: {
|
|
45
|
+
id: value.author.id,
|
|
46
|
+
name: value.author.name,
|
|
47
|
+
avatarUrl: value.author.avatarUrl,
|
|
48
|
+
url: value.author.url,
|
|
49
|
+
},
|
|
47
50
|
repository:
|
|
48
51
|
value.repository == null
|
|
49
52
|
? undefined
|
|
@@ -10,7 +10,7 @@ type ReachableRef = GitCommitReachability['refs'][number];
|
|
|
10
10
|
* set and the walk's HEAD-derived sets. Single-sourced so the host row processor
|
|
11
11
|
* (`GlGraphRowProcessor`), the R6b incremental fast path, and the equivalence harness all compute
|
|
12
12
|
* byte-identical flags (no manual mirrors that can drift). Stash rows carry no flags — callers skip this
|
|
13
|
-
* for `stash
|
|
13
|
+
* for `stash` rows. See {@link GitGraphRowContextFlags} for the per-bit semantics.
|
|
14
14
|
*/
|
|
15
15
|
export function computeGraphRowContextFlags(
|
|
16
16
|
sha: string,
|
|
@@ -7,6 +7,12 @@ export function getDefaultRemoteOrHighlander<T extends GitRemote>(remotes: T[]):
|
|
|
7
7
|
return remotes.length === 1 ? remotes[0] : remotes.find(r => r.default);
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
/** Like {@link getDefaultRemoteOrHighlander}, but falls back to `origin` when no remote is designated
|
|
11
|
+
* default — otherwise features keyed on "the default remote" silently do nothing on multi-remote repos */
|
|
12
|
+
export function getDefaultRemoteOrOrigin<T extends GitRemote>(remotes: T[]): T | undefined {
|
|
13
|
+
return getDefaultRemoteOrHighlander(remotes) ?? remotes.find(r => r.name === 'origin');
|
|
14
|
+
}
|
|
15
|
+
|
|
10
16
|
export function getHighlanderProviderName(remotes: GitRemote<RemoteProvider>[]): string | undefined {
|
|
11
17
|
if (remotes.length === 0) return undefined;
|
|
12
18
|
|
|
@@ -25,15 +25,11 @@ import type {
|
|
|
25
25
|
GitGraphSearchResultData,
|
|
26
26
|
GitGraphSearchResults,
|
|
27
27
|
} from '../../git/models/graphSearch.js';
|
|
28
|
-
import { graphSessionSnapshotVersion } from '../../git/models/graphSession.js';
|
|
29
28
|
import type {
|
|
30
29
|
GitGraphSession,
|
|
31
30
|
GitGraphSessionChangedChannels,
|
|
32
31
|
GitGraphSessionRefreshOptions,
|
|
33
32
|
GitGraphSessionRefreshResult,
|
|
34
|
-
GitGraphSessionSnapshot,
|
|
35
|
-
GraphSessionRestoreDiscardReason,
|
|
36
|
-
GraphSessionRestoreResult,
|
|
37
33
|
} from '../../git/models/graphSession.js';
|
|
38
34
|
import type { GitRemote } from '../../git/models/remote.js';
|
|
39
35
|
import type { SearchQuery } from '../../git/models/search.js';
|
|
@@ -195,130 +191,12 @@ function downstreamsChanged(prior: ReadonlyMap<string, string[]>, next: Readonly
|
|
|
195
191
|
return false;
|
|
196
192
|
}
|
|
197
193
|
|
|
198
|
-
/**
|
|
199
|
-
* Cap on the number of window rows a {@link GraphSession.serialize} persists. A longer window persists only
|
|
200
|
-
* its TOP slice with `hasMore` forced true (restore then behaves as a shorter loaded window — the bottom
|
|
201
|
-
* re-pages on demand). Pragmatic: generous enough that a typical session restores its whole window, bounded
|
|
202
|
-
* enough that the JSON file stays sub-MB-class (rows are a few hundred bytes each).
|
|
203
|
-
*/
|
|
204
|
-
const maxPersistedGraphRows = 2000;
|
|
205
|
-
|
|
206
194
|
/** The walk-shape key (`ordering|onlyFollowFirstParent`) an incremental seed is gated on — one builder so the
|
|
207
|
-
*
|
|
195
|
+
* refresh/rebuild sites can't drift in how they stringify it. */
|
|
208
196
|
function buildShapeKey(shape: { ordering: string; onlyFollowFirstParent: boolean }): string {
|
|
209
197
|
return `${shape.ordering}|${shape.onlyFollowFirstParent}`;
|
|
210
198
|
}
|
|
211
199
|
|
|
212
|
-
/**
|
|
213
|
-
* Validates a persisted {@link GitGraphSessionSnapshot} structurally before it's trusted to seed a restore.
|
|
214
|
-
* Returns the discard reason on any failure (→ the caller ignores the snapshot and does a normal initial
|
|
215
|
-
* walk), or `undefined` when it's coherent. Never throws — a corrupt/adversarial cache must degrade to a
|
|
216
|
-
* full walk, never crash the open. Deep validation (does the cache match current git?) is NOT done here:
|
|
217
|
-
* that's the restore's immediate refresh, which reconciles a stale-but-valid snapshot against ground truth.
|
|
218
|
-
*/
|
|
219
|
-
function validateGraphSnapshot(
|
|
220
|
-
snapshot: GitGraphSessionSnapshot,
|
|
221
|
-
repoPath: string,
|
|
222
|
-
currentBuildShape: string,
|
|
223
|
-
): GraphSessionRestoreDiscardReason | undefined {
|
|
224
|
-
if (snapshot.v !== graphSessionSnapshotVersion) return 'schema';
|
|
225
|
-
if (snapshot.repoPath !== repoPath) return 'repo-path';
|
|
226
|
-
// A shape change reshapes every row's parents/order — the cached rows can't be reused.
|
|
227
|
-
if (snapshot.buildShape !== currentBuildShape) return 'shape';
|
|
228
|
-
if (!Array.isArray(snapshot.rows) || snapshot.rows.length === 0) return 'empty';
|
|
229
|
-
if (
|
|
230
|
-
!Array.isArray(snapshot.refTips) ||
|
|
231
|
-
snapshot.refTips.some(
|
|
232
|
-
t => !Array.isArray(t) || t.length !== 2 || typeof t[0] !== 'string' || typeof t[1] !== 'string',
|
|
233
|
-
)
|
|
234
|
-
) {
|
|
235
|
-
return 'tips';
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Each row must be structurally sane — a corrupt row poisons the seed / stitch math. Duplicate shas break
|
|
239
|
-
// the ids set / stitch dedup (they'd re-appear as "already seen"), so reject them too.
|
|
240
|
-
const seenShas = new Set<string>();
|
|
241
|
-
for (const row of snapshot.rows) {
|
|
242
|
-
if (row == null || typeof row.sha !== 'string' || !Array.isArray(row.parents) || typeof row.date !== 'number') {
|
|
243
|
-
return 'rows';
|
|
244
|
-
}
|
|
245
|
-
if (seenShas.has(row.sha)) return 'rows';
|
|
246
|
-
|
|
247
|
-
seenShas.add(row.sha);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Every row's `reachabilityIndex` must be a valid index into the table's `sets` (the decode reads it
|
|
251
|
-
// directly). Guards against a truncated table / mismatched pairing.
|
|
252
|
-
const reachability = snapshot.reachability;
|
|
253
|
-
if (reachability != null && (!Array.isArray(reachability.sets) || !Array.isArray(reachability.dictionary))) {
|
|
254
|
-
return 'reachability';
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const setCount = reachability?.sets?.length ?? 0;
|
|
258
|
-
for (const row of snapshot.rows) {
|
|
259
|
-
const index = row.contexts?.reachabilityIndex;
|
|
260
|
-
if (index != null && (!Number.isInteger(index) || index < 0 || index >= setCount)) return 'reachability';
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// Downstreams must be `[upstream name, tracking branch names]` entries (reconstructed into a Map on restore).
|
|
264
|
-
if (
|
|
265
|
-
!Array.isArray(snapshot.downstreams) ||
|
|
266
|
-
snapshot.downstreams.some(
|
|
267
|
-
d => !Array.isArray(d) || d.length !== 2 || typeof d[0] !== 'string' || !Array.isArray(d[1]),
|
|
268
|
-
)
|
|
269
|
-
) {
|
|
270
|
-
return 'downstreams';
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// Per-sha stats, when present, must be `[sha, stats-object]` entries (reconstructed into a Map on restore).
|
|
274
|
-
if (
|
|
275
|
-
snapshot.rowsStats != null &&
|
|
276
|
-
(!Array.isArray(snapshot.rowsStats) ||
|
|
277
|
-
snapshot.rowsStats.some(
|
|
278
|
-
s =>
|
|
279
|
-
!Array.isArray(s) ||
|
|
280
|
-
s.length !== 2 ||
|
|
281
|
-
typeof s[0] !== 'string' ||
|
|
282
|
-
typeof s[1] !== 'object' ||
|
|
283
|
-
s[1] == null,
|
|
284
|
-
))
|
|
285
|
-
) {
|
|
286
|
-
return 'rowsStats';
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
return undefined;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Reconstruct a persisted snapshot into the synthetic `GitGraph` a {@link GraphSession.restore} hands to its
|
|
294
|
-
* immediate refresh as the PRIOR generation — carrying exactly the fields the refresh's seed construction +
|
|
295
|
-
* change-diffing read (`refTips`, `reachability`, `rowsStats`, `downstreams`, `paging.hasMore`). Avatars are
|
|
296
|
-
* deliberately empty: resolved avatar URLs are webview-instance-bound and don't survive a restart, so the
|
|
297
|
-
* refresh re-derives them (and the write-once merge over an empty prior map is a no-op). The side-queried
|
|
298
|
-
* collections (`branches`/`remotes`/`stashes`/`worktrees`) aren't read by the refresh, so they're left empty.
|
|
299
|
-
*/
|
|
300
|
-
function buildRestoredGraph(snapshot: GitGraphSessionSnapshot): GitGraph {
|
|
301
|
-
return {
|
|
302
|
-
repoPath: snapshot.repoPath,
|
|
303
|
-
avatars: new Map<string, string>(),
|
|
304
|
-
ids: new Set<string>(snapshot.rows.map(r => r.sha)),
|
|
305
|
-
includes: snapshot.includesStats ? { stats: true } : undefined,
|
|
306
|
-
branches: new Map<string, GitBranch>(),
|
|
307
|
-
remotes: new Map<string, GitRemote>(),
|
|
308
|
-
downstreams: new Map(snapshot.downstreams),
|
|
309
|
-
stashes: undefined,
|
|
310
|
-
worktrees: undefined,
|
|
311
|
-
worktreesByBranch: undefined,
|
|
312
|
-
reachability: snapshot.reachability,
|
|
313
|
-
refTips: new Map(snapshot.refTips),
|
|
314
|
-
decorationFingerprint: snapshot.decorationFingerprint,
|
|
315
|
-
shallow: snapshot.shallow,
|
|
316
|
-
rows: snapshot.rows,
|
|
317
|
-
rowsStats: snapshot.rowsStats != null ? new Map(snapshot.rowsStats) : undefined,
|
|
318
|
-
paging: { limit: snapshot.rows.length, startingCursor: undefined, hasMore: snapshot.hasMore },
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
|
|
322
200
|
/** The graph log parser's per-record shape (see `graphMapping`), narrowed to the fields the row builder reads. */
|
|
323
201
|
type GraphCommitRecord = {
|
|
324
202
|
sha: string;
|
|
@@ -405,8 +283,6 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
|
|
|
405
283
|
rev?: string;
|
|
406
284
|
limit?: number;
|
|
407
285
|
include?: { stats?: boolean };
|
|
408
|
-
restore?: GitGraphSessionSnapshot;
|
|
409
|
-
onRestore?: (result: GraphSessionRestoreResult) => void;
|
|
410
286
|
},
|
|
411
287
|
cancellation?: AbortSignal,
|
|
412
288
|
): Promise<GitGraphSession> {
|
|
@@ -422,30 +298,6 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
|
|
|
422
298
|
};
|
|
423
299
|
const session = new GraphSession(this, repoPath, options?.rowProcessor, getWalkShape);
|
|
424
300
|
|
|
425
|
-
// R7c restart persistence: seed from a persisted snapshot when it's structurally valid for THIS repo +
|
|
426
|
-
// walk shape, then immediately refresh (the snapshot is exactly an R6 seed — enumeration + the tip/FF/
|
|
427
|
-
// stash gates reconcile a stale one). Any validation failure discards it and falls through to a normal
|
|
428
|
-
// initial walk. Never trusted over git.
|
|
429
|
-
const restore = options?.restore;
|
|
430
|
-
if (restore != null) {
|
|
431
|
-
const shape = getWalkShape();
|
|
432
|
-
const reason = validateGraphSnapshot(restore, repoPath, buildShapeKey(shape));
|
|
433
|
-
if (reason == null) {
|
|
434
|
-
try {
|
|
435
|
-
const refresh = await session.restore(restore, options, cancellation);
|
|
436
|
-
options?.onRestore?.({ restored: true, rows: restore.rows.length, refresh: refresh });
|
|
437
|
-
return session;
|
|
438
|
-
} catch {
|
|
439
|
-
// Belt-and-suspenders: structural validation can't catch a snapshot that reconstructs but
|
|
440
|
-
// throws in the restore's refresh (e.g. a garbage rev its enumerate chokes on). A corrupt
|
|
441
|
-
// cache must degrade to a full walk, never crash the open — report a miss and fall through.
|
|
442
|
-
options?.onRestore?.({ restored: false, reason: 'corrupt' });
|
|
443
|
-
}
|
|
444
|
-
} else {
|
|
445
|
-
options?.onRestore?.({ restored: false, reason: reason });
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
301
|
await session.initialize(options, cancellation);
|
|
450
302
|
return session;
|
|
451
303
|
}
|
|
@@ -810,6 +662,9 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
|
|
|
810
662
|
// `defaultLocalName!` is non-null here because `tip === defaultBranchName` short-circuits when `defaultBranchName` is null.
|
|
811
663
|
isDefault: tip === defaultBranchName && !branchMap.has(defaultLocalName!),
|
|
812
664
|
hostingServiceType: remote.provider?.gkProviderId,
|
|
665
|
+
// `branchMap` is keyed by full name, so the remote ref resolves here — the same
|
|
666
|
+
// lookup the host does today to bake `+starred` into the context string.
|
|
667
|
+
starred: branchMap.get(tip)?.starred || undefined,
|
|
813
668
|
});
|
|
814
669
|
continue;
|
|
815
670
|
}
|
|
@@ -823,7 +678,11 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
|
|
|
823
678
|
(branchId === branchIdOfMainWorktree ? mainWorktree : undefined);
|
|
824
679
|
const worktreeRef =
|
|
825
680
|
worktree != null
|
|
826
|
-
? {
|
|
681
|
+
? {
|
|
682
|
+
id: getWorktreeId(repoPath, worktree.name),
|
|
683
|
+
path: worktree.path,
|
|
684
|
+
isDefault: worktree.isDefault === true,
|
|
685
|
+
}
|
|
827
686
|
: undefined;
|
|
828
687
|
refHeads.push({
|
|
829
688
|
id: branchId,
|
|
@@ -831,11 +690,19 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
|
|
|
831
690
|
isCurrentHead: head,
|
|
832
691
|
upstream:
|
|
833
692
|
branch?.upstream != null
|
|
834
|
-
? {
|
|
693
|
+
? {
|
|
694
|
+
name: branch.upstream.name,
|
|
695
|
+
id: getBranchId(repoPath, true, branch.upstream.name),
|
|
696
|
+
missing: branch.upstream.missing,
|
|
697
|
+
state: {
|
|
698
|
+
ahead: branch.upstream.state.ahead,
|
|
699
|
+
behind: branch.upstream.state.behind,
|
|
700
|
+
},
|
|
701
|
+
}
|
|
835
702
|
: undefined,
|
|
836
703
|
worktree: worktreeRef,
|
|
837
|
-
worktreeId: worktree?.isDefault === false ? worktreeRef?.id : undefined,
|
|
838
704
|
isDefault: defaultLocalName != null && tip === defaultLocalName,
|
|
705
|
+
starred: branch?.starred || undefined,
|
|
839
706
|
});
|
|
840
707
|
}
|
|
841
708
|
}
|
|
@@ -853,10 +720,11 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
|
|
|
853
720
|
date: Number(ordering === 'author-date' ? commit.authorDate : commit.committerDate) * 1000,
|
|
854
721
|
commitDate: Number(commit.committerDate) * 1000,
|
|
855
722
|
message: (stash.message ?? commit.message).trim(),
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
723
|
+
kind: 'stash',
|
|
724
|
+
stashNumber: stash.stashNumber,
|
|
725
|
+
heads: refHeads.length ? refHeads : undefined,
|
|
726
|
+
remotes: refRemoteHeads.length ? refRemoteHeads : undefined,
|
|
727
|
+
tags: refTags.length ? refTags : undefined,
|
|
860
728
|
isCurrentUser: true,
|
|
861
729
|
};
|
|
862
730
|
// Don't seed `rowStats` from `stash.stats` here: the graph's stash list is fetched with
|
|
@@ -873,10 +741,10 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
|
|
|
873
741
|
date: Number(ordering === 'author-date' ? commit.authorDate : commit.committerDate) * 1000,
|
|
874
742
|
commitDate: Number(commit.committerDate) * 1000,
|
|
875
743
|
message: commit.message.trim(),
|
|
876
|
-
|
|
877
|
-
heads: refHeads,
|
|
878
|
-
remotes: refRemoteHeads,
|
|
879
|
-
tags: refTags,
|
|
744
|
+
kind: parents.length > 1 ? 'merge' : 'commit',
|
|
745
|
+
heads: refHeads.length ? refHeads : undefined,
|
|
746
|
+
remotes: refRemoteHeads.length ? refRemoteHeads : undefined,
|
|
747
|
+
tags: refTags.length ? refTags : undefined,
|
|
880
748
|
isCurrentUser: isCurrentUser || undefined,
|
|
881
749
|
};
|
|
882
750
|
if (commit.stats != null) {
|
|
@@ -1035,7 +903,7 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
|
|
|
1035
903
|
continue;
|
|
1036
904
|
}
|
|
1037
905
|
|
|
1038
|
-
if (row.
|
|
906
|
+
if (row.kind === 'stash') {
|
|
1039
907
|
missingStashStdin += `${row.sha}\n`;
|
|
1040
908
|
} else {
|
|
1041
909
|
missingStdin += `${row.sha}\n`;
|
|
@@ -1215,7 +1083,7 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
|
|
|
1215
1083
|
refRemoteHeads,
|
|
1216
1084
|
refTags,
|
|
1217
1085
|
head,
|
|
1218
|
-
row.
|
|
1086
|
+
row.kind === 'stash',
|
|
1219
1087
|
);
|
|
1220
1088
|
// Transient: the row processor's `+unique` decision reads this; stripped by finalize.
|
|
1221
1089
|
// Unsorted (the consumer re-sorts after decoding) — order doesn't affect interning.
|
|
@@ -1446,7 +1314,7 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
|
|
|
1446
1314
|
// Stash-set gate: any push/pop/drop changes the injected rows.
|
|
1447
1315
|
const currentStashShas =
|
|
1448
1316
|
gitStash?.stashes != null ? new Set<string>(gitStash.stashes.keys()) : new Set<string>();
|
|
1449
|
-
const priorStashShas = seed.rows.filter(r => r.
|
|
1317
|
+
const priorStashShas = seed.rows.filter(r => r.kind === 'stash').map(r => r.sha);
|
|
1450
1318
|
if (priorStashShas.length !== currentStashShas.size || priorStashShas.some(s => !currentStashShas.has(s))) {
|
|
1451
1319
|
fallback('stash-changed');
|
|
1452
1320
|
return undefined;
|
|
@@ -1456,9 +1324,9 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
|
|
|
1456
1324
|
// upstreams, worktree assignments, remote urls/providers, current user) that can change without
|
|
1457
1325
|
// moving any ref tip — e.g. `git remote set-head`, `branch --set-upstream-to`, `worktree add`.
|
|
1458
1326
|
// Reused rows keep their prior decorations (only flags/reachability are re-derived below), so ANY
|
|
1459
|
-
// change here must take the full walk that rebuilds them. An absent seed fingerprint
|
|
1460
|
-
//
|
|
1461
|
-
//
|
|
1327
|
+
// change here must take the full walk that rebuilds them. An absent seed fingerprint never matches —
|
|
1328
|
+
// safe full fallback. Deliberately LAST of the gates so the structural gates above keep their precise
|
|
1329
|
+
// reasons (a deleted tracking branch reports `ref-deleted`, not this).
|
|
1462
1330
|
if (seed.decorationFingerprint !== decorationFingerprint) {
|
|
1463
1331
|
fallback('metadata-changed');
|
|
1464
1332
|
return undefined;
|
|
@@ -1617,7 +1485,7 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
|
|
|
1617
1485
|
row.remotes ?? [],
|
|
1618
1486
|
row.tags ?? [],
|
|
1619
1487
|
row.sha === currentHeadSha,
|
|
1620
|
-
row.
|
|
1488
|
+
row.kind === 'stash',
|
|
1621
1489
|
);
|
|
1622
1490
|
if (freshShas.has(row.sha)) {
|
|
1623
1491
|
// Fresh rows are raw — run the full processor (contexts, emojify, avatar). Reused rows keep
|
|
@@ -1627,7 +1495,7 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
|
|
|
1627
1495
|
}
|
|
1628
1496
|
// Authoritative flags for every commit row (membership can shift on reused rows too — e.g. a new
|
|
1629
1497
|
// interior branch flips `+unique`). Stash rows carry none.
|
|
1630
|
-
if (row.
|
|
1498
|
+
if (row.kind !== 'stash') {
|
|
1631
1499
|
(row.contexts ??= {}).flags = computeGraphRowContextFlags(row.sha, refs?.values(), graphCtx);
|
|
1632
1500
|
}
|
|
1633
1501
|
finalizeRowReachability(row, row.sha, refs);
|
|
@@ -2112,39 +1980,6 @@ class GraphSession implements GitGraphSession {
|
|
|
2112
1980
|
this.applyRebuild(graph, shape);
|
|
2113
1981
|
}
|
|
2114
1982
|
|
|
2115
|
-
/**
|
|
2116
|
-
* R7c restart persistence. Reconstruct the prior generation from a persisted snapshot as an R6 seed
|
|
2117
|
-
* WITHOUT any git (its window/tips/reachability/stats become this session's prior `current`/`window`/
|
|
2118
|
-
* `buildShape`), then IMMEDIATELY {@link refresh} to current truth — the enumeration + tip/FF/stash gates
|
|
2119
|
-
* reconcile a stale snapshot (fast when unchanged, a full walk on any structural change). The session
|
|
2120
|
-
* handed back is ALWAYS post-refresh; the raw snapshot is never surfaced. The caller has already validated
|
|
2121
|
-
* the snapshot structurally. Returns the refresh outcome so the caller can report the restore result.
|
|
2122
|
-
*/
|
|
2123
|
-
async restore(
|
|
2124
|
-
snapshot: GitGraphSessionSnapshot,
|
|
2125
|
-
options?: { rev?: string; limit?: number; include?: { stats?: boolean } },
|
|
2126
|
-
cancellation?: AbortSignal,
|
|
2127
|
-
): Promise<GitGraphSessionRefreshResult> {
|
|
2128
|
-
this._window = snapshot.rows;
|
|
2129
|
-
this._buildShape = snapshot.buildShape;
|
|
2130
|
-
this._current = buildRestoredGraph(snapshot);
|
|
2131
|
-
|
|
2132
|
-
// Anchor the re-walk on the window's BOTTOM commit so it spans the whole restored window (the host's
|
|
2133
|
-
// rebuild-anchor discipline), matching to at least the current limit so a since-grown repo still fills a
|
|
2134
|
-
// full page. Stash rows aren't real revs, so skip them when picking the anchor.
|
|
2135
|
-
let anchor: string | undefined;
|
|
2136
|
-
for (let i = snapshot.rows.length - 1; i >= 0; i--) {
|
|
2137
|
-
const type = snapshot.rows[i].type;
|
|
2138
|
-
if (type === 'commit-node' || type === 'merge-node') {
|
|
2139
|
-
anchor = snapshot.rows[i].sha;
|
|
2140
|
-
break;
|
|
2141
|
-
}
|
|
2142
|
-
}
|
|
2143
|
-
const limit = Math.max(snapshot.rows.length, options?.limit ?? 0);
|
|
2144
|
-
|
|
2145
|
-
return this.refresh({ rev: anchor, limit: limit, include: options?.include }, cancellation);
|
|
2146
|
-
}
|
|
2147
|
-
|
|
2148
1983
|
async refresh(
|
|
2149
1984
|
options?: GitGraphSessionRefreshOptions,
|
|
2150
1985
|
cancellation?: AbortSignal,
|
|
@@ -2266,56 +2101,8 @@ class GraphSession implements GitGraphSession {
|
|
|
2266
2101
|
return true;
|
|
2267
2102
|
}
|
|
2268
2103
|
|
|
2269
|
-
serialize(): GitGraphSessionSnapshot | undefined {
|
|
2270
|
-
const current = this._current;
|
|
2271
|
-
// Nothing worth persisting: no window yet, no build shape recorded, or no ref tips (the restore's
|
|
2272
|
-
// tip-diff gate has nothing to diff — never true for the CLI provider, but guards a defensive future).
|
|
2273
|
-
if (this._window.length === 0 || this._buildShape == null || current?.refTips == null) return undefined;
|
|
2274
|
-
|
|
2275
|
-
const [ordering, onlyFollowFirstParent] = this._buildShape.split('|');
|
|
2276
|
-
|
|
2277
|
-
// Cap the persisted window to a bounded top slice; a longer window persists its top with `hasMore`
|
|
2278
|
-
// forced true (restore behaves as a shorter loaded window — the bottom re-pages on demand). Slicing
|
|
2279
|
-
// makes a new array of the SAME row objects — no mutation of the live window.
|
|
2280
|
-
const capped = this._window.length > maxPersistedGraphRows;
|
|
2281
|
-
const rows = capped ? this._window.slice(0, maxPersistedGraphRows) : (this._window as GitGraphRow[]);
|
|
2282
|
-
const hasMore = capped || (current.paging?.hasMore ?? false);
|
|
2283
|
-
|
|
2284
|
-
// Stats are immutable per sha; when capped, trim to the persisted shas (entries for dropped rows are
|
|
2285
|
-
// dead weight). The reachability table is kept whole even when capped — it's proportional to distinct
|
|
2286
|
-
// ref-sets (small), the persisted rows' indices stay valid, and extra sets are harmless on decode.
|
|
2287
|
-
let rowsStats: [string, GitGraphRowStats][] | undefined;
|
|
2288
|
-
if (current.includes?.stats === true && current.rowsStats != null && current.rowsStats.size > 0) {
|
|
2289
|
-
const shas = capped ? new Set(rows.map(r => r.sha)) : undefined;
|
|
2290
|
-
rowsStats = [];
|
|
2291
|
-
for (const [sha, stats] of current.rowsStats) {
|
|
2292
|
-
if (shas != null && !shas.has(sha)) continue;
|
|
2293
|
-
|
|
2294
|
-
rowsStats.push([sha, stats]);
|
|
2295
|
-
}
|
|
2296
|
-
}
|
|
2297
|
-
|
|
2298
|
-
return {
|
|
2299
|
-
v: graphSessionSnapshotVersion,
|
|
2300
|
-
repoPath: this.repoPath,
|
|
2301
|
-
buildShape: this._buildShape,
|
|
2302
|
-
ordering: ordering as 'date' | 'author-date' | 'topo',
|
|
2303
|
-
onlyFollowFirstParent: onlyFollowFirstParent === 'true',
|
|
2304
|
-
rows: rows,
|
|
2305
|
-
refTips: [...current.refTips],
|
|
2306
|
-
decorationFingerprint: current.decorationFingerprint,
|
|
2307
|
-
reachability: current.reachability,
|
|
2308
|
-
rowsStats: rowsStats,
|
|
2309
|
-
downstreams: Array.from(current.downstreams, ([k, v]): [string, string[]] => [k, [...v]]),
|
|
2310
|
-
hasMore: hasMore,
|
|
2311
|
-
includesStats: current.includes?.stats === true,
|
|
2312
|
-
shallow: current.shallow ?? false,
|
|
2313
|
-
};
|
|
2314
|
-
}
|
|
2315
|
-
|
|
2316
2104
|
dispose(): void {
|
|
2317
|
-
// No-op — the window lives entirely in memory
|
|
2318
|
-
// serializes this session (debounced + on its own dispose) and owns the storage IO.
|
|
2105
|
+
// No-op — the window lives entirely in memory and dies with the session.
|
|
2319
2106
|
}
|
|
2320
2107
|
|
|
2321
2108
|
/** A cursor-less rebuild (full walk / fast path) IS the full window. */
|