dsh-git-branch-manage 1.1.0 → 1.3.0

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.
@@ -1,49 +1,41 @@
1
1
  /**
2
- * The one entry point every credential channel goes through (decision 0034).
2
+ * The one entry point every credential channel goes through (decision 0037).
3
3
  *
4
- * Before this module the store, the `-c http.<url>.extraHeader` injection
5
- * (`authConfigArgs`, 0021/0029) and the shell environment bridge
6
- * (`shell-bridge.ts`, 0032/0034) were assembled in three places — the routes,
7
- * the two model-facing tools and the plugin entry — each resolving the store
8
- * path itself. Every future plugin that needs git credentials would have
9
- * repeated that wiring and drifted from it.
4
+ * The store is the machine's own `~/.git-credentials` (git's
5
+ * `credential-store` format), plus a secret-free metadata sidecar for the
6
+ * panel's "method / updated" columns. Every channel — the panel routes, both
7
+ * model-facing tools, and a plain `git` command in any shell — authenticates
8
+ * through the same `credential-store` helper, because `createShellBridge`
9
+ * installs a URL-scoped `helper = store` include into `~/.gitconfig`.
10
10
  *
11
- * `createCredentialService` owns the store instance and both injection
12
- * channels, so callers share one contract:
11
+ * `createCredentialService` owns the store instance and the include setup, so
12
+ * callers share one contract:
13
13
  *
14
- * - {@link CredentialService.authConfigArgs} — the `-c extraHeader` pairs for
15
- * git commands the plugin itself runs (panel routes, `git_network`). The
16
- * token rides the argv of that one command and never an environment.
17
- * - {@link CredentialService.refreshShellEnv} — re-derive the session
18
- * gitconfig + `GIT_CONFIG_GLOBAL` environment so a plain shell `git push`
19
- * authenticates as configured. Call it after every store change.
20
- * - {@link CredentialService.dispose} — undo the environment injection and
21
- * delete the session config file (plugin teardown).
14
+ * - {@link CredentialService.refreshShellEnv} — re-derive the include after a
15
+ * store change and run the one-time legacy import. Call it after every save
16
+ * / delete / sign-in; it never throws.
17
+ * - {@link CredentialService.dispose} — remove the include line and the
18
+ * generated file on plugin teardown. The credentials themselves stay in
19
+ * `~/.git-credentials`: they belong to the user, not to the plugin.
22
20
  *
23
21
  * A store that cannot be read degrades to "no credentials" on every channel
24
22
  * (an unauthenticated command runs as it always did; the credentials route is
25
23
  * where the file problem surfaces) — a broken file must never break git.
26
24
  */
27
25
  import { type CredentialStore } from './credentials.js';
26
+ import { type LocalImportReport } from './local-import.js';
28
27
  /** Host-side seams the service reads through (tests substitute all of them). */
29
28
  export interface CredentialServiceDeps {
30
29
  /** User home directory seam. */
31
30
  readonly home: () => string;
32
31
  /** `$DSH_HOME` seam (blank/undefined = unset). */
33
32
  readonly envHome: () => string | undefined;
34
- /** Store seam; the fs-backed store at the resolved path when absent. */
33
+ /** Store seam; the fs-backed local store when absent. */
35
34
  readonly store?: CredentialStore;
36
35
  /** The environment object to mutate; `process.env` when absent. */
37
36
  readonly env?: Record<string, string | undefined>;
38
- /** Absolute helper script path; the compiled sibling under `helpers/` when absent. */
39
- readonly helperPath?: string;
40
- /** Node executable that runs the helper; `process.execPath` when absent. */
41
- readonly nodePath?: string;
42
- /** Directory for the session config; the OS temp dir when absent. */
43
- readonly configDir?: () => string;
44
- /** Exact session config path (tests); derived from the pid when absent. */
45
- readonly configPath?: string;
46
- /** User global config the session file includes; `<home>/.gitconfig` when absent. */
37
+ /** User global config the generated include is installed into;
38
+ * `<home>/.gitconfig` when absent. */
47
39
  readonly userGitconfig?: string;
48
40
  }
49
41
  /** The single credential entry point. */
