@gitkraken/core-gitlens 0.15.3 → 0.15.5
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 +45 -1
- package/dist/git/cache.d.ts +28 -18
- package/dist/git/cache.js +71 -25
- package/dist/git/cache.js.map +1 -1
- package/dist/git/context.d.ts +19 -2
- package/dist/git/context.js.map +1 -1
- package/dist/git/models/pullRequest.d.ts +7 -5
- package/dist/git/models/pullRequest.js.map +1 -1
- package/dist/git/models/reference.d.ts +8 -0
- package/dist/git/providers/branches.d.ts +43 -0
- package/dist/git/providers/provider.d.ts +7 -0
- package/dist/git/service.d.ts +5 -1
- package/dist/git/service.js +8 -4
- package/dist/git/service.js.map +1 -1
- package/dist/git/watching/changeEvent.d.ts +29 -3
- package/dist/git/watching/changeEvent.js +39 -3
- package/dist/git/watching/changeEvent.js.map +1 -1
- package/dist/git/watching/configSections.d.ts +35 -0
- package/dist/git/watching/configSections.js +210 -0
- package/dist/git/watching/configSections.js.map +1 -0
- package/dist/git/watching/watchGroup.d.ts +37 -4
- package/dist/git/watching/watchGroup.js +108 -2
- package/dist/git/watching/watchGroup.js.map +1 -1
- package/dist/git/watching/watchService.d.ts +14 -3
- package/dist/git/watching/watchService.js +10 -7
- package/dist/git/watching/watchService.js.map +1 -1
- package/dist/git/watching/watchSession.d.ts +52 -20
- package/dist/git/watching/watchSession.js +166 -43
- package/dist/git/watching/watchSession.js.map +1 -1
- package/dist/git/watching/watcherPatterns.js +1 -0
- package/dist/git/watching/watcherPatterns.js.map +1 -1
- package/dist/git/watching/worktreeSummary.d.ts +21 -3
- package/dist/git/watching/worktreeSummary.js +118 -17
- package/dist/git/watching/worktreeSummary.js.map +1 -1
- package/dist/git-cli/cliGitProvider.d.ts +6 -1
- package/dist/git-cli/cliGitProvider.js +45 -5
- package/dist/git-cli/cliGitProvider.js.map +1 -1
- package/dist/git-cli/parsers/refParser.js +2 -1
- package/dist/git-cli/parsers/refParser.js.map +1 -1
- package/dist/git-cli/providers/branches.d.ts +10 -3
- package/dist/git-cli/providers/branches.js +99 -25
- package/dist/git-cli/providers/branches.js.map +1 -1
- package/dist/git-cli/providers/config.d.ts +45 -9
- package/dist/git-cli/providers/config.js +150 -24
- package/dist/git-cli/providers/config.js.map +1 -1
- package/dist/git-cli/providers/operations.d.ts +35 -1
- package/dist/git-cli/providers/operations.js +175 -16
- package/dist/git-cli/providers/operations.js.map +1 -1
- package/dist/git-cli/providers/stash.d.ts +1 -0
- package/dist/git-cli/providers/stash.js +9 -3
- package/dist/git-cli/providers/stash.js.map +1 -1
- package/dist/git-cli/providers/worktrees.js +11 -4
- package/dist/git-cli/providers/worktrees.js.map +1 -1
- package/dist/git-cli/service.js +5 -1
- package/dist/git-cli/service.js.map +1 -1
- package/dist/plus/integrations/models/gitHostIntegration.d.ts +29 -14
- package/dist/plus/integrations/models/gitHostIntegration.js +41 -19
- package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
- package/dist/plus/integrations/providers/azure/azure.d.ts +16 -16
- package/dist/plus/integrations/providers/azure/azure.js +45 -25
- package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
- package/dist/plus/integrations/providers/azure/azure.utils.d.ts +12 -0
- package/dist/plus/integrations/providers/azure/azure.utils.js +17 -1
- package/dist/plus/integrations/providers/azure/azure.utils.js.map +1 -1
- package/dist/plus/integrations/providers/azure/search.d.ts +4 -0
- package/dist/plus/integrations/providers/azure/search.js +46 -2
- package/dist/plus/integrations/providers/azure/search.js.map +1 -1
- package/dist/plus/integrations/providers/azure/workItemSearch.d.ts +11 -0
- package/dist/plus/integrations/providers/azure/workItemSearch.js +54 -0
- package/dist/plus/integrations/providers/azure/workItemSearch.js.map +1 -0
- package/dist/plus/integrations/providers/azure/workItemStateCategories.d.ts +2 -2
- package/dist/plus/integrations/providers/azure/workItemStateCategories.js +19 -15
- package/dist/plus/integrations/providers/azure/workItemStateCategories.js.map +1 -1
- package/dist/plus/integrations/providers/azureDevOps.d.ts +33 -20
- package/dist/plus/integrations/providers/azureDevOps.js +109 -48
- package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
- package/dist/plus/integrations/providers/azureDevOpsServer.d.ts +16 -47
- package/dist/plus/integrations/providers/azureDevOpsServer.js +52 -91
- package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -1
- package/dist/plus/integrations/providers/issueSorts.d.ts +0 -1
- package/dist/plus/integrations/providers/models.d.ts +5 -0
- package/dist/plus/integrations/providers/models.js +14 -3
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/reads/drains.d.ts +2 -3
- package/dist/plus/integrations/reads/drains.js +4 -4
- package/dist/plus/integrations/reads/drains.js.map +1 -1
- package/dist/plus/integrations/reads/issues.js +7 -4
- package/dist/plus/integrations/reads/issues.js.map +1 -1
- package/dist/plus/integrations/reads/resolveRepository.js +5 -5
- package/dist/plus/integrations/reads/resolveRepository.js.map +1 -1
- package/package.json +1 -1
|
@@ -5,7 +5,9 @@ function isRefChangeKind(change) {
|
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
7
|
* The kind of ref a full ref name (or ref directory) belongs to. A pseudo-ref (no `refs/` prefix, e.g. `HEAD`,
|
|
8
|
-
* `ORIG_HEAD`) counts as `heads`; a ref outside the watched namespaces (e.g. `refs/notes/x`) has no kind
|
|
8
|
+
* `ORIG_HEAD`) counts as `heads`; a ref outside the watched namespaces (e.g. `refs/notes/x`) has no kind, and neither
|
|
9
|
+
* has a namespace directory itself (`refs/remotes`), so a change reported for one names no ref: any ref of the kind it
|
|
10
|
+
* holds may have moved.
|
|
9
11
|
*/
|
|
10
12
|
function getRefChangeKind(ref) {
|
|
11
13
|
if (!ref.startsWith("refs/")) return "heads";
|
|
@@ -27,6 +29,7 @@ var WatcherRepoChangeEvent = class WatcherRepoChangeEvent {
|
|
|
27
29
|
_refs;
|
|
28
30
|
_unnamedRefKinds;
|
|
29
31
|
_announced;
|
|
32
|
+
_configBranches;
|
|
30
33
|
constructor(repoPath, changes, refs, announced) {
|
|
31
34
|
this.repoPath = repoPath;
|
|
32
35
|
this._changes = new Set(changes);
|
|
@@ -37,6 +40,7 @@ var WatcherRepoChangeEvent = class WatcherRepoChangeEvent {
|
|
|
37
40
|
this._refs = new Set(refs.names);
|
|
38
41
|
this._unnamedRefKinds = new Set(refs.unnamed);
|
|
39
42
|
}
|
|
43
|
+
this._configBranches = refs?.configBranches != null && this._changes.has("config") ? new Set(refs.configBranches) : void 0;
|
|
40
44
|
this._announced = announced != null ? new Set(filter(announced, (c) => this._changes.has(c))) : /* @__PURE__ */ new Set();
|
|
41
45
|
}
|
|
42
46
|
get changes() {
|
|
@@ -62,6 +66,32 @@ var WatcherRepoChangeEvent = class WatcherRepoChangeEvent {
|
|
|
62
66
|
get unnamedRefKinds() {
|
|
63
67
|
return this._unnamedRefKinds;
|
|
64
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* The local branches (full `refs/heads/` names) whose `branch.<name>` sections are the only part of `.git/config`
|
|
71
|
+
* that changed; `undefined` when `config` didn't change, or any config change may be elsewhere. Independent of
|
|
72
|
+
* {@link refs}: an outside edit to a branch's tracking moves no ref.
|
|
73
|
+
*/
|
|
74
|
+
get configBranches() {
|
|
75
|
+
return this._configBranches;
|
|
76
|
+
}
|
|
77
|
+
/** Whether `config` changed only in the sections of {@link configBranches} */
|
|
78
|
+
get configConfined() {
|
|
79
|
+
return this._configBranches != null;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Test whether `config` may have changed in a way that concerns a subscriber following `relevantRefs`.
|
|
83
|
+
*
|
|
84
|
+
* A branch's config section matters only to a checkout on that branch or one whose upstream or base is that branch,
|
|
85
|
+
* and those follow `refs/heads/<name>` (or `HEAD`, which may be on any branch). So a {@link configConfined} change
|
|
86
|
+
* concerns `relevantRefs` only when one of them is `HEAD` or one of {@link configBranches}; any other config change
|
|
87
|
+
* concerns everyone.
|
|
88
|
+
*/
|
|
89
|
+
mayHaveChangedConfig(relevantRefs) {
|
|
90
|
+
if (!this._changes.has("config")) return false;
|
|
91
|
+
if (this._configBranches == null) return true;
|
|
92
|
+
for (const ref of relevantRefs) if (ref === "HEAD" || this._configBranches.has(ref)) return true;
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
65
95
|
toString(changesOnly = false) {
|
|
66
96
|
const changeList = [...this._changes].join(", ");
|
|
67
97
|
const refCount = this._refs.size;
|
|
@@ -115,7 +145,8 @@ var WatcherRepoChangeEvent = class WatcherRepoChangeEvent {
|
|
|
115
145
|
}
|
|
116
146
|
/**
|
|
117
147
|
* Coalesce: return a new event with all changes and refs from both this and the given set. When `refs` is omitted,
|
|
118
|
-
* every ref kind in `changes` counts as unnamed. A kind in `changes` is no longer {@link announced}.
|
|
148
|
+
* every ref kind in `changes` counts as unnamed. A kind in `changes` is no longer {@link announced}. `config` stays
|
|
149
|
+
* {@link configConfined}, to the branches of both, only when each side that changed it was confined.
|
|
119
150
|
*/
|
|
120
151
|
with(changes, refs) {
|
|
121
152
|
const names = [...this._refs];
|
|
@@ -125,9 +156,14 @@ var WatcherRepoChangeEvent = class WatcherRepoChangeEvent {
|
|
|
125
156
|
if (refs.names != null) names.push(...refs.names);
|
|
126
157
|
if (refs.unnamed != null) unnamed.push(...refs.unnamed);
|
|
127
158
|
}
|
|
159
|
+
let configBranches;
|
|
160
|
+
const own = this._changes.has("config") ? this._configBranches : /* @__PURE__ */ new Set();
|
|
161
|
+
const other = changes.includes("config") ? refs?.configBranches : [];
|
|
162
|
+
if (own != null && other != null) configBranches = /* @__PURE__ */ new Set([...own, ...other]);
|
|
128
163
|
return new WatcherRepoChangeEvent(this.repoPath, [...this._changes, ...changes], {
|
|
129
164
|
names,
|
|
130
|
-
unnamed
|
|
165
|
+
unnamed,
|
|
166
|
+
configBranches
|
|
131
167
|
}, filter(this._announced, (c) => !changes.includes(c)));
|
|
132
168
|
}
|
|
133
169
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"changeEvent.js","names":[],"sources":["../../../../git/src/watching/changeEvent.ts"],"sourcesContent":["import { filter, some } from '@gitlens/utils/iterable.js';\nimport type { RepositoryChange } from '../models/repository.js';\n\n/** The {@link RepositoryChange} kinds that describe ref movement, which an event can attribute to named refs */\nexport type RefChangeKind = 'heads' | 'remotes' | 'tags' | 'stash';\n\nexport interface WatcherRefChanges {\n\t/** Full ref names whose files changed (`refs/heads/main`), plus per-worktree pseudo-refs (`HEAD`, `ORIG_HEAD`). A name may be a ref directory and then covers every ref beneath it. */\n\treadonly names?: Iterable<string>;\n\t/** Ref kinds that changed without a name (packed-refs rewrite, config, shallow/replace, an injected change): any ref of that kind may have moved. */\n\treadonly unnamed?: Iterable<RefChangeKind>;\n}\n\nexport function isRefChangeKind(change: RepositoryChange): change is RefChangeKind {\n\treturn change === 'heads' || change === 'remotes' || change === 'tags' || change === 'stash';\n}\n\n/**\n * The kind of ref a full ref name (or ref directory) belongs to. A pseudo-ref (no `refs/` prefix, e.g. `HEAD`,\n * `ORIG_HEAD`) counts as `heads`; a ref outside the watched namespaces (e.g. `refs/notes/x`) has no kind.\n */\nexport function getRefChangeKind(ref: string): RefChangeKind | undefined {\n\tif (!ref.startsWith('refs/')) return 'heads';\n\tif (ref.startsWith('refs/heads/')) return 'heads';\n\tif (ref.startsWith('refs/remotes/')) return 'remotes';\n\tif (ref.startsWith('refs/tags/')) return 'tags';\n\tif (ref === 'refs/stash') return 'stash';\n\n\treturn undefined;\n}\n\n/**\n * An event describing changes to a repository, produced by the watching pipeline.\n *\n * Changes are coalesced within a debounce window — a single event may represent\n * multiple filesystem changes. Use {@link changed} to test for specific change types,\n * and {@link mayHaveChangedRef} to test whether a specific ref may have moved.\n */\nexport class WatcherRepoChangeEvent {\n\tprotected readonly _changes: Set<RepositoryChange>;\n\tprivate readonly _refs: Set<string>;\n\tprivate readonly _unnamedRefKinds: Set<RefChangeKind>;\n\tprivate readonly _announced: Set<RepositoryChange>;\n\n\tconstructor(\n\t\t/** The repository path this event is for */\n\t\tpublic readonly repoPath: string,\n\t\tchanges: RepositoryChange[],\n\t\t/** Which refs changed; when omitted, every ref kind in `changes` is unnamed (any ref of that kind may have moved) */\n\t\trefs?: WatcherRefChanges,\n\t\t/** The kinds of `changes` that come only from core's own announced writes (see {@link announced}) */\n\t\tannounced?: Iterable<RepositoryChange>,\n\t) {\n\t\tthis._changes = new Set(changes);\n\t\tif (refs == null) {\n\t\t\tthis._refs = new Set();\n\t\t\tthis._unnamedRefKinds = new Set(filter(changes, isRefChangeKind));\n\t\t} else {\n\t\t\tthis._refs = new Set(refs.names);\n\t\t\tthis._unnamedRefKinds = new Set(refs.unnamed);\n\t\t}\n\t\tthis._announced = announced != null ? new Set(filter(announced, c => this._changes.has(c))) : new Set();\n\t}\n\n\tget changes(): ReadonlySet<RepositoryChange> {\n\t\treturn this._changes;\n\t}\n\n\t/**\n\t * The kinds in {@link changes} that come only from core's own writes, which core already announced through\n\t * `hooks.repository.onChanged` after evicting their caches. A host that handled that announcement can skip them;\n\t * a kind the watcher or an injected change also reported is not among them.\n\t */\n\tget announced(): ReadonlySet<RepositoryChange> {\n\t\treturn this._announced;\n\t}\n\n\t/** The kinds in {@link changes} that aren't {@link announced}: what a cache that core already evicted for its write still has to clear */\n\tgetUnannouncedChanges(): RepositoryChange[] {\n\t\treturn this._announced.size ? [...filter(this._changes, c => !this._announced.has(c))] : [...this._changes];\n\t}\n\n\t/** Full ref names (or ref directories, or pseudo-refs) known to have changed. See {@link WatcherRefChanges.names}. */\n\tget refs(): ReadonlySet<string> {\n\t\treturn this._refs;\n\t}\n\n\t/** Ref kinds that changed without a name — any ref of these kinds may have moved */\n\tget unnamedRefKinds(): ReadonlySet<RefChangeKind> {\n\t\treturn this._unnamedRefKinds;\n\t}\n\n\ttoString(changesOnly: boolean = false): string {\n\t\tconst changeList = [...this._changes].join(', ');\n\t\tconst refCount = this._refs.size;\n\t\tif (changesOnly) return refCount ? `changes=${changeList}, refs=${refCount}` : `changes=${changeList}`;\n\n\t\treturn refCount\n\t\t\t? `{ repoPath: ${this.repoPath}, changes: ${changeList}, refs: ${refCount} }`\n\t\t\t: `{ repoPath: ${this.repoPath}, changes: ${changeList} }`;\n\t}\n\n\t/**\n\t * Test whether the given ref may have moved in this event.\n\t *\n\t * @param ref - A full ref name (`refs/heads/main`) or a pseudo-ref (`HEAD`).\n\t * @returns `false` when the ref's kind didn't change (or the ref is outside the watched kinds); `true` when its kind\n\t * changed without a name, or when the ref itself or a ref directory above it changed. `HEAD` is usually a symref\n\t * to the checked-out branch, so it may have moved whenever any `heads` ref did.\n\t */\n\tmayHaveChangedRef(ref: string): boolean {\n\t\tconst kind = getRefChangeKind(ref);\n\t\tif (kind == null || !this._changes.has(kind)) return false;\n\t\tif (this._unnamedRefKinds.has(kind) || ref === 'HEAD') return true;\n\n\t\treturn findCoveringRefName(this._refs, ref) != null;\n\t}\n\n\t/**\n\t * Test whether any of the specified change types are present in the event.\n\t *\n\t * @param affected - Change types to test for.\n\t * @returns `true` if any of the specified changes are present.\n\t */\n\tchanged(...affected: RepositoryChange[]): boolean {\n\t\treturn some(this._changes, c => affected.includes(c));\n\t}\n\n\t/**\n\t * Test whether the event contains ONLY the specified change types.\n\t *\n\t * `pausedOp` is treated as a union of `cherryPick`, `merge`, `rebase`,\n\t * and `revert` for convenience.\n\t *\n\t * @param affected - Change types to test for.\n\t * @returns `true` if the event contains only the specified changes.\n\t */\n\tchangedExclusive(...affected: RepositoryChange[]): boolean {\n\t\tlet changes = this._changes;\n\n\t\t// When checking for specific paused operation types, also accept the union type\n\t\tif (\n\t\t\taffected.includes('cherryPick') ||\n\t\t\taffected.includes('merge') ||\n\t\t\taffected.includes('rebase') ||\n\t\t\taffected.includes('revert')\n\t\t) {\n\t\t\tif (!affected.includes('pausedOp')) {\n\t\t\t\taffected = [...affected, 'pausedOp'];\n\t\t\t}\n\t\t} else if (affected.includes('pausedOp')) {\n\t\t\t// When checking for the union type exclusively, ignore specific subtypes\n\t\t\tchanges = new Set(changes);\n\t\t\tchanges.delete('cherryPick');\n\t\t\tchanges.delete('merge');\n\t\t\tchanges.delete('rebase');\n\t\t\tchanges.delete('revert');\n\t\t}\n\n\t\tconst intersection = [...filter(changes, c => affected.includes(c))];\n\t\treturn intersection.length === changes.size;\n\t}\n\n\t/**\n\t * Coalesce: return a new event with all changes and refs from both this and the given set. When `refs` is omitted,\n\t * every ref kind in `changes` counts as unnamed. A kind in `changes` is no longer {@link announced}.\n\t */\n\twith(changes: RepositoryChange[], refs?: WatcherRefChanges): WatcherRepoChangeEvent {\n\t\tconst names = [...this._refs];\n\t\tconst unnamed = [...this._unnamedRefKinds];\n\t\tif (refs == null) {\n\t\t\tunnamed.push(...filter(changes, isRefChangeKind));\n\t\t} else {\n\t\t\tif (refs.names != null) {\n\t\t\t\tnames.push(...refs.names);\n\t\t\t}\n\t\t\tif (refs.unnamed != null) {\n\t\t\t\tunnamed.push(...refs.unnamed);\n\t\t\t}\n\t\t}\n\n\t\treturn new WatcherRepoChangeEvent(\n\t\t\tthis.repoPath,\n\t\t\t[...this._changes, ...changes],\n\t\t\t{ names: names, unnamed: unnamed },\n\t\t\tfilter(this._announced, c => !changes.includes(c)),\n\t\t);\n\t}\n}\n\n/**\n * Returns the name in `names` that covers `ref` — `ref` itself, or a ref directory above it (compared on `/`\n * boundaries, so `refs/heads/fe` doesn't cover `refs/heads/feature`) — or `undefined` if none does.\n */\nexport function findCoveringRefName(names: ReadonlySet<string>, ref: string): string | undefined {\n\tif (names.size === 0) return undefined;\n\tif (names.has(ref)) return ref;\n\n\tlet index = ref.lastIndexOf('/');\n\twhile (index > 0) {\n\t\tconst dir = ref.substring(0, index);\n\t\tif (names.has(dir)) return dir;\n\n\t\tindex = ref.lastIndexOf('/', index - 1);\n\t}\n\treturn undefined;\n}\n\n/**\n * An event describing working tree file changes, produced by the working tree pipeline.\n *\n * Paths have already been filtered through the gitignore filter and noise filter.\n */\nexport interface WorkingTreeChangeEvent {\n\treadonly repoPath: string;\n\t/**\n\t * Absolute paths of changed files or directories (post-gitignore filtering). A directory stands for everything\n\t * beneath it. Partial when {@link incomplete}.\n\t */\n\treadonly paths: ReadonlySet<string>;\n\t/**\n\t * `true` when more paths changed than the accumulator retains, so {@link paths} is a partial set and a\n\t * miss does NOT mean the file is unchanged. Consumers testing membership must treat this as a match.\n\t */\n\treadonly incomplete: boolean;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"changeEvent.js","names":[],"sources":["../../../../git/src/watching/changeEvent.ts"],"sourcesContent":["import { filter, some } from '@gitlens/utils/iterable.js';\nimport type { RepositoryChange } from '../models/repository.js';\n\n/** The {@link RepositoryChange} kinds that describe ref movement, which an event can attribute to named refs */\nexport type RefChangeKind = 'heads' | 'remotes' | 'tags' | 'stash';\n\nexport interface WatcherRefChanges {\n\t/** Full ref names whose files changed (`refs/heads/main`), plus per-worktree pseudo-refs (`HEAD`, `ORIG_HEAD`). A name may be a ref directory and then covers every ref beneath it. */\n\treadonly names?: Iterable<string>;\n\t/** Ref kinds that changed without a name (packed-refs rewrite, a ref namespace directory like `refs/remotes`, shallow/replace, an injected change): any ref of that kind may have moved. */\n\treadonly unnamed?: Iterable<RefChangeKind>;\n\t/**\n\t * The local branches (full `refs/heads/` names) whose `branch.<name>` sections are all that every `config` change\n\t * among the changes touched in `.git/config`. Omitted when any of them may have changed anything else.\n\t */\n\treadonly configBranches?: Iterable<string>;\n}\n\nexport function isRefChangeKind(change: RepositoryChange): change is RefChangeKind {\n\treturn change === 'heads' || change === 'remotes' || change === 'tags' || change === 'stash';\n}\n\n/**\n * The kind of ref a full ref name (or ref directory) belongs to. A pseudo-ref (no `refs/` prefix, e.g. `HEAD`,\n * `ORIG_HEAD`) counts as `heads`; a ref outside the watched namespaces (e.g. `refs/notes/x`) has no kind, and neither\n * has a namespace directory itself (`refs/remotes`), so a change reported for one names no ref: any ref of the kind it\n * holds may have moved.\n */\nexport function getRefChangeKind(ref: string): RefChangeKind | undefined {\n\tif (!ref.startsWith('refs/')) return 'heads';\n\tif (ref.startsWith('refs/heads/')) return 'heads';\n\tif (ref.startsWith('refs/remotes/')) return 'remotes';\n\tif (ref.startsWith('refs/tags/')) return 'tags';\n\tif (ref === 'refs/stash') return 'stash';\n\n\treturn undefined;\n}\n\n/**\n * An event describing changes to a repository, produced by the watching pipeline.\n *\n * Changes are coalesced within a debounce window — a single event may represent\n * multiple filesystem changes. Use {@link changed} to test for specific change types,\n * and {@link mayHaveChangedRef} to test whether a specific ref may have moved.\n */\nexport class WatcherRepoChangeEvent {\n\tprotected readonly _changes: Set<RepositoryChange>;\n\tprivate readonly _refs: Set<string>;\n\tprivate readonly _unnamedRefKinds: Set<RefChangeKind>;\n\tprivate readonly _announced: Set<RepositoryChange>;\n\tprivate readonly _configBranches: Set<string> | undefined;\n\n\tconstructor(\n\t\t/** The repository path this event is for */\n\t\tpublic readonly repoPath: string,\n\t\tchanges: RepositoryChange[],\n\t\t/** Which refs changed; when omitted, every ref kind in `changes` is unnamed (any ref of that kind may have moved) */\n\t\trefs?: WatcherRefChanges,\n\t\t/** The kinds of `changes` that come only from core's own announced writes (see {@link announced}) */\n\t\tannounced?: Iterable<RepositoryChange>,\n\t) {\n\t\tthis._changes = new Set(changes);\n\t\tif (refs == null) {\n\t\t\tthis._refs = new Set();\n\t\t\tthis._unnamedRefKinds = new Set(filter(changes, isRefChangeKind));\n\t\t} else {\n\t\t\tthis._refs = new Set(refs.names);\n\t\t\tthis._unnamedRefKinds = new Set(refs.unnamed);\n\t\t}\n\t\tthis._configBranches =\n\t\t\trefs?.configBranches != null && this._changes.has('config') ? new Set(refs.configBranches) : undefined;\n\t\tthis._announced = announced != null ? new Set(filter(announced, c => this._changes.has(c))) : new Set();\n\t}\n\n\tget changes(): ReadonlySet<RepositoryChange> {\n\t\treturn this._changes;\n\t}\n\n\t/**\n\t * The kinds in {@link changes} that come only from core's own writes, which core already announced through\n\t * `hooks.repository.onChanged` after evicting their caches. A host that handled that announcement can skip them;\n\t * a kind the watcher or an injected change also reported is not among them.\n\t */\n\tget announced(): ReadonlySet<RepositoryChange> {\n\t\treturn this._announced;\n\t}\n\n\t/** The kinds in {@link changes} that aren't {@link announced}: what a cache that core already evicted for its write still has to clear */\n\tgetUnannouncedChanges(): RepositoryChange[] {\n\t\treturn this._announced.size ? [...filter(this._changes, c => !this._announced.has(c))] : [...this._changes];\n\t}\n\n\t/** Full ref names (or ref directories, or pseudo-refs) known to have changed. See {@link WatcherRefChanges.names}. */\n\tget refs(): ReadonlySet<string> {\n\t\treturn this._refs;\n\t}\n\n\t/** Ref kinds that changed without a name — any ref of these kinds may have moved */\n\tget unnamedRefKinds(): ReadonlySet<RefChangeKind> {\n\t\treturn this._unnamedRefKinds;\n\t}\n\n\t/**\n\t * The local branches (full `refs/heads/` names) whose `branch.<name>` sections are the only part of `.git/config`\n\t * that changed; `undefined` when `config` didn't change, or any config change may be elsewhere. Independent of\n\t * {@link refs}: an outside edit to a branch's tracking moves no ref.\n\t */\n\tget configBranches(): ReadonlySet<string> | undefined {\n\t\treturn this._configBranches;\n\t}\n\n\t/** Whether `config` changed only in the sections of {@link configBranches} */\n\tget configConfined(): boolean {\n\t\treturn this._configBranches != null;\n\t}\n\n\t/**\n\t * Test whether `config` may have changed in a way that concerns a subscriber following `relevantRefs`.\n\t *\n\t * A branch's config section matters only to a checkout on that branch or one whose upstream or base is that branch,\n\t * and those follow `refs/heads/<name>` (or `HEAD`, which may be on any branch). So a {@link configConfined} change\n\t * concerns `relevantRefs` only when one of them is `HEAD` or one of {@link configBranches}; any other config change\n\t * concerns everyone.\n\t */\n\tmayHaveChangedConfig(relevantRefs: Iterable<string>): boolean {\n\t\tif (!this._changes.has('config')) return false;\n\t\tif (this._configBranches == null) return true;\n\n\t\tfor (const ref of relevantRefs) {\n\t\t\tif (ref === 'HEAD' || this._configBranches.has(ref)) return true;\n\t\t}\n\t\treturn false;\n\t}\n\n\ttoString(changesOnly: boolean = false): string {\n\t\tconst changeList = [...this._changes].join(', ');\n\t\tconst refCount = this._refs.size;\n\t\tif (changesOnly) return refCount ? `changes=${changeList}, refs=${refCount}` : `changes=${changeList}`;\n\n\t\treturn refCount\n\t\t\t? `{ repoPath: ${this.repoPath}, changes: ${changeList}, refs: ${refCount} }`\n\t\t\t: `{ repoPath: ${this.repoPath}, changes: ${changeList} }`;\n\t}\n\n\t/**\n\t * Test whether the given ref may have moved in this event.\n\t *\n\t * @param ref - A full ref name (`refs/heads/main`) or a pseudo-ref (`HEAD`).\n\t * @returns `false` when the ref's kind didn't change (or the ref is outside the watched kinds); `true` when its kind\n\t * changed without a name, or when the ref itself or a ref directory above it changed. `HEAD` is usually a symref\n\t * to the checked-out branch, so it may have moved whenever any `heads` ref did.\n\t */\n\tmayHaveChangedRef(ref: string): boolean {\n\t\tconst kind = getRefChangeKind(ref);\n\t\tif (kind == null || !this._changes.has(kind)) return false;\n\t\tif (this._unnamedRefKinds.has(kind) || ref === 'HEAD') return true;\n\n\t\treturn findCoveringRefName(this._refs, ref) != null;\n\t}\n\n\t/**\n\t * Test whether any of the specified change types are present in the event.\n\t *\n\t * @param affected - Change types to test for.\n\t * @returns `true` if any of the specified changes are present.\n\t */\n\tchanged(...affected: RepositoryChange[]): boolean {\n\t\treturn some(this._changes, c => affected.includes(c));\n\t}\n\n\t/**\n\t * Test whether the event contains ONLY the specified change types.\n\t *\n\t * `pausedOp` is treated as a union of `cherryPick`, `merge`, `rebase`,\n\t * and `revert` for convenience.\n\t *\n\t * @param affected - Change types to test for.\n\t * @returns `true` if the event contains only the specified changes.\n\t */\n\tchangedExclusive(...affected: RepositoryChange[]): boolean {\n\t\tlet changes = this._changes;\n\n\t\t// When checking for specific paused operation types, also accept the union type\n\t\tif (\n\t\t\taffected.includes('cherryPick') ||\n\t\t\taffected.includes('merge') ||\n\t\t\taffected.includes('rebase') ||\n\t\t\taffected.includes('revert')\n\t\t) {\n\t\t\tif (!affected.includes('pausedOp')) {\n\t\t\t\taffected = [...affected, 'pausedOp'];\n\t\t\t}\n\t\t} else if (affected.includes('pausedOp')) {\n\t\t\t// When checking for the union type exclusively, ignore specific subtypes\n\t\t\tchanges = new Set(changes);\n\t\t\tchanges.delete('cherryPick');\n\t\t\tchanges.delete('merge');\n\t\t\tchanges.delete('rebase');\n\t\t\tchanges.delete('revert');\n\t\t}\n\n\t\tconst intersection = [...filter(changes, c => affected.includes(c))];\n\t\treturn intersection.length === changes.size;\n\t}\n\n\t/**\n\t * Coalesce: return a new event with all changes and refs from both this and the given set. When `refs` is omitted,\n\t * every ref kind in `changes` counts as unnamed. A kind in `changes` is no longer {@link announced}. `config` stays\n\t * {@link configConfined}, to the branches of both, only when each side that changed it was confined.\n\t */\n\twith(changes: RepositoryChange[], refs?: WatcherRefChanges): WatcherRepoChangeEvent {\n\t\tconst names = [...this._refs];\n\t\tconst unnamed = [...this._unnamedRefKinds];\n\t\tif (refs == null) {\n\t\t\tunnamed.push(...filter(changes, isRefChangeKind));\n\t\t} else {\n\t\t\tif (refs.names != null) {\n\t\t\t\tnames.push(...refs.names);\n\t\t\t}\n\t\t\tif (refs.unnamed != null) {\n\t\t\t\tunnamed.push(...refs.unnamed);\n\t\t\t}\n\t\t}\n\n\t\tlet configBranches: Set<string> | undefined;\n\t\tconst own = this._changes.has('config') ? this._configBranches : new Set<string>();\n\t\tconst other = changes.includes('config') ? refs?.configBranches : [];\n\t\tif (own != null && other != null) {\n\t\t\tconfigBranches = new Set([...own, ...other]);\n\t\t}\n\n\t\treturn new WatcherRepoChangeEvent(\n\t\t\tthis.repoPath,\n\t\t\t[...this._changes, ...changes],\n\t\t\t{ names: names, unnamed: unnamed, configBranches: configBranches },\n\t\t\tfilter(this._announced, c => !changes.includes(c)),\n\t\t);\n\t}\n}\n\n/**\n * Returns the name in `names` that covers `ref` — `ref` itself, or a ref directory above it (compared on `/`\n * boundaries, so `refs/heads/fe` doesn't cover `refs/heads/feature`) — or `undefined` if none does.\n */\nexport function findCoveringRefName(names: ReadonlySet<string>, ref: string): string | undefined {\n\tif (names.size === 0) return undefined;\n\tif (names.has(ref)) return ref;\n\n\tlet index = ref.lastIndexOf('/');\n\twhile (index > 0) {\n\t\tconst dir = ref.substring(0, index);\n\t\tif (names.has(dir)) return dir;\n\n\t\tindex = ref.lastIndexOf('/', index - 1);\n\t}\n\treturn undefined;\n}\n\n/**\n * An event describing working tree file changes, produced by the working tree pipeline.\n *\n * Paths have already been filtered through the gitignore filter and noise filter.\n */\nexport interface WorkingTreeChangeEvent {\n\treadonly repoPath: string;\n\t/**\n\t * Absolute paths of changed files or directories (post-gitignore filtering). A directory stands for everything\n\t * beneath it. Partial when {@link incomplete}.\n\t */\n\treadonly paths: ReadonlySet<string>;\n\t/**\n\t * `true` when more paths changed than the accumulator retains, so {@link paths} is a partial set and a\n\t * miss does NOT mean the file is unchanged. Consumers testing membership must treat this as a match.\n\t */\n\treadonly incomplete: boolean;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//#region ../git/src/watching/configSections.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* A git config file's variables by section, as git reads them: each key is a section's full name (`core`,
|
|
4
|
+
* `branch.feature/x`: the section name lowercased, then its subsection), and each value lists that section's variables
|
|
5
|
+
* in file order as `<key>=<value>` (the key lowercased, the value unquoted and unescaped), or a bare `<key>` for one
|
|
6
|
+
* with no value. A section whose header repeats lists the variables of every occurrence.
|
|
7
|
+
*/
|
|
8
|
+
export type GitConfigSections = ReadonlyMap<string, readonly string[]>;
|
|
9
|
+
/**
|
|
10
|
+
* What changed between two reads of the same config file: every local branch (full `refs/heads/` name) whose
|
|
11
|
+
* `branch.<name>` section changed, and whether that is all that changed. `undefined` when nothing changed.
|
|
12
|
+
*/
|
|
13
|
+
export interface GitConfigSectionsChange {
|
|
14
|
+
readonly branches: ReadonlySet<string>;
|
|
15
|
+
readonly confined: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** Parses a config file's bytes; `undefined` when they aren't UTF-8 or git would reject them */
|
|
18
|
+
export declare function parseGitConfigBytes(data: Uint8Array): GitConfigSections | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Parses a config file's text the way git does (`config.c`): a header is `[section]`, `[section "subsection"]` (the
|
|
21
|
+
* subsection kept as written, `\` escaping the next character) or the legacy `[section.subsection]` (lowercased); a
|
|
22
|
+
* variable may follow a header on its line; `#` and `;` start a comment outside quotes; a value may be quoted, and `\`
|
|
23
|
+
* escapes `"`, `\`, `n`, `t` and `b`, or continues the value on the next line. `undefined` for anything git rejects
|
|
24
|
+
* (a malformed header or name, an unknown escape, a line ending inside quotes), and for a variable before any header,
|
|
25
|
+
* which has no section to compare.
|
|
26
|
+
*/
|
|
27
|
+
export declare function parseGitConfigSections(text: string): GitConfigSections | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Compares two reads of a config file section by section. A changed `branch.<name>` section (a subsection of
|
|
30
|
+
* `branch`) names that local branch; any other changed section, the bare `[branch]` included, makes the change
|
|
31
|
+
* unconfined.
|
|
32
|
+
*/
|
|
33
|
+
export declare function diffGitConfigSections(before: GitConfigSections, after: GitConfigSections): GitConfigSectionsChange | undefined;
|
|
34
|
+
//#endregion
|
|
35
|
+
//# sourceMappingURL=configSections.d.ts.map
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
//#region ../git/src/watching/configSections.ts
|
|
2
|
+
const utf8 = new TextDecoder("utf-8", { fatal: true });
|
|
3
|
+
/** Parses a config file's bytes; `undefined` when they aren't UTF-8 or git would reject them */
|
|
4
|
+
function parseGitConfigBytes(data) {
|
|
5
|
+
let text;
|
|
6
|
+
try {
|
|
7
|
+
text = utf8.decode(data);
|
|
8
|
+
} catch {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
return parseGitConfigSections(text);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Parses a config file's text the way git does (`config.c`): a header is `[section]`, `[section "subsection"]` (the
|
|
15
|
+
* subsection kept as written, `\` escaping the next character) or the legacy `[section.subsection]` (lowercased); a
|
|
16
|
+
* variable may follow a header on its line; `#` and `;` start a comment outside quotes; a value may be quoted, and `\`
|
|
17
|
+
* escapes `"`, `\`, `n`, `t` and `b`, or continues the value on the next line. `undefined` for anything git rejects
|
|
18
|
+
* (a malformed header or name, an unknown escape, a line ending inside quotes), and for a variable before any header,
|
|
19
|
+
* which has no section to compare.
|
|
20
|
+
*/
|
|
21
|
+
function parseGitConfigSections(text) {
|
|
22
|
+
const sections = /* @__PURE__ */ new Map();
|
|
23
|
+
let index = 0;
|
|
24
|
+
let eof = false;
|
|
25
|
+
/** Git's `get_next_char`: CRLF reads as LF, and the end of the text reads as a final LF */
|
|
26
|
+
const next = () => {
|
|
27
|
+
if (index >= text.length) {
|
|
28
|
+
eof = true;
|
|
29
|
+
return "\n";
|
|
30
|
+
}
|
|
31
|
+
const c = text[index++];
|
|
32
|
+
if (c === "\r" && text[index] === "\n") {
|
|
33
|
+
index++;
|
|
34
|
+
return "\n";
|
|
35
|
+
}
|
|
36
|
+
return c;
|
|
37
|
+
};
|
|
38
|
+
/** The rest of a `[section "subsection"]` header, from the whitespace after the section name */
|
|
39
|
+
const readSubsection = (first) => {
|
|
40
|
+
let c = first;
|
|
41
|
+
do {
|
|
42
|
+
if (c === "\n") return void 0;
|
|
43
|
+
c = next();
|
|
44
|
+
} while (isSpace(c));
|
|
45
|
+
if (c !== "\"") return void 0;
|
|
46
|
+
let subsection = "";
|
|
47
|
+
for (;;) {
|
|
48
|
+
c = next();
|
|
49
|
+
if (c === "\n") return void 0;
|
|
50
|
+
if (c === "\"") break;
|
|
51
|
+
if (c === "\\") {
|
|
52
|
+
c = next();
|
|
53
|
+
if (c === "\n") return void 0;
|
|
54
|
+
}
|
|
55
|
+
subsection += c;
|
|
56
|
+
}
|
|
57
|
+
return next() === "]" ? subsection : void 0;
|
|
58
|
+
};
|
|
59
|
+
/** A header's full section name, from just after its `[` */
|
|
60
|
+
const readHeader = () => {
|
|
61
|
+
let name = "";
|
|
62
|
+
for (;;) {
|
|
63
|
+
const c = next();
|
|
64
|
+
if (eof) return void 0;
|
|
65
|
+
if (c === "]") return name;
|
|
66
|
+
if (isSpace(c)) {
|
|
67
|
+
const subsection = readSubsection(c);
|
|
68
|
+
return subsection != null ? `${name}.${subsection}` : void 0;
|
|
69
|
+
}
|
|
70
|
+
if (!isKeyChar(c) && c !== ".") return void 0;
|
|
71
|
+
name += c.toLowerCase();
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
/** Git's `parse_value`: a value, from just after its `=` to the end of its (last continued) line */
|
|
75
|
+
const readValue = () => {
|
|
76
|
+
let value = "";
|
|
77
|
+
let quoted = false;
|
|
78
|
+
let comment = false;
|
|
79
|
+
let trimTo;
|
|
80
|
+
for (;;) {
|
|
81
|
+
let c = next();
|
|
82
|
+
if (c === "\n") {
|
|
83
|
+
if (quoted) return void 0;
|
|
84
|
+
return trimTo != null ? value.substring(0, trimTo) : value;
|
|
85
|
+
}
|
|
86
|
+
if (comment) continue;
|
|
87
|
+
if (isSpace(c) && !quoted) {
|
|
88
|
+
trimTo ??= value.length;
|
|
89
|
+
if (value.length) value += c;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (!quoted && (c === ";" || c === "#")) {
|
|
93
|
+
comment = true;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
trimTo = void 0;
|
|
97
|
+
if (c === "\\") {
|
|
98
|
+
c = next();
|
|
99
|
+
switch (c) {
|
|
100
|
+
case "\n": continue;
|
|
101
|
+
case "t":
|
|
102
|
+
c = " ";
|
|
103
|
+
break;
|
|
104
|
+
case "b":
|
|
105
|
+
c = "\b";
|
|
106
|
+
break;
|
|
107
|
+
case "n":
|
|
108
|
+
c = "\n";
|
|
109
|
+
break;
|
|
110
|
+
case "\\":
|
|
111
|
+
case "\"": break;
|
|
112
|
+
default: return;
|
|
113
|
+
}
|
|
114
|
+
value += c;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (c === "\"") {
|
|
118
|
+
quoted = !quoted;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
value += c;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
let section;
|
|
125
|
+
let comment = false;
|
|
126
|
+
for (;;) {
|
|
127
|
+
const c = next();
|
|
128
|
+
if (c === "\n") {
|
|
129
|
+
if (eof) return sections;
|
|
130
|
+
comment = false;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (comment || isSpace(c)) continue;
|
|
134
|
+
if (c === "#" || c === ";") {
|
|
135
|
+
comment = true;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (c === "[") {
|
|
139
|
+
const name = readHeader();
|
|
140
|
+
if (!name) return void 0;
|
|
141
|
+
section = sections.get(name);
|
|
142
|
+
if (section == null) {
|
|
143
|
+
section = [];
|
|
144
|
+
sections.set(name, section);
|
|
145
|
+
}
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (section == null || !isAlpha(c)) return void 0;
|
|
149
|
+
let key = c.toLowerCase();
|
|
150
|
+
let k;
|
|
151
|
+
for (;;) {
|
|
152
|
+
k = next();
|
|
153
|
+
if (eof || !isKeyChar(k)) break;
|
|
154
|
+
key += k.toLowerCase();
|
|
155
|
+
}
|
|
156
|
+
while (k === " " || k === " ") k = next();
|
|
157
|
+
if (k === "\n") {
|
|
158
|
+
section.push(key);
|
|
159
|
+
if (eof) return sections;
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (k !== "=") return void 0;
|
|
163
|
+
const value = readValue();
|
|
164
|
+
if (value == null) return void 0;
|
|
165
|
+
section.push(`${key}=${value}`);
|
|
166
|
+
if (eof) return sections;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Compares two reads of a config file section by section. A changed `branch.<name>` section (a subsection of
|
|
171
|
+
* `branch`) names that local branch; any other changed section, the bare `[branch]` included, makes the change
|
|
172
|
+
* unconfined.
|
|
173
|
+
*/
|
|
174
|
+
function diffGitConfigSections(before, after) {
|
|
175
|
+
const branches = /* @__PURE__ */ new Set();
|
|
176
|
+
let changed = false;
|
|
177
|
+
let confined = true;
|
|
178
|
+
const compare = (name) => {
|
|
179
|
+
if (sameVariables(before.get(name), after.get(name))) return;
|
|
180
|
+
changed = true;
|
|
181
|
+
if (name.startsWith("branch.") && name.length > 7) branches.add(`refs/heads/${name.substring(7)}`);
|
|
182
|
+
else confined = false;
|
|
183
|
+
};
|
|
184
|
+
for (const name of before.keys()) compare(name);
|
|
185
|
+
for (const name of after.keys()) if (!before.has(name)) compare(name);
|
|
186
|
+
return changed ? {
|
|
187
|
+
branches,
|
|
188
|
+
confined
|
|
189
|
+
} : void 0;
|
|
190
|
+
}
|
|
191
|
+
function sameVariables(a, b) {
|
|
192
|
+
if (a == null || b == null) return a === b;
|
|
193
|
+
if (a.length !== b.length) return false;
|
|
194
|
+
return a.every((v, i) => v === b[i]);
|
|
195
|
+
}
|
|
196
|
+
/** Git's own `isspace`, which leaves out `\v` and `\f`: a value keeps them, so they must not compare as trimmed */
|
|
197
|
+
function isSpace(c) {
|
|
198
|
+
return c === " " || c === " " || c === "\n" || c === "\r";
|
|
199
|
+
}
|
|
200
|
+
function isAlpha(c) {
|
|
201
|
+
return c >= "a" && c <= "z" || c >= "A" && c <= "Z";
|
|
202
|
+
}
|
|
203
|
+
/** Git's `iskeychar`: an ASCII letter or digit, or `-` */
|
|
204
|
+
function isKeyChar(c) {
|
|
205
|
+
return isAlpha(c) || c >= "0" && c <= "9" || c === "-";
|
|
206
|
+
}
|
|
207
|
+
//#endregion
|
|
208
|
+
export { diffGitConfigSections, parseGitConfigBytes, parseGitConfigSections };
|
|
209
|
+
|
|
210
|
+
//# sourceMappingURL=configSections.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configSections.js","names":[],"sources":["../../../../git/src/watching/configSections.ts"],"sourcesContent":["/**\n * A git config file's variables by section, as git reads them: each key is a section's full name (`core`,\n * `branch.feature/x`: the section name lowercased, then its subsection), and each value lists that section's variables\n * in file order as `<key>=<value>` (the key lowercased, the value unquoted and unescaped), or a bare `<key>` for one\n * with no value. A section whose header repeats lists the variables of every occurrence.\n */\nexport type GitConfigSections = ReadonlyMap<string, readonly string[]>;\n\n/**\n * What changed between two reads of the same config file: every local branch (full `refs/heads/` name) whose\n * `branch.<name>` section changed, and whether that is all that changed. `undefined` when nothing changed.\n */\nexport interface GitConfigSectionsChange {\n\treadonly branches: ReadonlySet<string>;\n\treadonly confined: boolean;\n}\n\nconst utf8 = new TextDecoder('utf-8', { fatal: true });\n\n/** Parses a config file's bytes; `undefined` when they aren't UTF-8 or git would reject them */\nexport function parseGitConfigBytes(data: Uint8Array): GitConfigSections | undefined {\n\tlet text: string;\n\ttry {\n\t\ttext = utf8.decode(data);\n\t} catch {\n\t\treturn undefined;\n\t}\n\treturn parseGitConfigSections(text);\n}\n\n/**\n * Parses a config file's text the way git does (`config.c`): a header is `[section]`, `[section \"subsection\"]` (the\n * subsection kept as written, `\\` escaping the next character) or the legacy `[section.subsection]` (lowercased); a\n * variable may follow a header on its line; `#` and `;` start a comment outside quotes; a value may be quoted, and `\\`\n * escapes `\"`, `\\`, `n`, `t` and `b`, or continues the value on the next line. `undefined` for anything git rejects\n * (a malformed header or name, an unknown escape, a line ending inside quotes), and for a variable before any header,\n * which has no section to compare.\n */\nexport function parseGitConfigSections(text: string): GitConfigSections | undefined {\n\tconst sections = new Map<string, string[]>();\n\tlet index = 0;\n\tlet eof = false;\n\n\t/** Git's `get_next_char`: CRLF reads as LF, and the end of the text reads as a final LF */\n\tconst next = (): string => {\n\t\tif (index >= text.length) {\n\t\t\teof = true;\n\t\t\treturn '\\n';\n\t\t}\n\n\t\tconst c = text[index++];\n\t\tif (c === '\\r' && text[index] === '\\n') {\n\t\t\tindex++;\n\t\t\treturn '\\n';\n\t\t}\n\t\treturn c;\n\t};\n\n\t/** The rest of a `[section \"subsection\"]` header, from the whitespace after the section name */\n\tconst readSubsection = (first: string): string | undefined => {\n\t\tlet c = first;\n\t\tdo {\n\t\t\tif (c === '\\n') return undefined;\n\n\t\t\tc = next();\n\t\t} while (isSpace(c));\n\t\tif (c !== '\"') return undefined;\n\n\t\tlet subsection = '';\n\t\tfor (;;) {\n\t\t\tc = next();\n\t\t\tif (c === '\\n') return undefined;\n\t\t\tif (c === '\"') break;\n\n\t\t\tif (c === '\\\\') {\n\t\t\t\tc = next();\n\t\t\t\tif (c === '\\n') return undefined;\n\t\t\t}\n\n\t\t\tsubsection += c;\n\t\t}\n\t\treturn next() === ']' ? subsection : undefined;\n\t};\n\n\t/** A header's full section name, from just after its `[` */\n\tconst readHeader = (): string | undefined => {\n\t\tlet name = '';\n\t\tfor (;;) {\n\t\t\tconst c = next();\n\t\t\tif (eof) return undefined;\n\t\t\tif (c === ']') return name;\n\n\t\t\tif (isSpace(c)) {\n\t\t\t\tconst subsection = readSubsection(c);\n\t\t\t\treturn subsection != null ? `${name}.${subsection}` : undefined;\n\t\t\t}\n\t\t\tif (!isKeyChar(c) && c !== '.') return undefined;\n\n\t\t\tname += c.toLowerCase();\n\t\t}\n\t};\n\n\t/** Git's `parse_value`: a value, from just after its `=` to the end of its (last continued) line */\n\tconst readValue = (): string | undefined => {\n\t\tlet value = '';\n\t\tlet quoted = false;\n\t\tlet comment = false;\n\t\tlet trimTo: number | undefined;\n\t\tfor (;;) {\n\t\t\tlet c = next();\n\t\t\tif (c === '\\n') {\n\t\t\t\tif (quoted) return undefined;\n\n\t\t\t\treturn trimTo != null ? value.substring(0, trimTo) : value;\n\t\t\t}\n\t\t\tif (comment) continue;\n\n\t\t\tif (isSpace(c) && !quoted) {\n\t\t\t\ttrimTo ??= value.length;\n\t\t\t\tif (value.length) {\n\t\t\t\t\tvalue += c;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!quoted && (c === ';' || c === '#')) {\n\t\t\t\tcomment = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\ttrimTo = undefined;\n\t\t\tif (c === '\\\\') {\n\t\t\t\tc = next();\n\t\t\t\tswitch (c) {\n\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tcase 't':\n\t\t\t\t\t\tc = '\\t';\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'b':\n\t\t\t\t\t\tc = '\\b';\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'n':\n\t\t\t\t\t\tc = '\\n';\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '\\\\':\n\t\t\t\t\tcase '\"':\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t\tvalue += c;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (c === '\"') {\n\t\t\t\tquoted = !quoted;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tvalue += c;\n\t\t}\n\t};\n\n\tlet section: string[] | undefined;\n\tlet comment = false;\n\tfor (;;) {\n\t\tconst c = next();\n\t\tif (c === '\\n') {\n\t\t\tif (eof) return sections;\n\n\t\t\tcomment = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (comment || isSpace(c)) continue;\n\n\t\tif (c === '#' || c === ';') {\n\t\t\tcomment = true;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (c === '[') {\n\t\t\tconst name = readHeader();\n\t\t\tif (!name) return undefined;\n\n\t\t\tsection = sections.get(name);\n\t\t\tif (section == null) {\n\t\t\t\tsection = [];\n\t\t\t\tsections.set(name, section);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (section == null || !isAlpha(c)) return undefined;\n\n\t\t// Git's `get_value`: the name runs to the first character a name can't hold\n\t\tlet key = c.toLowerCase();\n\t\tlet k: string;\n\t\tfor (;;) {\n\t\t\tk = next();\n\t\t\tif (eof || !isKeyChar(k)) break;\n\n\t\t\tkey += k.toLowerCase();\n\t\t}\n\t\twhile (k === ' ' || k === '\\t') {\n\t\t\tk = next();\n\t\t}\n\n\t\tif (k === '\\n') {\n\t\t\tsection.push(key);\n\t\t\tif (eof) return sections;\n\n\t\t\tcontinue;\n\t\t}\n\t\tif (k !== '=') return undefined;\n\n\t\tconst value = readValue();\n\t\tif (value == null) return undefined;\n\n\t\tsection.push(`${key}=${value}`);\n\t\tif (eof) return sections;\n\t}\n}\n\n/**\n * Compares two reads of a config file section by section. A changed `branch.<name>` section (a subsection of\n * `branch`) names that local branch; any other changed section, the bare `[branch]` included, makes the change\n * unconfined.\n */\nexport function diffGitConfigSections(\n\tbefore: GitConfigSections,\n\tafter: GitConfigSections,\n): GitConfigSectionsChange | undefined {\n\tconst branches = new Set<string>();\n\tlet changed = false;\n\tlet confined = true;\n\n\tconst compare = (name: string): void => {\n\t\tif (sameVariables(before.get(name), after.get(name))) return;\n\n\t\tchanged = true;\n\t\tif (name.startsWith('branch.') && name.length > 7) {\n\t\t\tbranches.add(`refs/heads/${name.substring(7)}`);\n\t\t} else {\n\t\t\tconfined = false;\n\t\t}\n\t};\n\n\tfor (const name of before.keys()) {\n\t\tcompare(name);\n\t}\n\tfor (const name of after.keys()) {\n\t\tif (!before.has(name)) {\n\t\t\tcompare(name);\n\t\t}\n\t}\n\treturn changed ? { branches: branches, confined: confined } : undefined;\n}\n\nfunction sameVariables(a: readonly string[] | undefined, b: readonly string[] | undefined): boolean {\n\tif (a == null || b == null) return a === b;\n\tif (a.length !== b.length) return false;\n\n\treturn a.every((v, i) => v === b[i]);\n}\n\n/** Git's own `isspace`, which leaves out `\\v` and `\\f`: a value keeps them, so they must not compare as trimmed */\nfunction isSpace(c: string): boolean {\n\treturn c === ' ' || c === '\\t' || c === '\\n' || c === '\\r';\n}\n\nfunction isAlpha(c: string): boolean {\n\treturn (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');\n}\n\n/** Git's `iskeychar`: an ASCII letter or digit, or `-` */\nfunction isKeyChar(c: string): boolean {\n\treturn isAlpha(c) || (c >= '0' && c <= '9') || c === '-';\n}\n"],"mappings":"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"}
|
|
@@ -10,10 +10,13 @@ import { GitDirPoller, GitDirProbeTarget } from "./gitDirPoller.js";
|
|
|
10
10
|
export interface WatchGroupHooks {
|
|
11
11
|
/**
|
|
12
12
|
* `ref` is the full ref name (or ref directory) or pseudo-ref (`HEAD`, `ORIG_HEAD`) whose file changed, with a
|
|
13
|
-
* trailing `.lock` stripped; `
|
|
14
|
-
* watcher that dropped events, which no single write can
|
|
13
|
+
* trailing `.lock` stripped; `false` when config changed without moving refs, and `undefined` when ref movement
|
|
14
|
+
* is unknown. `overflow` marks the every-kind rescan of a watcher that dropped events, which no single write can
|
|
15
|
+
* account for. `configBranches`, with a `config` change, are the local branches (full ref names) whose
|
|
16
|
+
* `branch.<name>` sections are all that changed in the shared `.git/config`; without it, the config change may be
|
|
17
|
+
* anywhere.
|
|
15
18
|
*/
|
|
16
|
-
readonly onRepoChanged: (repoPath: string, changes: RepositoryChange[], ref?: string, overflow?: boolean) => void;
|
|
19
|
+
readonly onRepoChanged: (repoPath: string, changes: RepositoryChange[], ref?: string | false, overflow?: boolean, configBranches?: ReadonlySet<string>) => void;
|
|
17
20
|
readonly onFetchHeadChanged?: (repoPath: string) => void;
|
|
18
21
|
readonly onIgnoresChanged?: (repoPath: string) => void;
|
|
19
22
|
/** The refs whose loose files a poll stats for this session, read at each poll */
|
|
@@ -49,20 +52,39 @@ interface SessionEntry {
|
|
|
49
52
|
*
|
|
50
53
|
* For a standard (non-worktree) repo, the common and root directories
|
|
51
54
|
* are the same, but we still create separate watchers for clean separation.
|
|
55
|
+
*
|
|
56
|
+
* Given a `readFile`, the group keeps the shared `.git/config` as it last read it, section by section. A report of
|
|
57
|
+
* that file (or its lock), from whichever watcher, is read and compared with it once, and every session hears what
|
|
58
|
+
* changed: only some branches' `branch.<name>` sections (`config`, confined to them), anything else (`config` and
|
|
59
|
+
* `remotes`, unconfined), or nothing at all, when an earlier report's read already delivered the change. Reads run
|
|
60
|
+
* one at a time, so each compares with the one before it and none is skipped. The first read, taken as the group
|
|
61
|
+
* starts, is the baseline and delivers nothing; a report arriving before it completes may describe a change it
|
|
62
|
+
* already holds, so it is delivered unconfined. A read that fails or doesn't settle within a few seconds, or a file
|
|
63
|
+
* git couldn't parse, is unconfined too.
|
|
52
64
|
*/
|
|
53
65
|
export declare class WatchGroup implements UnifiedDisposable {
|
|
54
66
|
readonly commonGitDir: string;
|
|
55
67
|
private readonly provider;
|
|
56
68
|
/** When given, a watcher that reports it can't be trusted is polled; otherwise none is asked */
|
|
57
69
|
private readonly poller?;
|
|
70
|
+
/** When given, reports of the shared `.git/config` are classified by what changed in it (see above) */
|
|
71
|
+
private readonly readFile?;
|
|
58
72
|
private readonly _sessions;
|
|
59
73
|
private _commonWatcher;
|
|
60
74
|
private _commonReliability;
|
|
61
75
|
private _commonTarget;
|
|
62
76
|
private _disposed;
|
|
77
|
+
/** The shared `.git/config`, by section, as the last read that could parse it found it */
|
|
78
|
+
private _config;
|
|
79
|
+
/** The baseline read is in flight */
|
|
80
|
+
private _configBaselining;
|
|
81
|
+
private _configReading;
|
|
82
|
+
private _configRereadQueued;
|
|
63
83
|
constructor(commonGitDir: string, provider: FileWatchingProvider,
|
|
64
84
|
/** When given, a watcher that reports it can't be trusted is polled; otherwise none is asked */
|
|
65
|
-
poller?: GitDirPoller | undefined
|
|
85
|
+
poller?: GitDirPoller | undefined,
|
|
86
|
+
/** When given, reports of the shared `.git/config` are classified by what changed in it (see above) */
|
|
87
|
+
readFile?: ((path: string) => Promise<Uint8Array>) | undefined);
|
|
66
88
|
[Symbol.dispose](): void;
|
|
67
89
|
/** Dispose the entire group (common watcher + all root watchers). */
|
|
68
90
|
dispose(): void;
|
|
@@ -97,6 +119,17 @@ export declare class WatchGroup implements UnifiedDisposable {
|
|
|
97
119
|
private onCommonEvent;
|
|
98
120
|
private deliverOverflow;
|
|
99
121
|
private onRootEvent;
|
|
122
|
+
private onConfigReported;
|
|
123
|
+
private readConfigBaseline;
|
|
124
|
+
/** Reads and compares until no report came in during the last read: it may describe a change the read missed */
|
|
125
|
+
private readConfigChanges;
|
|
126
|
+
/** `undefined` when the file couldn't be read or parsed, or the read didn't settle in time */
|
|
127
|
+
private readConfig;
|
|
128
|
+
/**
|
|
129
|
+
* Tells every session of a change to the shared `.git/config`: confined to some branches' sections, or (`undefined`,
|
|
130
|
+
* or a change outside them) anywhere, which may have changed a remote's settings too. Either way no ref moved
|
|
131
|
+
*/
|
|
132
|
+
private deliverConfigChange;
|
|
100
133
|
}
|
|
101
134
|
//#endregion
|
|
102
135
|
//# sourceMappingURL=watchGroup.d.ts.map
|