@gitkraken/core-gitlens 0.1.0 → 0.2.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.
- package/CHANGELOG.md +22 -1
- package/dist/git/cache.d.ts +21 -5
- package/dist/git/cache.d.ts.map +1 -1
- package/dist/git/cache.js +102 -6
- package/dist/git/cache.js.map +1 -1
- package/dist/git/context.d.ts +11 -2
- package/dist/git/context.d.ts.map +1 -1
- package/dist/git/context.js.map +1 -1
- package/dist/git/models/branch.d.ts +1 -0
- package/dist/git/models/branch.d.ts.map +1 -1
- package/dist/git/models/branch.js.map +1 -1
- package/dist/git/models/commit.d.ts +1 -9
- package/dist/git/models/commit.d.ts.map +1 -1
- package/dist/git/models/commit.js +4 -9
- package/dist/git/models/commit.js.map +1 -1
- package/dist/git/models/graph.d.ts +2 -4
- package/dist/git/models/graph.d.ts.map +1 -1
- package/dist/git/models/repository.d.ts +12 -3
- package/dist/git/models/repository.d.ts.map +1 -1
- package/dist/git/models/repository.js +25 -4
- package/dist/git/models/repository.js.map +1 -1
- package/dist/git/models/worktree.d.ts +1 -1
- package/dist/git/models/worktree.d.ts.map +1 -1
- package/dist/git/models/worktree.js +2 -2
- package/dist/git/models/worktree.js.map +1 -1
- package/dist/git/parsers/diffParser.d.ts +0 -1
- package/dist/git/parsers/diffParser.d.ts.map +1 -1
- package/dist/git/parsers/diffParser.js +22 -61
- package/dist/git/parsers/diffParser.js.map +1 -1
- package/dist/git/providers/branches.d.ts +3 -0
- package/dist/git/providers/branches.d.ts.map +1 -1
- package/dist/git/providers/commits.d.ts +6 -0
- package/dist/git/providers/commits.d.ts.map +1 -1
- package/dist/git/providers/config.d.ts +2 -0
- package/dist/git/providers/config.d.ts.map +1 -1
- package/dist/git/providers/diff.d.ts +2 -0
- package/dist/git/providers/diff.d.ts.map +1 -1
- package/dist/git/providers/operations.d.ts +6 -0
- package/dist/git/providers/operations.d.ts.map +1 -1
- package/dist/git/providers/revision.d.ts +1 -1
- package/dist/git/providers/revision.d.ts.map +1 -1
- package/dist/git/providers/staging.d.ts +2 -0
- package/dist/git/providers/staging.d.ts.map +1 -1
- package/dist/git/utils/sorting.d.ts +6 -0
- package/dist/git/utils/sorting.d.ts.map +1 -1
- package/dist/git/utils/sorting.js +19 -0
- package/dist/git/utils/sorting.js.map +1 -1
- package/dist/git-cli/cliGitProvider.js +1 -1
- package/dist/git-cli/cliGitProvider.js.map +1 -1
- package/dist/git-cli/exec/git.d.ts +29 -4
- package/dist/git-cli/exec/git.d.ts.map +1 -1
- package/dist/git-cli/exec/git.js +70 -9
- package/dist/git-cli/exec/git.js.map +1 -1
- package/dist/git-cli/exec/gitQueue.d.ts +12 -4
- package/dist/git-cli/exec/gitQueue.d.ts.map +1 -1
- package/dist/git-cli/exec/gitQueue.js +49 -12
- package/dist/git-cli/exec/gitQueue.js.map +1 -1
- package/dist/git-cli/parsers/logParser.d.ts +1 -0
- package/dist/git-cli/parsers/logParser.d.ts.map +1 -1
- package/dist/git-cli/parsers/logParser.js +4 -0
- package/dist/git-cli/parsers/logParser.js.map +1 -1
- package/dist/git-cli/providers/blame.js +1 -1
- package/dist/git-cli/providers/blame.js.map +1 -1
- package/dist/git-cli/providers/branches.d.ts +2 -0
- package/dist/git-cli/providers/branches.d.ts.map +1 -1
- package/dist/git-cli/providers/branches.js +176 -123
- package/dist/git-cli/providers/branches.js.map +1 -1
- package/dist/git-cli/providers/commits.d.ts +4 -0
- package/dist/git-cli/providers/commits.d.ts.map +1 -1
- package/dist/git-cli/providers/commits.js +58 -17
- package/dist/git-cli/providers/commits.js.map +1 -1
- package/dist/git-cli/providers/config.d.ts.map +1 -1
- package/dist/git-cli/providers/config.js +9 -9
- package/dist/git-cli/providers/config.js.map +1 -1
- package/dist/git-cli/providers/contributors.d.ts.map +1 -1
- package/dist/git-cli/providers/contributors.js +7 -4
- package/dist/git-cli/providers/contributors.js.map +1 -1
- package/dist/git-cli/providers/diff.d.ts +2 -0
- package/dist/git-cli/providers/diff.d.ts.map +1 -1
- package/dist/git-cli/providers/diff.js +89 -19
- package/dist/git-cli/providers/diff.js.map +1 -1
- package/dist/git-cli/providers/graph.d.ts.map +1 -1
- package/dist/git-cli/providers/graph.js +43 -27
- package/dist/git-cli/providers/graph.js.map +1 -1
- package/dist/git-cli/providers/operations.d.ts +17 -0
- package/dist/git-cli/providers/operations.d.ts.map +1 -1
- package/dist/git-cli/providers/operations.js +47 -14
- package/dist/git-cli/providers/operations.js.map +1 -1
- package/dist/git-cli/providers/patch.d.ts.map +1 -1
- package/dist/git-cli/providers/patch.js +15 -30
- package/dist/git-cli/providers/patch.js.map +1 -1
- package/dist/git-cli/providers/pausedOperations.js +6 -6
- package/dist/git-cli/providers/pausedOperations.js.map +1 -1
- package/dist/git-cli/providers/refs.d.ts.map +1 -1
- package/dist/git-cli/providers/refs.js +26 -7
- package/dist/git-cli/providers/refs.js.map +1 -1
- package/dist/git-cli/providers/remotes.js +4 -4
- package/dist/git-cli/providers/remotes.js.map +1 -1
- package/dist/git-cli/providers/revision.d.ts +1 -1
- package/dist/git-cli/providers/revision.d.ts.map +1 -1
- package/dist/git-cli/providers/revision.js +15 -11
- package/dist/git-cli/providers/revision.js.map +1 -1
- package/dist/git-cli/providers/staging.d.ts +2 -0
- package/dist/git-cli/providers/staging.d.ts.map +1 -1
- package/dist/git-cli/providers/staging.js +20 -8
- package/dist/git-cli/providers/staging.js.map +1 -1
- package/dist/git-cli/providers/stash.d.ts +0 -8
- package/dist/git-cli/providers/stash.d.ts.map +1 -1
- package/dist/git-cli/providers/stash.js +63 -119
- package/dist/git-cli/providers/stash.js.map +1 -1
- package/dist/git-cli/providers/status.js +6 -6
- package/dist/git-cli/providers/status.js.map +1 -1
- package/dist/git-cli/providers/tags.js +4 -4
- package/dist/git-cli/providers/tags.js.map +1 -1
- package/dist/git-cli/providers/worktrees.js +3 -3
- package/dist/git-cli/providers/worktrees.js.map +1 -1
- package/dist/plus/ai/constants.d.ts +2 -1
- package/dist/plus/ai/constants.d.ts.map +1 -1
- package/dist/plus/ai/constants.js +7 -0
- package/dist/plus/ai/constants.js.map +1 -1
- package/dist/plus/ai/models/model.d.ts +1 -1
- package/dist/plus/ai/models/model.d.ts.map +1 -1
- package/dist/plus/ai/models/model.js.map +1 -1
- package/dist/plus/ai/models/promptTemplates.d.ts +19 -2
- package/dist/plus/ai/models/promptTemplates.d.ts.map +1 -1
- package/dist/plus/ai/models/results.d.ts +28 -0
- package/dist/plus/ai/models/results.d.ts.map +1 -1
- package/dist/plus/ai/prompts.d.ts +3 -0
- package/dist/plus/ai/prompts.d.ts.map +1 -1
- package/dist/plus/ai/prompts.js +126 -0
- package/dist/plus/ai/prompts.js.map +1 -1
- package/dist/plus/ai/utils/ai.utils.d.ts.map +1 -1
- package/dist/plus/ai/utils/ai.utils.js +2 -0
- package/dist/plus/ai/utils/ai.utils.js.map +1 -1
- package/dist/plus/ai/utils/results.utils.d.ts +3 -0
- package/dist/plus/ai/utils/results.utils.d.ts.map +1 -1
- package/dist/plus/ai/utils/results.utils.js +75 -0
- package/dist/plus/ai/utils/results.utils.js.map +1 -1
- package/dist/plus/ai/utils/truncation.utils.d.ts +1 -1
- package/dist/plus/ai/utils/truncation.utils.d.ts.map +1 -1
- package/dist/plus/ai/utils/truncation.utils.js.map +1 -1
- package/dist/plus/git-github/providers/github/branches.d.ts.map +1 -1
- package/dist/plus/git-github/providers/github/branches.js +16 -1
- package/dist/plus/git-github/providers/github/branches.js.map +1 -1
- package/dist/plus/git-github/providers/github/diff.d.ts +2 -0
- package/dist/plus/git-github/providers/github/diff.d.ts.map +1 -1
- package/dist/plus/git-github/providers/github/diff.js.map +1 -1
- package/dist/utils/decorators/log.js +1 -1
- package/dist/utils/decorators/log.js.map +1 -1
- package/dist/utils/decorators/sequentialize.d.ts.map +1 -1
- package/dist/utils/decorators/sequentialize.js +4 -2
- package/dist/utils/decorators/sequentialize.js.map +1 -1
- package/dist/utils/env/node/base64.js.map +1 -1
- package/dist/utils/env/node/exec.js.map +1 -1
- package/dist/utils/formatter.d.ts.map +1 -1
- package/dist/utils/formatter.js +4 -10
- package/dist/utils/formatter.js.map +1 -1
- package/dist/utils/iterable.js.map +1 -1
- package/dist/utils/lruMap.d.ts +40 -0
- package/dist/utils/lruMap.d.ts.map +1 -0
- package/dist/utils/lruMap.js +81 -0
- package/dist/utils/lruMap.js.map +1 -0
- package/dist/utils/version.d.ts +14 -0
- package/dist/utils/version.d.ts.map +1 -1
- package/dist/utils/version.js +48 -0
- package/dist/utils/version.js.map +1 -1
- package/package.json +1 -1
- package/src/git/cache.ts +179 -13
- package/src/git/context.ts +11 -2
- package/src/git/models/branch.ts +1 -0
- package/src/git/models/commit.ts +1 -17
- package/src/git/models/graph.ts +2 -4
- package/src/git/models/repository.ts +26 -6
- package/src/git/models/worktree.ts +2 -2
- package/src/git/parsers/diffParser.ts +7 -44
- package/src/git/providers/branches.ts +3 -0
- package/src/git/providers/commits.ts +7 -0
- package/src/git/providers/config.ts +2 -0
- package/src/git/providers/diff.ts +8 -2
- package/src/git/providers/operations.ts +10 -3
- package/src/git/providers/revision.ts +5 -1
- package/src/git/providers/staging.ts +2 -0
- package/src/git/utils/sorting.ts +21 -0
- package/src/git-cli/cliGitProvider.ts +1 -1
- package/src/git-cli/exec/git.ts +70 -18
- package/src/git-cli/exec/gitQueue.ts +57 -13
- package/src/git-cli/parsers/logParser.ts +5 -1
- package/src/git-cli/providers/blame.ts +1 -1
- package/src/git-cli/providers/branches.ts +207 -133
- package/src/git-cli/providers/commits.ts +71 -18
- package/src/git-cli/providers/config.ts +9 -14
- package/src/git-cli/providers/contributors.ts +7 -12
- package/src/git-cli/providers/diff.ts +100 -19
- package/src/git-cli/providers/graph.ts +47 -27
- package/src/git-cli/providers/operations.ts +77 -18
- package/src/git-cli/providers/patch.ts +15 -37
- package/src/git-cli/providers/pausedOperations.ts +6 -6
- package/src/git-cli/providers/refs.ts +27 -8
- package/src/git-cli/providers/remotes.ts +4 -4
- package/src/git-cli/providers/revision.ts +20 -13
- package/src/git-cli/providers/staging.ts +18 -8
- package/src/git-cli/providers/stash.ts +67 -134
- package/src/git-cli/providers/status.ts +6 -6
- package/src/git-cli/providers/tags.ts +4 -4
- package/src/git-cli/providers/worktrees.ts +3 -3
- package/src/plus/ai/constants.ts +8 -0
- package/src/plus/ai/models/model.ts +1 -0
- package/src/plus/ai/models/promptTemplates.ts +29 -0
- package/src/plus/ai/models/results.ts +30 -0
- package/src/plus/ai/prompts.ts +129 -0
- package/src/plus/ai/utils/ai.utils.ts +2 -0
- package/src/plus/ai/utils/results.utils.ts +99 -0
- package/src/plus/ai/utils/truncation.utils.ts +3 -1
- package/src/plus/git-github/providers/github/branches.ts +17 -5
- package/src/plus/git-github/providers/github/diff.ts +8 -2
- package/src/utils/decorators/log.ts +1 -1
- package/src/utils/decorators/sequentialize.ts +8 -6
- package/src/utils/env/node/base64.ts +1 -1
- package/src/utils/env/node/exec.ts +2 -2
- package/src/utils/formatter.ts +4 -11
- package/src/utils/iterable.ts +1 -1
- package/src/utils/lruMap.ts +87 -0
- package/src/utils/version.ts +48 -0
|
@@ -6,7 +6,7 @@ import { deletedOrMissing } from '../../git/models/revision.js';
|
|
|
6
6
|
import type { GitTag } from '../../git/models/tag.js';
|
|
7
7
|
import type { GitRefsSubProvider } from '../../git/providers/refs.js';
|
|
8
8
|
import { createReference } from '../../git/utils/reference.utils.js';
|
|
9
|
-
import { isShaWithOptionalRevisionSuffix, isUncommitted } from '../../git/utils/revision.utils.js';
|
|
9
|
+
import { isSha, isShaWithOptionalRevisionSuffix, isUncommitted } from '../../git/utils/revision.utils.js';
|
|
10
10
|
import { isCancellationError } from '../../utils/cancellation.js';
|
|
11
11
|
import { debug, trace } from '../../utils/decorators/log.js';
|
|
12
12
|
import { getScopedLogger } from '../../utils/logger.scoped.js';
|
|
@@ -26,7 +26,7 @@ export class RefsGitSubProvider implements GitRefsSubProvider {
|
|
|
26
26
|
@debug()
|
|
27
27
|
async checkIfCouldBeValidBranchOrTagName(repoPath: string, ref: string): Promise<boolean> {
|
|
28
28
|
try {
|
|
29
|
-
const result = await this.git.
|
|
29
|
+
const result = await this.git.run({ cwd: repoPath, errors: 'throw' }, 'check-ref-format', '--branch', ref);
|
|
30
30
|
return Boolean(result.stdout.trim());
|
|
31
31
|
} catch {
|
|
32
32
|
return false;
|
|
@@ -44,10 +44,13 @@ export class RefsGitSubProvider implements GitRefsSubProvider {
|
|
|
44
44
|
const scope = getScopedLogger();
|
|
45
45
|
|
|
46
46
|
try {
|
|
47
|
-
const result = await this.git.
|
|
47
|
+
const result = await this.git.run(
|
|
48
48
|
{
|
|
49
49
|
cwd: repoPath,
|
|
50
50
|
cancellation: cancellation,
|
|
51
|
+
// Why: ref1/ref2 are usually branch names; correctness relies on the gitResults cache being
|
|
52
|
+
// cleared on 'heads'/'remotes' events when refs move. Web (no fs watcher) sees up to
|
|
53
|
+
// `accessTTL` of staleness — acceptable trade-off for the perf win on graph/branch reads.
|
|
51
54
|
caching: { cache: this.cache.gitResults, options: { accessTTL: 5 * 60 * 1000 } },
|
|
52
55
|
},
|
|
53
56
|
'merge-base',
|
|
@@ -105,8 +108,16 @@ export class RefsGitSubProvider implements GitRefsSubProvider {
|
|
|
105
108
|
): Promise<string | undefined> {
|
|
106
109
|
const supportsEndOfOptions = await this.git.supports('git:rev-parse:end-of-options');
|
|
107
110
|
|
|
108
|
-
const result = await this.git.
|
|
109
|
-
{
|
|
111
|
+
const result = await this.git.run(
|
|
112
|
+
{
|
|
113
|
+
cwd: repoPath,
|
|
114
|
+
cancellation: cancellation,
|
|
115
|
+
errors: 'ignore',
|
|
116
|
+
// Why: a fixed ref name's symbolic name is itself stable; only HEAD is mutable, and the
|
|
117
|
+
// gitResults cache is cleared on 'head' events. 60s TTL is the failsafe for watcher
|
|
118
|
+
// latency / web — matches the other "resolve symbolic state" calls in commits.ts.
|
|
119
|
+
caching: { cache: this.cache.gitResults, options: { accessTTL: 60 * 1000 } },
|
|
120
|
+
},
|
|
110
121
|
'rev-parse',
|
|
111
122
|
'--verify',
|
|
112
123
|
'--quiet',
|
|
@@ -164,12 +175,20 @@ export class RefsGitSubProvider implements GitRefsSubProvider {
|
|
|
164
175
|
|
|
165
176
|
const supportsEndOfOptions = await this.git.supports('git:rev-parse:end-of-options');
|
|
166
177
|
|
|
167
|
-
|
|
178
|
+
// Why: a SHA-only validation (no path suffix) is effectively immutable — 5-min TTL is safe.
|
|
179
|
+
// Otherwise the resolved SHA can shift on ref move (or working-tree change for path-scoped
|
|
180
|
+
// validation); rely on gitResults being cleared on 'head'/'heads'/'remotes' events, with 60s
|
|
181
|
+
// TTL as the failsafe for watcher latency / web.
|
|
182
|
+
const stable = relativePath == null && isSha(ref);
|
|
183
|
+
const result = await this.git.run(
|
|
168
184
|
{
|
|
169
185
|
cwd: repoPath,
|
|
170
186
|
cancellation: cancellation,
|
|
171
187
|
errors: 'ignore',
|
|
172
|
-
caching: {
|
|
188
|
+
caching: {
|
|
189
|
+
cache: this.cache.gitResults,
|
|
190
|
+
options: { accessTTL: stable ? 5 * 60 * 1000 : 60 * 1000 },
|
|
191
|
+
},
|
|
173
192
|
},
|
|
174
193
|
'rev-parse',
|
|
175
194
|
'--verify',
|
|
@@ -184,7 +203,7 @@ export class RefsGitSubProvider implements GitRefsSubProvider {
|
|
|
184
203
|
const scope = getScopedLogger();
|
|
185
204
|
|
|
186
205
|
try {
|
|
187
|
-
await this.git.
|
|
206
|
+
await this.git.run({ cwd: repoPath, cancellation: cancellation }, 'update-ref', ref, newRef);
|
|
188
207
|
} catch (ex) {
|
|
189
208
|
scope?.error(ex);
|
|
190
209
|
if (isCancellationError(ex)) throw ex;
|
|
@@ -41,7 +41,7 @@ export class RemotesGitSubProvider extends RemotesGitProviderBase implements Git
|
|
|
41
41
|
const remoteProviderMatcher = createRemoteProviderMatcher(configs, this.context.remotes);
|
|
42
42
|
|
|
43
43
|
try {
|
|
44
|
-
const result = await this.git.
|
|
44
|
+
const result = await this.git.run(
|
|
45
45
|
{
|
|
46
46
|
cwd: repoPath,
|
|
47
47
|
cancellation: signal,
|
|
@@ -79,7 +79,7 @@ export class RemotesGitSubProvider extends RemotesGitProviderBase implements Git
|
|
|
79
79
|
@gate()
|
|
80
80
|
@debug()
|
|
81
81
|
async addRemote(repoPath: string, name: string, url: string, options?: { fetch?: boolean }): Promise<void> {
|
|
82
|
-
await this.git.
|
|
82
|
+
await this.git.run({ cwd: repoPath }, 'remote', 'add', options?.fetch ? '-f' : undefined, name, url);
|
|
83
83
|
this.context.hooks?.cache?.onReset?.(repoPath, 'remotes');
|
|
84
84
|
this.context.hooks?.repository?.onChanged?.(repoPath, ['remotes']);
|
|
85
85
|
}
|
|
@@ -100,7 +100,7 @@ export class RemotesGitSubProvider extends RemotesGitProviderBase implements Git
|
|
|
100
100
|
@gate()
|
|
101
101
|
@debug()
|
|
102
102
|
async pruneRemote(repoPath: string, name: string): Promise<void> {
|
|
103
|
-
await this.git.
|
|
103
|
+
await this.git.run({ cwd: repoPath }, 'remote', 'prune', name);
|
|
104
104
|
this.context.hooks?.cache?.onReset?.(repoPath, 'remotes', 'branches');
|
|
105
105
|
this.context.hooks?.repository?.onChanged?.(repoPath, ['remotes']);
|
|
106
106
|
}
|
|
@@ -108,7 +108,7 @@ export class RemotesGitSubProvider extends RemotesGitProviderBase implements Git
|
|
|
108
108
|
@gate()
|
|
109
109
|
@debug()
|
|
110
110
|
async removeRemote(repoPath: string, name: string): Promise<void> {
|
|
111
|
-
await this.git.
|
|
111
|
+
await this.git.run({ cwd: repoPath }, 'remote', 'remove', name);
|
|
112
112
|
this.context.hooks?.cache?.onReset?.(repoPath, 'remotes');
|
|
113
113
|
this.context.hooks?.repository?.onChanged?.(repoPath, ['remotes']);
|
|
114
114
|
}
|
|
@@ -36,16 +36,20 @@ export class RevisionGitSubProvider implements GitRevisionSubProvider {
|
|
|
36
36
|
private readonly provider: CliGitProviderInternal,
|
|
37
37
|
) {}
|
|
38
38
|
|
|
39
|
-
exists(
|
|
39
|
+
exists(
|
|
40
|
+
repoPath: string,
|
|
41
|
+
path: string,
|
|
42
|
+
revOrOptions?: string | { untracked?: 'only' | 'include' },
|
|
43
|
+
): Promise<boolean> {
|
|
40
44
|
let rev: string | undefined;
|
|
41
|
-
let untracked:
|
|
45
|
+
let untracked: 'only' | 'include' | undefined;
|
|
42
46
|
if (typeof revOrOptions === 'string') {
|
|
43
47
|
rev = revOrOptions;
|
|
44
48
|
} else if (revOrOptions != null) {
|
|
45
49
|
untracked = revOrOptions.untracked;
|
|
46
50
|
}
|
|
47
51
|
|
|
48
|
-
const cacheKey = `${path}\0${rev ?? ''}\0${untracked
|
|
52
|
+
const cacheKey = `${path}\0${rev ?? ''}\0${untracked ?? ''}`;
|
|
49
53
|
return this.cache.fileExistence.getOrCreate(repoPath, cacheKey, async () => {
|
|
50
54
|
const args = ['ls-files'];
|
|
51
55
|
if (rev) {
|
|
@@ -54,11 +58,14 @@ export class RevisionGitSubProvider implements GitRevisionSubProvider {
|
|
|
54
58
|
} else if (isUncommittedStaged(rev)) {
|
|
55
59
|
args.push('--stage');
|
|
56
60
|
}
|
|
57
|
-
} else if (untracked) {
|
|
61
|
+
} else if (untracked === 'only') {
|
|
58
62
|
args.push('-o');
|
|
63
|
+
} else if (untracked === 'include') {
|
|
64
|
+
// Tracked (cached) OR untracked (others)
|
|
65
|
+
args.push('-c', '-o');
|
|
59
66
|
}
|
|
60
67
|
|
|
61
|
-
const result = await this.git.
|
|
68
|
+
const result = await this.git.run({ cwd: repoPath, errors: 'ignore' }, ...args, '--', path);
|
|
62
69
|
return Boolean(result.stdout.trim());
|
|
63
70
|
});
|
|
64
71
|
}
|
|
@@ -79,7 +86,7 @@ export class RevisionGitSubProvider implements GitRevisionSubProvider {
|
|
|
79
86
|
const [relativePath, root] = splitPath(submodulePath, repoPath);
|
|
80
87
|
const submoduleFullPath = this.provider.getAbsoluteUri(relativePath, root).fsPath;
|
|
81
88
|
|
|
82
|
-
const result = await this.git.
|
|
89
|
+
const result = await this.git.run({ cwd: submoduleFullPath, errors: 'ignore' }, 'rev-parse', 'HEAD');
|
|
83
90
|
const sha = result.stdout.trim();
|
|
84
91
|
return sha || undefined;
|
|
85
92
|
}
|
|
@@ -92,7 +99,7 @@ export class RevisionGitSubProvider implements GitRevisionSubProvider {
|
|
|
92
99
|
const [relativePath, root] = splitPath(path, repoPath);
|
|
93
100
|
|
|
94
101
|
if (isUncommittedStaged(rev)) {
|
|
95
|
-
let result = await this.git.
|
|
102
|
+
let result = await this.git.run(
|
|
96
103
|
{ cwd: root, errors: 'ignore' },
|
|
97
104
|
'ls-files',
|
|
98
105
|
'-z',
|
|
@@ -105,7 +112,7 @@ export class RevisionGitSubProvider implements GitRevisionSubProvider {
|
|
|
105
112
|
if (entries.length === 0) return undefined;
|
|
106
113
|
|
|
107
114
|
const entry = entries[0];
|
|
108
|
-
result = await this.git.
|
|
115
|
+
result = await this.git.run({ cwd: root }, 'cat-file', '-s', entry.oid);
|
|
109
116
|
const size = parseInt(result.stdout.trim(), 10) || 0;
|
|
110
117
|
|
|
111
118
|
return { ref: rev, oid: entry.oid, path: relativePath, size: size, type: 'blob' };
|
|
@@ -119,7 +126,7 @@ export class RevisionGitSubProvider implements GitRevisionSubProvider {
|
|
|
119
126
|
async getTrackedFiles(repoPath: string): Promise<string[]> {
|
|
120
127
|
if (!repoPath) return [];
|
|
121
128
|
|
|
122
|
-
const result = await this.git.
|
|
129
|
+
const result = await this.git.run({ cwd: repoPath, errors: 'ignore' }, 'ls-files', '-z');
|
|
123
130
|
const data = result.stdout;
|
|
124
131
|
if (!data) return [];
|
|
125
132
|
|
|
@@ -135,7 +142,7 @@ export class RevisionGitSubProvider implements GitRevisionSubProvider {
|
|
|
135
142
|
private async getTreeForRevisionCore(repoPath: string, rev: string, path?: string): Promise<GitTreeEntry[]> {
|
|
136
143
|
const hasPath = Boolean(path);
|
|
137
144
|
const args = hasPath ? ['ls-tree', '-l', rev, '--', path] : ['ls-tree', '-lrt', rev, '--'];
|
|
138
|
-
const result = await this.git.
|
|
145
|
+
const result = await this.git.run({ cwd: repoPath, errors: 'ignore' }, ...args);
|
|
139
146
|
const data = result.stdout.trim();
|
|
140
147
|
if (!data) return emptyArray as GitTreeEntry[];
|
|
141
148
|
|
|
@@ -168,7 +175,7 @@ export class RevisionGitSubProvider implements GitRevisionSubProvider {
|
|
|
168
175
|
const params = ['show', '--textconv', args, '--'];
|
|
169
176
|
|
|
170
177
|
try {
|
|
171
|
-
const result = await this.git.
|
|
178
|
+
const result = await this.git.run<T>(opts, ...params);
|
|
172
179
|
return result.stdout;
|
|
173
180
|
} catch (ex) {
|
|
174
181
|
const msg: string = ex?.toString() ?? '';
|
|
@@ -261,7 +268,7 @@ export class RevisionGitSubProvider implements GitRevisionSubProvider {
|
|
|
261
268
|
const resolvedRevisionCaching = { cache: this.cache.gitResults, options: { accessTTL: 5 * 60 * 1000 } };
|
|
262
269
|
|
|
263
270
|
const parser = getShaAndFileSummaryLogParser();
|
|
264
|
-
let result = await this.git.
|
|
271
|
+
let result = await this.git.run(
|
|
265
272
|
{ cwd: repoPath, errors: 'ignore', caching: resolvedRevisionCaching },
|
|
266
273
|
'log',
|
|
267
274
|
...parser.arguments,
|
|
@@ -293,7 +300,7 @@ export class RevisionGitSubProvider implements GitRevisionSubProvider {
|
|
|
293
300
|
|
|
294
301
|
if (file.status === 'D') {
|
|
295
302
|
// If the file was deleted, check if it was moved or renamed
|
|
296
|
-
result = await this.git.
|
|
303
|
+
result = await this.git.run(
|
|
297
304
|
{ cwd: repoPath, errors: 'ignore', caching: resolvedRevisionCaching },
|
|
298
305
|
'log',
|
|
299
306
|
...parser.arguments,
|
|
@@ -64,7 +64,7 @@ export class StagingGitSubProvider implements GitStagingSubProvider {
|
|
|
64
64
|
if (ref == null) throw new Error(`ref is required when from is 'ref'`);
|
|
65
65
|
|
|
66
66
|
// Create the temp index file from a base ref/sha
|
|
67
|
-
const newIndexResult = await this.git.
|
|
67
|
+
const newIndexResult = await this.git.run(
|
|
68
68
|
{ cwd: repoPath, env: env },
|
|
69
69
|
'ls-tree',
|
|
70
70
|
'-z',
|
|
@@ -75,7 +75,7 @@ export class StagingGitSubProvider implements GitStagingSubProvider {
|
|
|
75
75
|
|
|
76
76
|
if (newIndexResult.stdout.trim()) {
|
|
77
77
|
// Write the tree to our temp index
|
|
78
|
-
await this.git.
|
|
78
|
+
await this.git.run(
|
|
79
79
|
{ cwd: repoPath, env: env, stdin: newIndexResult.stdout },
|
|
80
80
|
'update-index',
|
|
81
81
|
'-z',
|
|
@@ -98,7 +98,7 @@ export class StagingGitSubProvider implements GitStagingSubProvider {
|
|
|
98
98
|
|
|
99
99
|
@debug()
|
|
100
100
|
async stageFile(repoPath: string, pathOrUri: string | Uri): Promise<void> {
|
|
101
|
-
await this.git.
|
|
101
|
+
await this.git.run({ cwd: repoPath }, 'add', '-A', '--', toFsPath(pathOrUri));
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
@debug()
|
|
@@ -117,7 +117,7 @@ export class StagingGitSubProvider implements GitStagingSubProvider {
|
|
|
117
117
|
// Process files in batches (will be a single batch if under the limit)
|
|
118
118
|
const batches = chunk(paths, batchSize);
|
|
119
119
|
for (const batch of batches) {
|
|
120
|
-
await this.git.
|
|
120
|
+
await this.git.run(
|
|
121
121
|
{ cwd: repoPath, env: options?.index?.env },
|
|
122
122
|
'add',
|
|
123
123
|
options?.intentToAdd ? '-N' : '-A',
|
|
@@ -129,12 +129,12 @@ export class StagingGitSubProvider implements GitStagingSubProvider {
|
|
|
129
129
|
|
|
130
130
|
@debug()
|
|
131
131
|
async stageDirectory(repoPath: string, directoryOrUri: string | Uri): Promise<void> {
|
|
132
|
-
await this.git.
|
|
132
|
+
await this.git.run({ cwd: repoPath }, 'add', '-A', '--', toFsPath(directoryOrUri));
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
@debug()
|
|
136
136
|
async unstageFile(repoPath: string, pathOrUri: string | Uri): Promise<void> {
|
|
137
|
-
await this.git.
|
|
137
|
+
await this.git.run({ cwd: repoPath }, 'reset', '-q', '--', toFsPath(pathOrUri));
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
@debug()
|
|
@@ -149,12 +149,22 @@ export class StagingGitSubProvider implements GitStagingSubProvider {
|
|
|
149
149
|
// Process files in batches (will be a single batch if under the limit)
|
|
150
150
|
const batches = chunk(paths, batchSize);
|
|
151
151
|
for (const batch of batches) {
|
|
152
|
-
await this.git.
|
|
152
|
+
await this.git.run({ cwd: repoPath }, 'reset', '-q', '--', ...batch);
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
@debug()
|
|
157
157
|
async unstageDirectory(repoPath: string, directoryOrUri: string | Uri): Promise<void> {
|
|
158
|
-
await this.git.
|
|
158
|
+
await this.git.run({ cwd: repoPath }, 'reset', '-q', '--', toFsPath(directoryOrUri));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
@debug()
|
|
162
|
+
async stageAll(repoPath: string): Promise<void> {
|
|
163
|
+
await this.git.run({ cwd: repoPath }, 'add', '-A');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
@debug()
|
|
167
|
+
async unstageAll(repoPath: string): Promise<void> {
|
|
168
|
+
await this.git.run({ cwd: repoPath }, 'reset', '-q');
|
|
159
169
|
}
|
|
160
170
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Cache } from '../../git/cache.js';
|
|
2
2
|
import type { GitServiceContext } from '../../git/context.js';
|
|
3
3
|
import { StashApplyError, StashPushError } from '../../git/errors.js';
|
|
4
|
-
import type { GitStashCommit
|
|
4
|
+
import type { GitStashCommit } from '../../git/models/commit.js';
|
|
5
5
|
import { GitCommit, GitCommitIdentity } from '../../git/models/commit.js';
|
|
6
6
|
import { GitFileChange } from '../../git/models/fileChange.js';
|
|
7
7
|
import type { GitFileStatus } from '../../git/models/fileStatus.js';
|
|
@@ -12,8 +12,7 @@ import type { GitStashSubProvider, StashApplyResult } from '../../git/providers/
|
|
|
12
12
|
import { countStringLength } from '../../utils/array.js';
|
|
13
13
|
import { gate } from '../../utils/decorators/gate.js';
|
|
14
14
|
import { debug } from '../../utils/decorators/log.js';
|
|
15
|
-
import {
|
|
16
|
-
import { Logger } from '../../utils/logger.js';
|
|
15
|
+
import { skip } from '../../utils/iterable.js';
|
|
17
16
|
import { normalizePath, splitPath } from '../../utils/path.js';
|
|
18
17
|
import { getSettledValue } from '../../utils/promise.js';
|
|
19
18
|
import type { Uri } from '../../utils/uri.js';
|
|
@@ -23,7 +22,7 @@ import { gitFeaturesByVersion } from '../exec/features.js';
|
|
|
23
22
|
import type { Git } from '../exec/git.js';
|
|
24
23
|
import { getGitCommandError, GitError, GitErrors, maxGitCliLength } from '../exec/git.js';
|
|
25
24
|
import type { ParsedStash, ParsedStashWithFiles } from '../parsers/logParser.js';
|
|
26
|
-
import {
|
|
25
|
+
import { getStashFilesOnlyLogParser, getStashLogParser } from '../parsers/logParser.js';
|
|
27
26
|
import { createCommitFileset } from './commitFilesetUtils.js';
|
|
28
27
|
|
|
29
28
|
export class StashGitSubProvider implements GitStashSubProvider {
|
|
@@ -57,7 +56,7 @@ export class StashGitSubProvider implements GitStashSubProvider {
|
|
|
57
56
|
conflictCommand: 'stash-apply' | 'stash-pop',
|
|
58
57
|
): Promise<StashApplyResult> {
|
|
59
58
|
try {
|
|
60
|
-
await this.git.
|
|
59
|
+
await this.git.run({ cwd: repoPath }, ...args);
|
|
61
60
|
this.context.hooks?.cache?.onReset?.(repoPath, 'stashes');
|
|
62
61
|
this.context.hooks?.repository?.onChanged?.(repoPath, ['stash']);
|
|
63
62
|
return { conflicted: false };
|
|
@@ -94,7 +93,7 @@ export class StashGitSubProvider implements GitStashSubProvider {
|
|
|
94
93
|
args.push(message);
|
|
95
94
|
}
|
|
96
95
|
|
|
97
|
-
const result = await this.git.
|
|
96
|
+
const result = await this.git.run({ cwd: repoPath }, ...args);
|
|
98
97
|
return result.stdout.trim() || undefined;
|
|
99
98
|
}
|
|
100
99
|
|
|
@@ -110,135 +109,81 @@ export class StashGitSubProvider implements GitStashSubProvider {
|
|
|
110
109
|
): Promise<GitStash | undefined> {
|
|
111
110
|
if (repoPath == null) return undefined;
|
|
112
111
|
|
|
112
|
+
// Resolve options against config up front so the cache key reflects the actual values used
|
|
113
|
+
// to produce the cached payload — undefined inputs aren't conflated across config changes.
|
|
113
114
|
const cfg = this.context.config;
|
|
115
|
+
const includeFiles = options?.includeFiles ?? cfg?.commits.includeFileDetails ?? true;
|
|
116
|
+
const similarityThreshold = includeFiles
|
|
117
|
+
? (options?.similarityThreshold ?? cfg?.commits.similarityThreshold ?? null)
|
|
118
|
+
: null;
|
|
119
|
+
const reachableFrom = options?.reachableFrom;
|
|
120
|
+
|
|
121
|
+
// Cache only the unfiltered stash list; reachability projection runs below by testing each
|
|
122
|
+
// distinct stash parent against `reachableFrom` directly, so re-projecting is cheap
|
|
123
|
+
const cacheKey = `f${includeFiles ? 1 : 0}|s${similarityThreshold ?? ''}`;
|
|
124
|
+
|
|
114
125
|
const stash = await this.cache.getStash(
|
|
115
126
|
repoPath,
|
|
127
|
+
cacheKey,
|
|
116
128
|
async (commonPath, _cacheable, signal) => {
|
|
117
129
|
// Prefer the aggregate signal from the cache; fall back to the caller's cancellation.
|
|
118
130
|
signal ??= cancellation;
|
|
119
|
-
const includeFiles = options?.includeFiles ?? cfg?.commits.includeFileDetails ?? true;
|
|
120
131
|
const parser = getStashLogParser(includeFiles);
|
|
121
132
|
const args = [...parser.arguments];
|
|
122
133
|
|
|
123
134
|
if (includeFiles) {
|
|
124
|
-
const similarityThreshold = options?.similarityThreshold ?? cfg?.commits.similarityThreshold;
|
|
125
135
|
args.push(`-M${similarityThreshold == null ? '' : `${similarityThreshold}%`}`);
|
|
126
136
|
}
|
|
127
137
|
|
|
128
|
-
const result = await this.git.
|
|
138
|
+
const result = await this.git.run({ cwd: repoPath, cancellation: signal }, 'stash', 'list', ...args);
|
|
129
139
|
|
|
130
140
|
const currentUser = await this.provider.config.getCurrentUser(repoPath);
|
|
131
141
|
|
|
132
142
|
const stashes = new Map<string, GitStashCommit>();
|
|
133
|
-
const parentShas = new Set<string>();
|
|
134
|
-
|
|
135
|
-
// First pass: create stashes and collect parent SHAs
|
|
136
143
|
for (const s of parser.parse(result.stdout)) {
|
|
137
144
|
stashes.set(s.sha, createStash(s, commonPath, currentUser));
|
|
138
|
-
// Collect all parent SHAs for timestamp lookup
|
|
139
|
-
if (s.parents) {
|
|
140
|
-
for (const parentSha of s.parents.split(' ')) {
|
|
141
|
-
if (parentSha.trim()) {
|
|
142
|
-
parentShas.add(parentSha.trim());
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Second pass: fetch parent timestamps if we have any parents
|
|
149
|
-
const parentTimestamps = new Map<string, { authorDate: number; committerDate: number }>();
|
|
150
|
-
if (parentShas.size > 0) {
|
|
151
|
-
try {
|
|
152
|
-
const datesParser = getShaAndDatesLogParser();
|
|
153
|
-
const parentResult = await this.git.exec(
|
|
154
|
-
{
|
|
155
|
-
cwd: repoPath,
|
|
156
|
-
cancellation: signal,
|
|
157
|
-
stdin: [...parentShas].join('\n'),
|
|
158
|
-
},
|
|
159
|
-
'log',
|
|
160
|
-
...datesParser.arguments,
|
|
161
|
-
'--no-walk',
|
|
162
|
-
'--stdin',
|
|
163
|
-
);
|
|
164
|
-
|
|
165
|
-
for (const entry of datesParser.parse(parentResult.stdout)) {
|
|
166
|
-
parentTimestamps.set(entry.sha, {
|
|
167
|
-
authorDate: Number(entry.authorDate),
|
|
168
|
-
committerDate: Number(entry.committerDate),
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
} catch (ex) {
|
|
172
|
-
Logger.debug(`Failed to fetch parent timestamps for stash commits: ${ex}`);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Third pass: update stashes with parent timestamp information
|
|
177
|
-
for (const sha of stashes.keys()) {
|
|
178
|
-
const stashCommit = stashes.get(sha);
|
|
179
|
-
if (stashCommit?.parents.length) {
|
|
180
|
-
const parentsWithTimestamps: GitStashParentInfo[] = stashCommit.parents.map(parentSha => ({
|
|
181
|
-
sha: parentSha,
|
|
182
|
-
authorDate: parentTimestamps.get(parentSha)?.authorDate,
|
|
183
|
-
committerDate: parentTimestamps.get(parentSha)?.committerDate,
|
|
184
|
-
}));
|
|
185
|
-
// Store the parent timestamp information on the stash
|
|
186
|
-
stashes.set(sha, stashCommit.with({ parentTimestamps: parentsWithTimestamps }));
|
|
187
|
-
}
|
|
188
145
|
}
|
|
189
146
|
|
|
190
147
|
return { repoPath: commonPath, stashes: stashes };
|
|
191
148
|
},
|
|
192
|
-
cancellation,
|
|
149
|
+
{ cancellation: cancellation },
|
|
193
150
|
);
|
|
194
151
|
|
|
195
|
-
if (stash == null) return
|
|
196
|
-
if (!options?.reachableFrom || !stash?.stashes.size) return stash;
|
|
152
|
+
if (stash == null || !reachableFrom || !stash.stashes.size) return stash;
|
|
197
153
|
|
|
198
|
-
//
|
|
199
|
-
// Create a copy because we are going to modify it and we don't want to mutate the cache
|
|
154
|
+
// Copy because the cached `stash.stashes` map is shared and must not be mutated.
|
|
200
155
|
const stashes = new Map(stash.stashes);
|
|
201
156
|
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
const { reachableFrom } = options;
|
|
205
|
-
const reachableShas = await this.provider.commits.getLogShas(repoPath, reachableFrom, {
|
|
206
|
-
since: oldestStashDate,
|
|
207
|
-
ordering: 'date',
|
|
208
|
-
limit: 0,
|
|
209
|
-
});
|
|
210
|
-
const reachableCommits = new Set(reachableShas);
|
|
157
|
+
const reachableStashes = new Set<string>();
|
|
211
158
|
|
|
212
|
-
|
|
213
|
-
|
|
159
|
+
// Metadata pass: a stash belongs on `reachableFrom` iff git's auto-prepended
|
|
160
|
+
// `On <branch>: …` (or `WIP on <branch>: …`) prefix in the stash commit subject names
|
|
161
|
+
// this branch. `stashOnRef` is parsed from %s in createStash, so this covers autostashes
|
|
162
|
+
// and custom-reflog stashes too.
|
|
163
|
+
for (const [sha, s] of stash.stashes) {
|
|
164
|
+
if (s.stashOnRef === reachableFrom) {
|
|
165
|
+
reachableStashes.add(sha);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
214
168
|
|
|
215
|
-
|
|
169
|
+
// Transitive pass for the rare detached-HEAD-on-stash chain (stash B created while HEAD
|
|
170
|
+
// pointed at stash A). Such stashes have no `stashOnRef`, but if their parent is a stash
|
|
171
|
+
// already marked reachable they should follow.
|
|
172
|
+
let changed;
|
|
173
|
+
do {
|
|
174
|
+
changed = false;
|
|
216
175
|
for (const [sha, s] of stash.stashes) {
|
|
217
|
-
if (s.parents.some(p =>
|
|
176
|
+
if (!reachableStashes.has(sha) && s.parents.some(p => reachableStashes.has(p))) {
|
|
218
177
|
reachableStashes.add(sha);
|
|
178
|
+
changed = true;
|
|
219
179
|
}
|
|
220
180
|
}
|
|
181
|
+
} while (changed);
|
|
221
182
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
changed = false;
|
|
226
|
-
for (const [sha, s] of stash.stashes) {
|
|
227
|
-
if (!reachableStashes.has(sha) && s.parents.some(p => reachableStashes.has(p))) {
|
|
228
|
-
reachableStashes.add(sha);
|
|
229
|
-
changed = true;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
} while (changed);
|
|
233
|
-
|
|
234
|
-
// Remove unreachable stashes
|
|
235
|
-
for (const [sha] of stash.stashes) {
|
|
236
|
-
if (!reachableStashes.has(sha)) {
|
|
237
|
-
stashes.delete(sha);
|
|
238
|
-
}
|
|
183
|
+
for (const [sha] of stash.stashes) {
|
|
184
|
+
if (!reachableStashes.has(sha)) {
|
|
185
|
+
stashes.delete(sha);
|
|
239
186
|
}
|
|
240
|
-
} else {
|
|
241
|
-
stashes.clear();
|
|
242
187
|
}
|
|
243
188
|
|
|
244
189
|
return { ...stash, stashes: stashes };
|
|
@@ -288,7 +233,7 @@ export class StashGitSubProvider implements GitStashSubProvider {
|
|
|
288
233
|
}
|
|
289
234
|
|
|
290
235
|
const parser = getStashFilesOnlyLogParser();
|
|
291
|
-
const result = await this.git.
|
|
236
|
+
const result = await this.git.run(
|
|
292
237
|
{ cwd: repoPath, cancellation: cancellation },
|
|
293
238
|
'stash',
|
|
294
239
|
'show',
|
|
@@ -343,7 +288,7 @@ export class StashGitSubProvider implements GitStashSubProvider {
|
|
|
343
288
|
|
|
344
289
|
let result;
|
|
345
290
|
if (sha) {
|
|
346
|
-
result = await this.git.
|
|
291
|
+
result = await this.git.run(
|
|
347
292
|
{ cwd: repoPath, errors: 'ignore' },
|
|
348
293
|
'show',
|
|
349
294
|
'--format=%H',
|
|
@@ -355,7 +300,7 @@ export class StashGitSubProvider implements GitStashSubProvider {
|
|
|
355
300
|
}
|
|
356
301
|
}
|
|
357
302
|
|
|
358
|
-
result = await this.git.
|
|
303
|
+
result = await this.git.run({ cwd: repoPath }, 'stash', 'drop', stashName);
|
|
359
304
|
return result.stdout;
|
|
360
305
|
}
|
|
361
306
|
|
|
@@ -368,7 +313,7 @@ export class StashGitSubProvider implements GitStashSubProvider {
|
|
|
368
313
|
stashOnRef?: string,
|
|
369
314
|
): Promise<void> {
|
|
370
315
|
await this.deleteStashCore(repoPath, stashName, sha);
|
|
371
|
-
await this.git.
|
|
316
|
+
await this.git.run(
|
|
372
317
|
{ cwd: repoPath },
|
|
373
318
|
'stash',
|
|
374
319
|
'store',
|
|
@@ -478,7 +423,7 @@ export class StashGitSubProvider implements GitStashSubProvider {
|
|
|
478
423
|
}
|
|
479
424
|
|
|
480
425
|
try {
|
|
481
|
-
const result = await this.git.
|
|
426
|
+
const result = await this.git.run({ cwd: repoPath, stdin: stdin }, ...params);
|
|
482
427
|
if (GitErrors.stashNothingToSave.test(result.stdout)) {
|
|
483
428
|
throw new StashPushError({
|
|
484
429
|
reason: 'nothingToSave',
|
|
@@ -502,7 +447,7 @@ export class StashGitSubProvider implements GitStashSubProvider {
|
|
|
502
447
|
|
|
503
448
|
@debug()
|
|
504
449
|
async saveSnapshot(repoPath: string, message?: string): Promise<void> {
|
|
505
|
-
const result = await this.git.
|
|
450
|
+
const result = await this.git.run({ cwd: repoPath }, 'stash', 'create');
|
|
506
451
|
const id = result.stdout.trim() || undefined;
|
|
507
452
|
if (id == null) return;
|
|
508
453
|
|
|
@@ -510,7 +455,7 @@ export class StashGitSubProvider implements GitStashSubProvider {
|
|
|
510
455
|
if (message) {
|
|
511
456
|
args.push('-m', message);
|
|
512
457
|
}
|
|
513
|
-
await this.git.
|
|
458
|
+
await this.git.run({ cwd: repoPath }, 'stash', 'store', ...args, id);
|
|
514
459
|
|
|
515
460
|
this.context.hooks?.repository?.onChanged?.(repoPath, ['stash']);
|
|
516
461
|
this.context.hooks?.cache?.onReset?.(repoPath, 'stashes');
|
|
@@ -557,18 +502,25 @@ function createStash(
|
|
|
557
502
|
repoPath: string,
|
|
558
503
|
currentUser: GitUser | undefined,
|
|
559
504
|
): GitStashCommit {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
let
|
|
564
|
-
const
|
|
565
|
-
if (
|
|
566
|
-
onRef =
|
|
567
|
-
|
|
505
|
+
// `onRef` comes from the commit subject (%s), which git always prefixes with `On <branch>: …`
|
|
506
|
+
// or `WIP on <branch>: …`. This holds even when the reflog message was customized
|
|
507
|
+
// (autostash, `git stash store -m "…"`), so it's a reliable source
|
|
508
|
+
let onRef: string | undefined;
|
|
509
|
+
const subjectMatch = stashSummaryRegex.exec((s.commitSubject ?? '').trim());
|
|
510
|
+
if (subjectMatch?.groups?.onref) {
|
|
511
|
+
onRef = subjectMatch.groups.onref;
|
|
512
|
+
}
|
|
568
513
|
|
|
514
|
+
// The user-visible display message is derived from the reflog subject (%gs) so that short
|
|
515
|
+
// custom messages like "wip", "q", or "show args" stay as the user wrote them instead of
|
|
516
|
+
// being replaced by the verbose git-auto subject
|
|
517
|
+
let message = s.summary.trim();
|
|
518
|
+
const reflogMatch = stashSummaryRegex.exec(message);
|
|
519
|
+
if (reflogMatch?.groups != null) {
|
|
520
|
+
const summary = reflogMatch.groups.summary.trim();
|
|
569
521
|
if (summary.length === 0) {
|
|
570
522
|
message = 'WIP';
|
|
571
|
-
} else if (
|
|
523
|
+
} else if (reflogMatch.groups.wip) {
|
|
572
524
|
message = `WIP: ${summary}`;
|
|
573
525
|
} else {
|
|
574
526
|
message = summary;
|
|
@@ -605,22 +557,3 @@ function createStash(
|
|
|
605
557
|
onRef,
|
|
606
558
|
) as GitStashCommit;
|
|
607
559
|
}
|
|
608
|
-
|
|
609
|
-
/**
|
|
610
|
-
* Finds the oldest timestamp among stash commits and their parent commits.
|
|
611
|
-
* This includes both the stash commit dates and all parent commit timestamps (author and committer dates).
|
|
612
|
-
*
|
|
613
|
-
* @param stashes - Collection of stash commits to analyze
|
|
614
|
-
* @returns The oldest timestamp in milliseconds, or Infinity if no stashes provided
|
|
615
|
-
*/
|
|
616
|
-
export function findOldestStashTimestamp(stashes: Iterable<GitStashCommit>): number {
|
|
617
|
-
return min(stashes, c => {
|
|
618
|
-
return Math.min(
|
|
619
|
-
c.committer.date.getTime(),
|
|
620
|
-
...(c.parentTimestamps
|
|
621
|
-
?.flatMap(p => [p.authorDate, p.committerDate])
|
|
622
|
-
.filter((x): x is number => x != null)
|
|
623
|
-
.map(x => x * 1000) ?? []),
|
|
624
|
-
);
|
|
625
|
-
});
|
|
626
|
-
}
|