@@ -51,24 +43,26 @@ export interface CredentialService {
51
43
  /** The shared global store (one row per remote host). */
52
44
  readonly store: CredentialStore;
53
45
  /**
54
- * `-c http.<url>.extraHeader` pairs for every stored credential, ready to
55
- * prepend to a git network command the plugin runs itself. A broken store
56
- * degrades to "no credentials" rather than throwing.
46
+ * Import the retired plugin-private file (once) and re-derive the git
47
+ * include after a credential changed, so a message sent in the same session
48
+ * can `git push` with the token the user just saved. Never throws: the worst
49
+ * case is a stale include until the next refresh.
57
50
  */
58
- readonly authConfigArgs: () => Promise<readonly string[]>;
51
+ readonly refreshShellEnv: () => Promise<void>;
59
52
  /**
60
- * Re-derive the shell credential environment after a credential changed, so
61
- * a message sent in the same session can `git push` with the token the user
62
- * just saved. Never throws: the worst case is a stale environment until the
63
- * next refresh.
53
+ * Copy the credentials the machine's own helpers already answer for into
54
+ * the store, once per process (decision 0038). Runs on startup so a machine
55
+ * already signed in through gh / GCM / Keychain keeps working without the
56
+ * user re-entering a token here. Idempotent, never throws; the hosts that
57
+ * did import trigger one include refresh.
64
58
  */
65
- readonly refreshShellEnv: () => Promise<void>;
66
- /** Remove the environment injection and delete the session config file. */
59
+ readonly autoImport: () => Promise<LocalImportReport>;
60
+ /** Remove the fail-fast switches, the include line and the generated file. */
67
61
  readonly dispose: () => Promise<void>;
68
62
  }
69
63
  /**
70
64
  * Build the credential service for one host process: one store instance, one
71
- * session-gitconfig bridge, shared by the routes and every tool.
65
+ * gitconfig include, shared by the routes and every tool.
72
66
  * @param deps - host seams (see {@link CredentialServiceDeps}).
73
67
  */
74
68
  export declare function createCredentialService(deps: CredentialServiceDeps): CredentialService;
@@ -1,53 +1,82 @@
1
1
  /**
2
- * The one entry point every credential channel goes through (decision 0034).
2
+ * The one entry point every credential channel goes through (decision 0037).
3
3
  *
4
- * Before this module the store, the `-c http.<url>.extraHeader` injection
5
- * (`authConfigArgs`, 0021/0029) and the shell environment bridge
6
- * (`shell-bridge.ts`, 0032/0034) were assembled in three places — the routes,
7
- * the two model-facing tools and the plugin entry — each resolving the store
8
- * path itself. Every future plugin that needs git credentials would have
9
- * repeated that wiring and drifted from it.
4
+ * The store is the machine's own `~/.git-credentials` (git's
5
+ * `credential-store` format), plus a secret-free metadata sidecar for the
6
+ * panel's "method / updated" columns. Every channel — the panel routes, both
7
+ * model-facing tools, and a plain `git` command in any shell — authenticates
8
+ * through the same `credential-store` helper, because `createShellBridge`
9
+ * installs a URL-scoped `helper = store` include into `~/.gitconfig`.
10
10
  *
11
- * `createCredentialService` owns the store instance and both injection
12
- * channels, so callers share one contract:
11
+ * `createCredentialService` owns the store instance and the include setup, so
12
+ * callers share one contract:
13
13
  *
14
- * - {@link CredentialService.authConfigArgs} — the `-c extraHeader` pairs for
15
- * git commands the plugin itself runs (panel routes, `git_network`). The
16
- * token rides the argv of that one command and never an environment.
17
- * - {@link CredentialService.refreshShellEnv} — re-derive the session
18
- * gitconfig + `GIT_CONFIG_GLOBAL` environment so a plain shell `git push`
19
- * authenticates as configured. Call it after every store change.
20
- * - {@link CredentialService.dispose} — undo the environment injection and
21
- * delete the session config file (plugin teardown).
14
+ * - {@link CredentialService.refreshShellEnv} — re-derive the include after a
15
+ * store change and run the one-time legacy import. Call it after every save
16
+ * / delete / sign-in; it never throws.
17
+ * - {@link CredentialService.dispose} — remove the include line and the
18
+ * generated file on plugin teardown. The credentials themselves stay in
19
+ * `~/.git-credentials`: they belong to the user, not to the plugin.
22
20
  *
23
21
  * A store that cannot be read degrades to "no credentials" on every channel
24
22
  * (an unauthenticated command runs as it always did; the credentials route is
25
23
  * where the file problem surfaces) — a broken file must never break git.
26
24
  */
27
- import { authConfigArgs, createCredentialStore, credentialFilePath, } from './credentials.js';
25
+ import { createLocalCredentialStore, migrateLegacyCredentialFile, } from './credentials.js';
28
26
  import { createShellBridge } from './shell-bridge.js';
27
+ import { importLocalCredentials } from './local-import.js';
29
28
  /**
30
29
  * Build the credential service for one host process: one store instance, one
31
- * session-gitconfig bridge, shared by the routes and every tool.
30
+ * gitconfig include, shared by the routes and every tool.
32
31
  * @param deps - host seams (see {@link CredentialServiceDeps}).
33
32
  */
34
33
  export function createCredentialService(deps) {
35
- const store = deps.store
36
- ?? createCredentialStore(credentialFilePath(deps.home(), deps.envHome()));
37
- const bridgeDeps = { ...deps, store };
34
+ const store = deps.store ?? createLocalCredentialStore(deps.home(), deps.envHome());
35
+ const bridgeDeps = {
36
+ home: deps.home,
37
+ envHome: deps.envHome,
38
+ store,
39
+ ...(deps.env === undefined ? {} : { env: deps.env }),
40
+ ...(deps.userGitconfig === undefined ? {} : { userGitconfig: deps.userGitconfig }),
41
+ };
38
42
  const shellBridge = createShellBridge(bridgeDeps);
43
+ let autoImportTask;
39
44
  return {
40
45
  store,
41
- async authConfigArgs() {
46
+ async refreshShellEnv() {
42
47
  try {
43
- return authConfigArgs(await store.load());
48
+ await migrateLegacyCredentialFile(deps.home(), deps.envHome(), store);
44
49
  }
45
50
  catch {
46
- return [];
51
+ // A damaged legacy file must not block the include refresh; the
52
+ // credentials route reports it when the user opens the panel.
47
53
  }
54
+ await shellBridge.apply();
48
55
  },
49
- refreshShellEnv() {
50
- return shellBridge.apply();
56
+ autoImport() {
57
+ autoImportTask ??= (async () => {
58
+ try {
59
+ // The legacy file migrates first so an imported host cannot
60
+ // duplicate a row the panel already had.
61
+ await migrateLegacyCredentialFile(deps.home(), deps.envHome(), store);
62
+ }
63
+ catch {
64
+ // Same contract as refreshShellEnv: a broken legacy file is not
65
+ // worth failing the startup import over.
66
+ }
67
+ try {
68
+ const report = await importLocalCredentials(store, { env: deps.env ?? process.env });
69
+ if (report.imported.length > 0)
70
+ await shellBridge.apply();
71
+ return report;
72
+ }
73
+ catch {
74
+ // The import is opportunistic: a missing git, an unreadable store
75
+ // or a helper that misbehaves just leaves the panel as it was.
76
+ return { imported: [], skipped: [], failed: [] };
77
+ }
78
+ })();
79
+ return autoImportTask;
51
80
  },
52
81
  dispose() {
53
82
  return shellBridge.dispose();
@@ -1,25 +1,36 @@
1
1
  /**
2
- * Global git credential store (host half). Credentials belong to a remote
3
- * HOST, not to a project: the same `github.com` token serves every repository
4
- * the panel touches, so the record lives in one file under `$DSH_HOME`
5
- * (`~/.dsh`) instead of the per-repository settings document. The secret
6
- * never travels to the browser — the list route answers masked tails only —
7
- * and the store is read/written by the host alone.
2
+ * Global git credential store (host half), backed by the machine's own
3
+ * credential file (decision 0037).
8
4
  *
9
- * Authentication rides `git -c http.https://<host>/.extraHeader=…` on the
10
- * panel's own network commands and on the `git_network` tool: the user's git
11
- * configuration FILES are never written. Plain `git` commands in a spawned
12
- * shell are covered separately by the environment bridge in `shell-bridge.ts`,
13
- * which installs this store's per-host tokens as URL-scoped credential helpers
14
- * through `GIT_CONFIG_*` (process environment only, never a config file).
15
- * Only `https://` remotes receive the header; ssh and plain-http remotes are
16
- * never sent a secret.
5
+ * A credential belongs to a remote HOST, not to a project: the same
6
+ * `github.com` token serves every repository. Up to 1.1.x the plugin kept its
7
+ * own `~/.dsh/git-credentials.json`, which meant the panel's token was
8
+ * invisible to a plain `git` run in a shell and the machine's helpers
9
+ * answered instead (often with a different account). 0037 makes the panel
10
+ * manage the SAME file git's built-in `credential-store` helper uses —
11
+ * `~/.git-credentials` — so one save is visible to every git process:
12
+ *
13
+ * - the secret lines are the store format (`https://user:pass@host`),
14
+ * percent-encoded exactly as `git credential approve` writes them;
15
+ * - method/updatedAt cannot live in that format, so a sibling
16
+ * `<dsh-home>/git-credentials.meta.json` keeps the UI-only metadata
17
+ * (no secret ever goes in there);
18
+ * - a generated `<dsh-home>/git-credentials.include.gitconfig` scopes
19
+ * `helper = store` to each managed host, and `~/.gitconfig` gains one
20
+ * `[include] path = <that file>` line (idempotent, decision 0037
21
+ * overturns 0021's "never write the user's git config" for exactly this
22
+ * one scoped include).
23
+ *
24
+ * The secret never travels to the browser — the list route answers masked
25
+ * tails only — and only `https://` remotes are ever covered.
17
26
  */
18
27
  import type { CredentialMethod } from './wire.js';
19
28
  /**
20
29
  * The Basic-auth name git sends when a token was stored without a username.
21
30
  * GitHub, GitLab and Gitee all accept any non-empty name for a personal access
22
- * token, and `oauth2` is the conventional placeholder.
31
+ * token, and `oauth2` is the conventional placeholder. The store format has no
32
+ * "blank username", so this value is what a blank one becomes on disk and is
33
+ * read back as blank again.
23
34
  */
24
35
  export declare const TOKEN_USERNAME = "oauth2";
25
36
  /** One stored credential: exactly one per remote host. */
@@ -27,19 +38,19 @@ export interface GitCredential {
27
38
  /** Normalized remote host (`github.com`, `git.example.com:8443`). */
28
39
  host: string;
29
40
  /** Basic-auth username (a real account name, or a vendor-form placeholder
30
- * such as `x-access-token` / `oauth2`). */
41
+ * such as `x-access-token` / `oauth2`). Blank for a bare token. */
31
42
  username: string;
32
43
  /** Token or password, verbatim (never returned to the client). */
33
44
  secret: string;
34
45
  /** How the secret is meant to be used — informational for the UI; git
35
46
  * itself only sees the Basic header. */
36
47
  method: CredentialMethod;
37
- /** Epoch millis of the last save. */
48
+ /** Epoch millis of the last save (0 when unknown). */
38
49
  updatedAt: number;
39
50
  }
40
51
  /** Load/save seam for the credential file (tests substitute an in-memory one). */
41
52
  export interface CredentialStore {
42
- /** Absolute file the store persists to (for diagnostics/UI hints). */
53
+ /** Absolute file the store persists secrets to (for diagnostics/UI hints). */
43
54
  readonly path: string;
44
55
  /** Every stored credential, one per host, sorted by host. A missing file
45
56
  * is the empty list; an unreadable/malformed one throws
@@ -54,11 +65,32 @@ export declare class CredentialStoreError extends Error {
54
65
  constructor(path: string, message: string);
55
66
  }
56
67
  /**
57
- * Resolve the credential file location: `<dsh-home>/git-credentials.json`.
68
+ * The retired 0021/0029 plugin-private file `<dsh-home>/git-credentials.json`.
69
+ * Read once by {@link migrateLegacyCredentialFile} and deleted afterwards.
58
70
  * @param home - user home directory seam.
59
71
  * @param envHome - `$DSH_HOME` environment value seam (blank = unset).
60
72
  */
61
73
  export declare function credentialFilePath(home: string, envHome: string | undefined): string;
74
+ /**
75
+ * `~/.git-credentials` — the file git's `credential-store` helper reads and
76
+ * writes (`$HOME/.git-credentials` on every platform).
77
+ * @param home - user home directory seam.
78
+ */
79
+ export declare function gitCredentialsFile(home: string): string;
80
+ /**
81
+ * The UI-only metadata sidecar: method and `updatedAt` per host. Never holds a
82
+ * secret; a missing or damaged file only costs the "how/when" columns.
83
+ * @param home - user home directory seam.
84
+ * @param envHome - `$DSH_HOME` environment value seam.
85
+ */
86
+ export declare function credentialMetaFile(home: string, envHome: string | undefined): string;
87
+ /**
88
+ * The generated gitconfig included from `~/.gitconfig`: one URL-scoped
89
+ * `helper = store` section per managed host.
90
+ * @param home - user home directory seam.
91
+ * @param envHome - `$DSH_HOME` environment value seam.
92
+ */
93
+ export declare function credentialIncludeFile(home: string, envHome: string | undefined): string;
62
94
  /** Why a user-typed host is not usable yet (undefined = fine). */
63
95
  export type CredentialHostIssue = 'empty' | 'invalid';
64
96
  /**
@@ -99,16 +131,6 @@ export declare function credentialSecretIssue(value: string): 'empty' | 'illegal
99
131
  * @param secret - the stored secret.
100
132
  */
101
133
  export declare function maskSecret(secret: string): string;
102
- /**
103
- * The git config argv that authenticates network commands against the stored
104
- * hosts: one `http.<https-url>.extraHeader` per host. Git applies a
105
- * `http.<url>.*` section only to a matching remote URL, so injecting every
106
- * stored host at once cannot leak one host's header to another; only
107
- * `https://` URLs match (ssh/http never receive it).
108
- * @param entries - stored credentials (duplicates collapsed by host).
109
- * @returns the `-c <key>=<value>` pairs, ready to prepend to a subcommand.
110
- */
111
- export declare function authConfigArgs(entries: readonly GitCredential[]): readonly string[];
112
134
  /**
113
135
  * Insert or update one credential; the pair `(host, updatedAt)` changes,
114
136
  * everything else is replaced. An existing host keeps its position.
@@ -127,22 +149,105 @@ export declare function removeCredential(entries: readonly GitCredential[], host
127
149
  /** Stable host order for the file and the UI. */
128
150
  export declare function sortCredentials(entries: readonly GitCredential[]): GitCredential[];
129
151
  /**
130
- * Parse the credential file. Unknown extra fields are tolerated (a
131
- * hand-edited file), but a malformed entry is an error rather than a silent
132
- * drop — losing a credential behind the user's back is worse than an
133
- * explicit message. Hosts are re-normalized and de-duplicated.
152
+ * Percent-encode one userinfo component the way git's store helper does:
153
+ * everything outside the RFC 3986 unreserved set becomes `%xx` (lowercase
154
+ * hex, matching `git credential approve`). `encodeURIComponent` would leave
155
+ * `!'()*` alone and git would then mis-split the line at a literal `@`.
156
+ * @param value - raw username or secret.
157
+ */
158
+ export declare function encodeCredentialPart(value: string): string;
159
+ /**
160
+ * One `<scheme>://user:pass@host` line, ready for `~/.git-credentials`.
161
+ * @param entry - the credential to write.
162
+ */
163
+ export declare function formatCredentialLine(entry: GitCredential): string;
164
+ /** A `~/.git-credentials` file split into credentials and untouched lines. */
165
+ export interface ParsedCredentials {
166
+ /** One credential per https host; a duplicated host keeps the LAST line,
167
+ * which is the line git's store helper would resolve to after a rewrite. */
168
+ readonly entries: GitCredential[];
169
+ /** Lines this store does not own (comments, ssh/http rows, malformed
170
+ * text). Written back verbatim so a hand-edited file survives a save. */
171
+ readonly others: string[];
172
+ }
173
+ /**
174
+ * Parse a `~/.git-credentials` document.
175
+ * @param text - raw file contents (BOM tolerated).
176
+ */
177
+ export declare function parseGitCredentials(text: string): ParsedCredentials;
178
+ /**
179
+ * Parse the retired `<dsh-home>/git-credentials.json`. Unknown extra fields
180
+ * are tolerated (a hand-edited file), but a malformed entry is an error rather
181
+ * than a silent drop — losing a credential during the one-time migration is
182
+ * worse than an explicit message.
134
183
  * @param text - raw file contents.
135
184
  * @param path - file path (for the error message).
136
185
  */
137
186
  export declare function parseCredentialFile(text: string, path?: string): GitCredential[];
187
+ /** Write-then-rename so a crashed save never truncates the real file. */
188
+ export declare function writeFileAtomic(path: string, text: string, mode?: number): Promise<void>;
189
+ /**
190
+ * The real store over `~/.git-credentials`. The secret lines are the git
191
+ * store format; the sibling metadata file carries method/updatedAt only.
192
+ * A missing file reads as "no credentials yet".
193
+ * @param home - user home directory seam.
194
+ * @param envHome - `$DSH_HOME` environment value seam.
195
+ */
196
+ export declare function createLocalCredentialStore(home: string, envHome: string | undefined): CredentialStore;
197
+ /**
198
+ * One-time 0037 migration: import the retired plugin-private JSON into the
199
+ * local store (plugin values win for a host they share), then delete the old
200
+ * file so there is exactly one place a secret lives.
201
+ *
202
+ * Idempotent: a missing legacy file is a no-op, and the file is removed only
203
+ * after the store write succeeded — a failed migration keeps the old file.
204
+ * @param home - user home directory seam.
205
+ * @param envHome - `$DSH_HOME` environment value seam.
206
+ * @param store - destination store.
207
+ * @returns true when a legacy file was imported and removed.
208
+ */
209
+ export declare function migrateLegacyCredentialFile(home: string, envHome: string | undefined, store: CredentialStore): Promise<boolean>;
210
+ /**
211
+ * The generated include file's contents: one URL-scoped section per host that
212
+ * resets any inherited helper for that exact URL and then installs the
213
+ * built-in `store` helper. The empty `helper =` line is what beats a
214
+ * user-level helper (`gh`, GCM) configured for the same host.
215
+ * @param hosts - managed remote hosts (already normalized).
216
+ */
217
+ export declare function credentialIncludeBody(hosts: readonly string[]): string;
218
+ /**
219
+ * Whether `~/.gitconfig` already includes the generated file. Matched loosely
220
+ * (any `path = <X>` line pointing at it) so a hand-reformatted include — or
221
+ * one with forward slashes on Windows — is not added a second time.
222
+ * @param text - current `~/.gitconfig` contents.
223
+ * @param includePath - the generated file.
224
+ */
225
+ export declare function gitconfigHasInclude(text: string, includePath: string): boolean;
226
+ /**
227
+ * Append the `[include] path = …` section, leaving every existing byte alone.
228
+ * @param text - current `~/.gitconfig` contents.
229
+ * @param includePath - the generated file.
230
+ */
231
+ export declare function appendGitconfigInclude(text: string, includePath: string): string;
232
+ /**
233
+ * Idempotently ensure `~/.gitconfig` includes the generated file. Rewrites
234
+ * atomically, preserving the file's existing mode; a missing file is created.
235
+ * @param gitconfigPath - user global config (`<home>/.gitconfig`).
236
+ * @param includePath - the generated file.
237
+ * @returns true when the file had to be changed.
238
+ */
239
+ export declare function ensureGitconfigInclude(gitconfigPath: string, includePath: string): Promise<boolean>;
138
240
  /**
139
- * Serialize the credential set for the file.
140
- * @param entries - stored credentials.
241
+ * Remove the include section pointing at the generated file (plugin teardown).
242
+ * Every other line is preserved verbatim.
243
+ * @param text - current `~/.gitconfig` contents.
244
+ * @param includePath - the generated file.
141
245
  */
142
- export declare function serializeCredentialFile(entries: readonly GitCredential[]): string;
246
+ export declare function stripGitconfigInclude(text: string, includePath: string): string;
143
247
  /**
144
- * The real file-backed store: atomic replace, `0600` where the platform
145
- * honours it. A missing file reads as "no credentials yet".
146
- * @param path - absolute credential file path.
248
+ * Remove the generated include from `~/.gitconfig`. Best effort: a teardown
249
+ * must not throw, and an already-clean file is left untouched.
250
+ * @param gitconfigPath - user global config (`<home>/.gitconfig`).
251
+ * @param includePath - the generated file.
147
252
  */
148
- export declare function createCredentialStore(path: string): CredentialStore;
253
+ export declare function removeGitconfigInclude(gitconfigPath: string, includePath: string): Promise<void>;