@gitkraken/core-gitlens 0.5.113 → 0.5.115
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 +42 -1
- package/dist/git/cache.d.ts +4 -0
- package/dist/git/cache.d.ts.map +1 -1
- package/dist/git/cache.js +33 -0
- package/dist/git/cache.js.map +1 -1
- package/dist/git/context.d.ts +10 -0
- package/dist/git/context.d.ts.map +1 -1
- package/dist/git/context.js.map +1 -1
- package/dist/git/errors.d.ts +6 -3
- package/dist/git/errors.d.ts.map +1 -1
- package/dist/git/errors.js +9 -1
- package/dist/git/errors.js.map +1 -1
- package/dist/git/features.d.ts +1 -1
- package/dist/git/features.d.ts.map +1 -1
- package/dist/git/features.js +38 -0
- package/dist/git/features.js.map +1 -1
- package/dist/git/gitHealth.d.ts +153 -0
- package/dist/git/gitHealth.d.ts.map +1 -0
- package/dist/git/gitHealth.js +353 -0
- package/dist/git/gitHealth.js.map +1 -0
- package/dist/git/models/search.d.ts +6 -3
- package/dist/git/models/search.d.ts.map +1 -1
- package/dist/git/models/search.js +5 -1
- package/dist/git/models/search.js.map +1 -1
- package/dist/git/parsers/diffParser.d.ts +8 -2
- package/dist/git/parsers/diffParser.d.ts.map +1 -1
- package/dist/git/parsers/diffParser.js +27 -4
- package/dist/git/parsers/diffParser.js.map +1 -1
- package/dist/git/providers/config.d.ts +36 -3
- package/dist/git/providers/config.d.ts.map +1 -1
- package/dist/git/providers/graph.d.ts +9 -0
- package/dist/git/providers/graph.d.ts.map +1 -1
- package/dist/git/providers/maintenance.d.ts +222 -0
- package/dist/git/providers/maintenance.d.ts.map +1 -0
- package/dist/git/providers/maintenance.js +20 -0
- package/dist/git/providers/maintenance.js.map +1 -0
- package/dist/git/providers/operations.d.ts +10 -0
- package/dist/git/providers/operations.d.ts.map +1 -1
- package/dist/git/providers/pausedOperations.d.ts +3 -0
- package/dist/git/providers/pausedOperations.d.ts.map +1 -1
- package/dist/git/providers/provider.d.ts +2 -0
- package/dist/git/providers/provider.d.ts.map +1 -1
- package/dist/git/providers/tags.d.ts +3 -0
- package/dist/git/providers/tags.d.ts.map +1 -1
- package/dist/git/repositoryService.d.ts +1 -0
- package/dist/git/repositoryService.d.ts.map +1 -1
- package/dist/git/repositoryService.js.map +1 -1
- package/dist/git/run.types.d.ts +8 -0
- package/dist/git/run.types.d.ts.map +1 -1
- package/dist/git/service.js +3 -0
- package/dist/git/service.js.map +1 -1
- package/dist/git/utils/search.utils.d.ts.map +1 -1
- package/dist/git/utils/search.utils.js +112 -5
- package/dist/git/utils/search.utils.js.map +1 -1
- package/dist/git/watching/watchService.d.ts +3 -0
- package/dist/git/watching/watchService.d.ts.map +1 -1
- package/dist/git/watching/watchService.js +8 -0
- package/dist/git/watching/watchService.js.map +1 -1
- package/dist/git-cli/cliGitProvider.d.ts +11 -0
- package/dist/git-cli/cliGitProvider.d.ts.map +1 -1
- package/dist/git-cli/cliGitProvider.js +12 -0
- package/dist/git-cli/cliGitProvider.js.map +1 -1
- package/dist/git-cli/exec/git.d.ts +23 -1
- package/dist/git-cli/exec/git.d.ts.map +1 -1
- package/dist/git-cli/exec/git.js +83 -8
- package/dist/git-cli/exec/git.js.map +1 -1
- package/dist/git-cli/exec/gitQueue.d.ts +2 -0
- package/dist/git-cli/exec/gitQueue.d.ts.map +1 -1
- package/dist/git-cli/exec/gitQueue.js +19 -17
- package/dist/git-cli/exec/gitQueue.js.map +1 -1
- package/dist/git-cli/parsers/logParser.d.ts.map +1 -1
- package/dist/git-cli/parsers/logParser.js +4 -1
- package/dist/git-cli/parsers/logParser.js.map +1 -1
- package/dist/git-cli/providers/blame.d.ts.map +1 -1
- package/dist/git-cli/providers/blame.js +4 -7
- package/dist/git-cli/providers/blame.js.map +1 -1
- package/dist/git-cli/providers/branches.d.ts.map +1 -1
- package/dist/git-cli/providers/branches.js +13 -3
- package/dist/git-cli/providers/branches.js.map +1 -1
- package/dist/git-cli/providers/commits.d.ts.map +1 -1
- package/dist/git-cli/providers/commits.js +34 -3
- package/dist/git-cli/providers/commits.js.map +1 -1
- package/dist/git-cli/providers/config.d.ts +27 -0
- package/dist/git-cli/providers/config.d.ts.map +1 -1
- package/dist/git-cli/providers/config.js +31 -6
- package/dist/git-cli/providers/config.js.map +1 -1
- package/dist/git-cli/providers/graph.d.ts +8 -0
- package/dist/git-cli/providers/graph.d.ts.map +1 -1
- package/dist/git-cli/providers/graph.js +85 -6
- package/dist/git-cli/providers/graph.js.map +1 -1
- package/dist/git-cli/providers/maintenance.d.ts +306 -0
- package/dist/git-cli/providers/maintenance.d.ts.map +1 -0
- package/dist/git-cli/providers/maintenance.js +2129 -0
- package/dist/git-cli/providers/maintenance.js.map +1 -0
- package/dist/git-cli/providers/operations.d.ts +1 -0
- package/dist/git-cli/providers/operations.d.ts.map +1 -1
- package/dist/git-cli/providers/operations.js +8 -0
- package/dist/git-cli/providers/operations.js.map +1 -1
- package/dist/git-cli/providers/pausedOperations.d.ts +1 -0
- package/dist/git-cli/providers/pausedOperations.d.ts.map +1 -1
- package/dist/git-cli/providers/pausedOperations.js +1 -1
- package/dist/git-cli/providers/pausedOperations.js.map +1 -1
- package/dist/git-cli/providers/tags.d.ts +3 -0
- package/dist/git-cli/providers/tags.d.ts.map +1 -1
- package/dist/git-cli/providers/tags.js +23 -1
- package/dist/git-cli/providers/tags.js.map +1 -1
- package/dist/plus/agents/agentCapabilities.d.ts +77 -0
- package/dist/plus/agents/agentCapabilities.d.ts.map +1 -0
- package/dist/plus/agents/agentCapabilities.js +197 -0
- package/dist/plus/agents/agentCapabilities.js.map +1 -0
- package/dist/plus/agents/providers/claudeCodeTranscript.d.ts +77 -13
- package/dist/plus/agents/providers/claudeCodeTranscript.d.ts.map +1 -1
- package/dist/plus/agents/providers/claudeCodeTranscript.js +263 -55
- package/dist/plus/agents/providers/claudeCodeTranscript.js.map +1 -1
- package/dist/plus/agents/providers/{claudeCodeProvider.d.ts → gkAgentProvider.d.ts} +52 -19
- package/dist/plus/agents/providers/gkAgentProvider.d.ts.map +1 -0
- package/dist/plus/agents/providers/{claudeCodeProvider.js → gkAgentProvider.js} +935 -262
- package/dist/plus/agents/providers/gkAgentProvider.js.map +1 -0
- package/dist/plus/agents/types.d.ts +113 -35
- package/dist/plus/agents/types.d.ts.map +1 -1
- package/dist/plus/agents/types.js +7 -4
- package/dist/plus/agents/types.js.map +1 -1
- package/dist/plus/ai/models/promptTemplates.d.ts +1 -1
- package/dist/plus/ai/models/promptTemplates.d.ts.map +1 -1
- package/dist/plus/ai/prompts.d.ts +4 -0
- package/dist/plus/ai/prompts.d.ts.map +1 -1
- package/dist/plus/ai/prompts.js +59 -4
- package/dist/plus/ai/prompts.js.map +1 -1
- package/dist/plus/git-github/api/github.d.ts +44 -1
- package/dist/plus/git-github/api/github.d.ts.map +1 -1
- package/dist/plus/git-github/api/github.js +355 -62
- package/dist/plus/git-github/api/github.js.map +1 -1
- package/dist/plus/git-github/api/issueSearchQuery.d.ts +13 -0
- package/dist/plus/git-github/api/issueSearchQuery.d.ts.map +1 -1
- package/dist/plus/git-github/api/issueSearchQuery.js +41 -0
- package/dist/plus/git-github/api/issueSearchQuery.js.map +1 -1
- package/dist/plus/git-github/providers/github/blame.d.ts.map +1 -1
- package/dist/plus/git-github/providers/github/blame.js +2 -1
- package/dist/plus/git-github/providers/github/blame.js.map +1 -1
- package/dist/plus/integrations/collectionMetadata.d.ts +2 -0
- package/dist/plus/integrations/collectionMetadata.d.ts.map +1 -1
- package/dist/plus/integrations/collectionMetadata.js +17 -1
- package/dist/plus/integrations/collectionMetadata.js.map +1 -1
- package/dist/plus/integrations/context.d.ts +4 -1
- package/dist/plus/integrations/context.d.ts.map +1 -1
- package/dist/plus/integrations/index.d.ts +1 -0
- package/dist/plus/integrations/index.d.ts.map +1 -1
- package/dist/plus/integrations/integrationService.d.ts +20 -1
- package/dist/plus/integrations/integrationService.d.ts.map +1 -1
- package/dist/plus/integrations/integrationService.js +12 -4
- package/dist/plus/integrations/integrationService.js.map +1 -1
- package/dist/plus/integrations/manager.d.ts +48 -0
- package/dist/plus/integrations/manager.d.ts.map +1 -1
- package/dist/plus/integrations/models/gitHostIntegration.d.ts +53 -26
- package/dist/plus/integrations/models/gitHostIntegration.d.ts.map +1 -1
- package/dist/plus/integrations/models/gitHostIntegration.js +123 -47
- package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
- package/dist/plus/integrations/models/integration.d.ts +10 -1
- package/dist/plus/integrations/models/integration.d.ts.map +1 -1
- package/dist/plus/integrations/models/integration.js +52 -19
- package/dist/plus/integrations/models/integration.js.map +1 -1
- package/dist/plus/integrations/models/issueCache.d.ts +16 -0
- package/dist/plus/integrations/models/issueCache.d.ts.map +1 -0
- package/dist/plus/integrations/models/issueCache.js +9 -0
- package/dist/plus/integrations/models/issueCache.js.map +1 -0
- package/dist/plus/integrations/models/issueReads.d.ts +13 -0
- package/dist/plus/integrations/models/issueReads.d.ts.map +1 -1
- package/dist/plus/integrations/models/issuesIntegration.d.ts +11 -13
- package/dist/plus/integrations/models/issuesIntegration.d.ts.map +1 -1
- package/dist/plus/integrations/models/issuesIntegration.js +15 -1
- package/dist/plus/integrations/models/issuesIntegration.js.map +1 -1
- package/dist/plus/integrations/providers/azureDevOps.d.ts +3 -5
- package/dist/plus/integrations/providers/azureDevOps.d.ts.map +1 -1
- package/dist/plus/integrations/providers/azureDevOps.js +9 -8
- package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
- package/dist/plus/integrations/providers/bitbucket-server.d.ts +3 -5
- package/dist/plus/integrations/providers/bitbucket-server.d.ts.map +1 -1
- package/dist/plus/integrations/providers/bitbucket-server.js +2 -2
- package/dist/plus/integrations/providers/bitbucket-server.js.map +1 -1
- package/dist/plus/integrations/providers/bitbucket.d.ts +3 -5
- package/dist/plus/integrations/providers/bitbucket.d.ts.map +1 -1
- package/dist/plus/integrations/providers/bitbucket.js +2 -1
- package/dist/plus/integrations/providers/bitbucket.js.map +1 -1
- package/dist/plus/integrations/providers/github.d.ts +12 -5
- package/dist/plus/integrations/providers/github.d.ts.map +1 -1
- package/dist/plus/integrations/providers/github.js +10 -3
- package/dist/plus/integrations/providers/github.js.map +1 -1
- package/dist/plus/integrations/providers/gitlab.d.ts +3 -5
- package/dist/plus/integrations/providers/gitlab.d.ts.map +1 -1
- package/dist/plus/integrations/providers/gitlab.js +2 -2
- package/dist/plus/integrations/providers/gitlab.js.map +1 -1
- package/dist/plus/integrations/providers/jira.d.ts +3 -7
- package/dist/plus/integrations/providers/jira.d.ts.map +1 -1
- package/dist/plus/integrations/providers/jira.js +35 -22
- package/dist/plus/integrations/providers/jira.js.map +1 -1
- package/dist/plus/integrations/providers/jiraIssueByKey.d.ts +7 -0
- package/dist/plus/integrations/providers/jiraIssueByKey.d.ts.map +1 -0
- package/dist/plus/integrations/providers/jiraIssueByKey.js +102 -0
- package/dist/plus/integrations/providers/jiraIssueByKey.js.map +1 -0
- package/dist/plus/integrations/providers/linear.d.ts +3 -7
- package/dist/plus/integrations/providers/linear.d.ts.map +1 -1
- package/dist/plus/integrations/providers/linear.js +23 -22
- package/dist/plus/integrations/providers/linear.js.map +1 -1
- package/dist/plus/integrations/providers/models.d.ts +3 -1
- package/dist/plus/integrations/providers/models.d.ts.map +1 -1
- package/dist/plus/integrations/providers/models.js +10 -4
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/providers/providerErrors.d.ts +7 -0
- package/dist/plus/integrations/providers/providerErrors.d.ts.map +1 -0
- package/dist/plus/integrations/providers/providerErrors.js +91 -0
- package/dist/plus/integrations/providers/providerErrors.js.map +1 -0
- package/dist/plus/integrations/providers/providersApi.d.ts +3 -2
- package/dist/plus/integrations/providers/providersApi.d.ts.map +1 -1
- package/dist/plus/integrations/providers/providersApi.js +18 -52
- package/dist/plus/integrations/providers/providersApi.js.map +1 -1
- package/dist/plus/integrations/providers/trello.d.ts +2 -7
- package/dist/plus/integrations/providers/trello.d.ts.map +1 -1
- package/dist/plus/integrations/providers/trello.js +3 -1
- package/dist/plus/integrations/providers/trello.js.map +1 -1
- package/dist/plus/integrations/providers/utils/providerPaging.d.ts +2 -0
- package/dist/plus/integrations/providers/utils/providerPaging.d.ts.map +1 -1
- package/dist/plus/integrations/providers/utils/providerPaging.js +12 -7
- package/dist/plus/integrations/providers/utils/providerPaging.js.map +1 -1
- package/dist/plus/integrations/reads/broaden.d.ts +47 -10
- package/dist/plus/integrations/reads/broaden.d.ts.map +1 -1
- package/dist/plus/integrations/reads/broaden.js +144 -33
- package/dist/plus/integrations/reads/broaden.js.map +1 -1
- package/dist/plus/integrations/reads/filters.d.ts +18 -0
- package/dist/plus/integrations/reads/filters.d.ts.map +1 -1
- package/dist/plus/integrations/reads/filters.js +37 -1
- package/dist/plus/integrations/reads/filters.js.map +1 -1
- package/dist/plus/integrations/reads/issueBatch.d.ts +51 -0
- package/dist/plus/integrations/reads/issueBatch.d.ts.map +1 -0
- package/dist/plus/integrations/reads/issueBatch.js +91 -0
- package/dist/plus/integrations/reads/issueBatch.js.map +1 -0
- package/dist/plus/integrations/reads/issueTracker.d.ts.map +1 -1
- package/dist/plus/integrations/reads/issueTracker.js +23 -17
- package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
- package/dist/plus/integrations/reads/sweeps.d.ts.map +1 -1
- package/dist/plus/integrations/reads/sweeps.js +5 -3
- package/dist/plus/integrations/reads/sweeps.js.map +1 -1
- package/dist/plus/integrations/reads/trackerIssue.d.ts +21 -0
- package/dist/plus/integrations/reads/trackerIssue.d.ts.map +1 -0
- package/dist/plus/integrations/reads/trackerIssue.js +51 -0
- package/dist/plus/integrations/reads/trackerIssue.js.map +1 -0
- package/dist/plus/integrations/reads/warnings.d.ts +2 -15
- package/dist/plus/integrations/reads/warnings.d.ts.map +1 -1
- package/dist/plus/integrations/reads/warnings.js +19 -1
- package/dist/plus/integrations/reads/warnings.js.map +1 -1
- package/dist/plus/integrations/results.d.ts +4 -20
- package/dist/plus/integrations/results.d.ts.map +1 -1
- package/dist/plus/integrations/results.js.map +1 -1
- package/dist/utils/promise.d.ts +2 -0
- package/dist/utils/promise.d.ts.map +1 -1
- package/dist/utils/promise.js +11 -0
- package/dist/utils/promise.js.map +1 -1
- package/dist/utils/promiseCache.d.ts +19 -0
- package/dist/utils/promiseCache.d.ts.map +1 -1
- package/dist/utils/promiseCache.js +30 -0
- package/dist/utils/promiseCache.js.map +1 -1
- package/dist/utils/resourceUsage.d.ts +5 -0
- package/dist/utils/resourceUsage.d.ts.map +1 -0
- package/dist/utils/resourceUsage.js +2 -0
- package/dist/utils/resourceUsage.js.map +1 -0
- package/dist/utils/string.d.ts +12 -0
- package/dist/utils/string.d.ts.map +1 -1
- package/dist/utils/string.js +36 -1
- package/dist/utils/string.js.map +1 -1
- package/docs/integrations.md +48 -33
- package/docs/kepler-read-api-parity.md +25 -4
- package/package.json +12 -4
- package/src/git/cache.ts +36 -0
- package/src/git/context.ts +11 -0
- package/src/git/errors.ts +13 -1
- package/src/git/features.ts +51 -0
- package/src/git/gitHealth.ts +522 -0
- package/src/git/models/search.ts +19 -4
- package/src/git/parsers/diffParser.ts +27 -4
- package/src/git/providers/config.ts +42 -1
- package/src/git/providers/graph.ts +12 -0
- package/src/git/providers/maintenance.ts +224 -0
- package/src/git/providers/operations.ts +10 -0
- package/src/git/providers/pausedOperations.ts +3 -0
- package/src/git/providers/provider.ts +2 -0
- package/src/git/providers/tags.ts +1 -0
- package/src/git/repositoryService.ts +1 -0
- package/src/git/run.types.ts +8 -0
- package/src/git/service.ts +3 -0
- package/src/git/utils/search.utils.ts +129 -5
- package/src/git/watching/watchService.ts +10 -0
- package/src/git-cli/cliGitProvider.ts +25 -0
- package/src/git-cli/exec/git.ts +120 -6
- package/src/git-cli/exec/gitQueue.ts +19 -17
- package/src/git-cli/parsers/logParser.ts +4 -1
- package/src/git-cli/providers/blame.ts +3 -8
- package/src/git-cli/providers/branches.ts +28 -8
- package/src/git-cli/providers/commits.ts +34 -2
- package/src/git-cli/providers/config.ts +38 -6
- package/src/git-cli/providers/graph.ts +107 -7
- package/src/git-cli/providers/maintenance.ts +2599 -0
- package/src/git-cli/providers/operations.ts +11 -0
- package/src/git-cli/providers/pausedOperations.ts +7 -2
- package/src/git-cli/providers/tags.ts +36 -1
- package/src/plus/agents/agentCapabilities.ts +286 -0
- package/src/plus/agents/providers/claudeCodeTranscript.ts +313 -58
- package/src/plus/agents/providers/{claudeCodeProvider.ts → gkAgentProvider.ts} +1123 -277
- package/src/plus/agents/types.ts +137 -40
- package/src/plus/ai/models/promptTemplates.ts +1 -1
- package/src/plus/ai/prompts.ts +62 -4
- package/src/plus/git-github/api/github.ts +404 -82
- package/src/plus/git-github/api/issueSearchQuery.ts +49 -1
- package/src/plus/git-github/providers/github/blame.ts +2 -1
- package/src/plus/integrations/collectionMetadata.ts +23 -1
- package/src/plus/integrations/context.ts +1 -1
- package/src/plus/integrations/index.ts +1 -0
- package/src/plus/integrations/integrationService.ts +29 -4
- package/src/plus/integrations/manager.ts +48 -0
- package/src/plus/integrations/models/gitHostIntegration.ts +165 -129
- package/src/plus/integrations/models/integration.ts +57 -22
- package/src/plus/integrations/models/issueCache.ts +29 -0
- package/src/plus/integrations/models/issueReads.ts +15 -0
- package/src/plus/integrations/models/issuesIntegration.ts +38 -8
- package/src/plus/integrations/providers/azureDevOps.ts +16 -13
- package/src/plus/integrations/providers/bitbucket-server.ts +4 -6
- package/src/plus/integrations/providers/bitbucket.ts +3 -4
- package/src/plus/integrations/providers/github.ts +23 -7
- package/src/plus/integrations/providers/gitlab.ts +4 -6
- package/src/plus/integrations/providers/jira.ts +52 -26
- package/src/plus/integrations/providers/jiraIssueByKey.ts +155 -0
- package/src/plus/integrations/providers/linear.ts +38 -25
- package/src/plus/integrations/providers/models.ts +17 -5
- package/src/plus/integrations/providers/providerErrors.ts +115 -0
- package/src/plus/integrations/providers/providersApi.ts +26 -68
- package/src/plus/integrations/providers/trello.ts +5 -4
- package/src/plus/integrations/providers/utils/providerPaging.ts +14 -7
- package/src/plus/integrations/reads/broaden.ts +282 -70
- package/src/plus/integrations/reads/filters.ts +39 -1
- package/src/plus/integrations/reads/issueBatch.ts +197 -0
- package/src/plus/integrations/reads/issueTracker.ts +26 -24
- package/src/plus/integrations/reads/sweeps.ts +19 -12
- package/src/plus/integrations/reads/trackerIssue.ts +105 -0
- package/src/plus/integrations/reads/warnings.ts +23 -17
- package/src/plus/integrations/results.ts +4 -20
- package/src/utils/promise.ts +15 -0
- package/src/utils/promiseCache.ts +43 -0
- package/src/utils/resourceUsage.ts +5 -0
- package/src/utils/string.ts +38 -1
- package/dist/plus/agents/providers/claudeCodeProvider.d.ts.map +0 -1
- package/dist/plus/agents/providers/claudeCodeProvider.js.map +0 -1
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
GitHealthSnapshot,
|
|
3
|
+
GitMaintenanceTask,
|
|
4
|
+
GitOptimizationCapability,
|
|
5
|
+
GitOptimizationId,
|
|
6
|
+
} from './providers/maintenance.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Pure recommendation engine for the Git Health feature — no vscode imports, trivially unit-testable.
|
|
10
|
+
* Thresholds are initial, tunable, telemetry-informed values (see `.work/dev/git-health/spec.md`).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** Working-tree size (via the index-bytes proxy) at/above which untracked-cache/fsmonitor/manyFiles help. */
|
|
14
|
+
export const trackedFilesThreshold = 10_000;
|
|
15
|
+
/** Loose-object estimate at/above which the `loose-objects` maintenance task is worth running. */
|
|
16
|
+
export const looseObjectsThreshold = 5_000;
|
|
17
|
+
/** Git's default uncovered-pack threshold for the `incremental-repack` auto condition. */
|
|
18
|
+
export const packsOutsideMultiPackIndexThreshold = 10;
|
|
19
|
+
/** Loose refs at/above which packing the files ref backend avoids repeated filesystem traversal. */
|
|
20
|
+
export const looseRefsThreshold = 256;
|
|
21
|
+
/** Total pack bytes at/above which a repo counts as "clearly large" for the banner gate (~1 GiB). */
|
|
22
|
+
export const largePackBytesThreshold = 1024 ** 3;
|
|
23
|
+
/** Rough bytes-per-entry of a git v2 index — turns `.git/index` size into a tracked-file-count proxy. */
|
|
24
|
+
export const approxBytesPerIndexEntry = 80;
|
|
25
|
+
/** Duration (ms) above which a git command is "slow" — mirrors the exec-layer slow-call threshold. */
|
|
26
|
+
export const slowCommandThresholdMs = 2000;
|
|
27
|
+
|
|
28
|
+
/** Git operation families whose runtime maps to a distinct repository optimization surface. */
|
|
29
|
+
export type GitHealthSlownessCategory = 'worktree' | 'history' | 'refs' | 'objects';
|
|
30
|
+
|
|
31
|
+
/** Persisted passive-slowness sample for one operation family. */
|
|
32
|
+
export interface GitHealthSlownessSample {
|
|
33
|
+
/** Number of slow git commands observed for this repo. */
|
|
34
|
+
readonly count: number;
|
|
35
|
+
/** Epoch ms of the most recent slow command. */
|
|
36
|
+
readonly lastAt: number;
|
|
37
|
+
/** Longest single slow-command duration observed (ms). */
|
|
38
|
+
readonly maxDurationMs: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Per-repo passive-slowness summary, split so recommendations only target the affected operation family. */
|
|
42
|
+
export type GitHealthSlowness = Partial<Record<GitHealthSlownessCategory, GitHealthSlownessSample>>;
|
|
43
|
+
|
|
44
|
+
export type GitOptimizationTier = 'auto' | 'ask';
|
|
45
|
+
|
|
46
|
+
/** Coarse duration buckets for maintenance telemetry. */
|
|
47
|
+
export type GitHealthDurationBucket = '<1s' | '1-5s' | '5-15s' | '15-60s' | '>60s';
|
|
48
|
+
|
|
49
|
+
/** Which measured signal tripped a finding (drives view copy + telemetry buckets). */
|
|
50
|
+
export type GitHealthFindingReason =
|
|
51
|
+
| 'looseObjects'
|
|
52
|
+
| 'looseRefs'
|
|
53
|
+
| 'packsOutsideMultiPackIndex'
|
|
54
|
+
| 'trackedFiles'
|
|
55
|
+
| 'largePacks'
|
|
56
|
+
| 'worktreeSlowness';
|
|
57
|
+
|
|
58
|
+
/** A single recommendation: a lever to apply, its tier, and the evidence that triggered it. */
|
|
59
|
+
export interface GitHealthFinding {
|
|
60
|
+
readonly tier: GitOptimizationTier;
|
|
61
|
+
readonly reason: GitHealthFindingReason;
|
|
62
|
+
/** The measured value that crossed the threshold. */
|
|
63
|
+
readonly value: number;
|
|
64
|
+
/** The threshold it crossed. */
|
|
65
|
+
readonly threshold: number;
|
|
66
|
+
/** The concrete action this finding recommends. */
|
|
67
|
+
readonly action:
|
|
68
|
+
| { readonly kind: 'maintenance'; readonly task: GitMaintenanceTask }
|
|
69
|
+
| { readonly kind: 'optimization'; readonly id: GitOptimizationId };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface GitHealthReport {
|
|
73
|
+
readonly findings: readonly GitHealthFinding[];
|
|
74
|
+
/** Repository shape that scopes local history, object, and working-tree measurements. */
|
|
75
|
+
readonly repository: GitHealthSnapshot['repository'];
|
|
76
|
+
/** Whether the repo is "clearly large" (any working-tree threshold met, or pack bytes ≥ ~1 GiB). */
|
|
77
|
+
readonly clearlyLarge: boolean;
|
|
78
|
+
/** Extrapolated loose-object count (from the probe sample). */
|
|
79
|
+
readonly estimatedLooseObjects: number;
|
|
80
|
+
/** Loose refs found by the bounded files-backend probe. */
|
|
81
|
+
readonly looseRefs: { readonly count: number; readonly exact: boolean };
|
|
82
|
+
/** Repository-size signal from the index; see {@link trackedFilesScope} for how to interpret it. */
|
|
83
|
+
readonly estimatedTrackedFiles: number;
|
|
84
|
+
/** `true` when {@link estimatedTrackedFiles} is the exact repository-wide count from a normal index. */
|
|
85
|
+
readonly trackedFilesExact: boolean;
|
|
86
|
+
/** Whether the displayed count covers the repository, a sparse working set, or is only a byte-size estimate. */
|
|
87
|
+
readonly trackedFilesScope: 'repository' | 'sparseWorkingTree' | 'estimate';
|
|
88
|
+
readonly packCount: number;
|
|
89
|
+
readonly packsOutsideMultiPackIndex: number | undefined;
|
|
90
|
+
readonly packBytes: number;
|
|
91
|
+
readonly commitGraph: {
|
|
92
|
+
readonly present: boolean;
|
|
93
|
+
readonly mtime: number | undefined;
|
|
94
|
+
readonly changedPaths: boolean;
|
|
95
|
+
readonly changedPathsSupported: boolean;
|
|
96
|
+
readonly disabled: boolean;
|
|
97
|
+
/** `core.commitGraph` explicitly false in git config — git won't read the cache, GitLens won't write it. */
|
|
98
|
+
readonly readDisabled: boolean;
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Extrapolates total loose-object count from a uniform sample of the 256 object fanout dirs. Loose
|
|
104
|
+
* objects distribute uniformly by sha prefix, so `(objectsInSampled / dirsSampled) * 256` is sound.
|
|
105
|
+
*/
|
|
106
|
+
export function estimateLooseObjects(objectsInSampled: number, dirsSampled: number): number {
|
|
107
|
+
if (dirsSampled <= 0) return 0;
|
|
108
|
+
|
|
109
|
+
return Math.round((objectsInSampled / dirsSampled) * 256);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Turns `.git/index` byte size into an approximate tracked-file count. */
|
|
113
|
+
export function estimateTrackedFiles(indexBytes: number): number {
|
|
114
|
+
if (indexBytes <= 0) return 0;
|
|
115
|
+
|
|
116
|
+
return Math.round(indexBytes / approxBytesPerIndexEntry);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Computes the {@link GitHealthReport} from a cheap probe, passive slowness, and capabilities. Findings
|
|
121
|
+
* are only emitted for levers that are supported, not already applied, and past their threshold.
|
|
122
|
+
*/
|
|
123
|
+
export function computeHealthReport(
|
|
124
|
+
snapshot: GitHealthSnapshot,
|
|
125
|
+
slowness: GitHealthSlowness | undefined,
|
|
126
|
+
capabilities: readonly GitOptimizationCapability[],
|
|
127
|
+
): GitHealthReport {
|
|
128
|
+
const estimatedLooseObjects = estimateLooseObjects(
|
|
129
|
+
snapshot.looseObjects.objectsInSampledDirs,
|
|
130
|
+
snapshot.looseObjects.dirsSampled,
|
|
131
|
+
);
|
|
132
|
+
// A normal index header is the exact tracked-file count. A sparse index header is still a useful exact
|
|
133
|
+
// count of its populated working set, but not of the whole repository. A split index header covers only
|
|
134
|
+
// the mutable layer and conflict stages duplicate paths, so neither raw count is used.
|
|
135
|
+
const useIndexEntryCount =
|
|
136
|
+
snapshot.indexEntryCount != null &&
|
|
137
|
+
(snapshot.indexEntryCountType === 'full' || snapshot.indexEntryCountType === 'sparse');
|
|
138
|
+
const estimatedTrackedFiles = useIndexEntryCount
|
|
139
|
+
? snapshot.indexEntryCount
|
|
140
|
+
: estimateTrackedFiles(snapshot.indexBytes);
|
|
141
|
+
const trackedFilesExact = useIndexEntryCount && snapshot.indexEntryCountType === 'full';
|
|
142
|
+
const trackedFilesScope = trackedFilesExact
|
|
143
|
+
? 'repository'
|
|
144
|
+
: useIndexEntryCount && snapshot.indexEntryCountType === 'sparse'
|
|
145
|
+
? 'sparseWorkingTree'
|
|
146
|
+
: 'estimate';
|
|
147
|
+
const largeWorkingTree = estimatedTrackedFiles >= trackedFilesThreshold;
|
|
148
|
+
const clearlyLarge = largeWorkingTree || snapshot.packBytes >= largePackBytesThreshold;
|
|
149
|
+
|
|
150
|
+
const supported = (id: GitOptimizationId): boolean => capabilities.find(c => c.id === id)?.supported === true;
|
|
151
|
+
|
|
152
|
+
const findings: GitHealthFinding[] = [];
|
|
153
|
+
|
|
154
|
+
// Auto tier — safe `git maintenance run` one-shots (gated on `git maintenance` support).
|
|
155
|
+
if (snapshot.supportsMaintenanceRun) {
|
|
156
|
+
if (estimatedLooseObjects >= looseObjectsThreshold) {
|
|
157
|
+
findings.push({
|
|
158
|
+
tier: 'auto',
|
|
159
|
+
reason: 'looseObjects',
|
|
160
|
+
value: estimatedLooseObjects,
|
|
161
|
+
threshold: looseObjectsThreshold,
|
|
162
|
+
action: { kind: 'maintenance', task: 'loose-objects' },
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
const incrementalRepackThreshold = snapshot.incrementalRepackAutoThreshold;
|
|
166
|
+
const incrementalRepackDue =
|
|
167
|
+
snapshot.multiPackIndexEnabled === true &&
|
|
168
|
+
snapshot.packsOutsideMultiPackIndex != null &&
|
|
169
|
+
incrementalRepackThreshold != null &&
|
|
170
|
+
incrementalRepackThreshold !== 0 &&
|
|
171
|
+
(incrementalRepackThreshold < 0
|
|
172
|
+
? snapshot.packCount > 0
|
|
173
|
+
: snapshot.packsOutsideMultiPackIndex >= incrementalRepackThreshold);
|
|
174
|
+
if (incrementalRepackDue) {
|
|
175
|
+
findings.push({
|
|
176
|
+
tier: 'auto',
|
|
177
|
+
reason: 'packsOutsideMultiPackIndex',
|
|
178
|
+
value: snapshot.packsOutsideMultiPackIndex,
|
|
179
|
+
threshold: incrementalRepackThreshold,
|
|
180
|
+
action: { kind: 'maintenance', task: 'incremental-repack' },
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (
|
|
185
|
+
snapshot.supportsPackRefsMaintenance &&
|
|
186
|
+
snapshot.repository.refFormat === 'files' &&
|
|
187
|
+
snapshot.looseRefs.count >= looseRefsThreshold
|
|
188
|
+
) {
|
|
189
|
+
findings.push({
|
|
190
|
+
tier: 'auto',
|
|
191
|
+
reason: 'looseRefs',
|
|
192
|
+
value: snapshot.looseRefs.count,
|
|
193
|
+
threshold: looseRefsThreshold,
|
|
194
|
+
action: { kind: 'maintenance', task: 'pack-refs' },
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Large-working-tree levers share the same evidence; they differ only in tier and eligibility.
|
|
199
|
+
const trackedFilesEvidence = {
|
|
200
|
+
reason: 'trackedFiles',
|
|
201
|
+
value: estimatedTrackedFiles,
|
|
202
|
+
threshold: trackedFilesThreshold,
|
|
203
|
+
} as const;
|
|
204
|
+
const workingTreeLevers: { id: GitOptimizationId; tier: GitOptimizationTier; eligible: boolean }[] = [
|
|
205
|
+
{
|
|
206
|
+
id: 'untrackedCache',
|
|
207
|
+
tier: 'ask',
|
|
208
|
+
// Skip if already on, if it previously failed git's filesystem probe for this repo, or if the
|
|
209
|
+
// user set the key themselves — an explicit `false`/`keep` is a decision (often made after bad
|
|
210
|
+
// `git status` results), and this lever respects it rather than overriding it.
|
|
211
|
+
eligible:
|
|
212
|
+
!snapshot.config.untrackedCache &&
|
|
213
|
+
!snapshot.config.untrackedCacheConfigured &&
|
|
214
|
+
!snapshot.untrackedCacheNotApplicable,
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
id: 'fsmonitor',
|
|
218
|
+
tier: 'ask',
|
|
219
|
+
// Skip if already on or it previously failed to start for this repo.
|
|
220
|
+
eligible: !snapshot.config.fsmonitor && !snapshot.fsmonitorNotApplicable,
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
id: 'manyFiles',
|
|
224
|
+
tier: 'ask',
|
|
225
|
+
// `manyFiles` defaults the untracked cache on, so a repo whose filesystem already failed that
|
|
226
|
+
// probe must not be offered it either — unless the user's own explicit `core.untrackedCache`
|
|
227
|
+
// setting overrides the default, in which case the probe result is moot.
|
|
228
|
+
eligible:
|
|
229
|
+
!snapshot.config.manyFiles &&
|
|
230
|
+
!(snapshot.untrackedCacheNotApplicable && !snapshot.config.untrackedCacheConfigured),
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
id: 'sparseIndex',
|
|
234
|
+
tier: 'ask',
|
|
235
|
+
// Sparse-directory entries are safe only in cone mode. Require a normal full index so this
|
|
236
|
+
// recommendation is based on the repository-wide path count, not a split/conflicted approximation.
|
|
237
|
+
eligible:
|
|
238
|
+
snapshot.repository.sparseCheckout === true &&
|
|
239
|
+
snapshot.repository.sparseCheckoutCone === true &&
|
|
240
|
+
snapshot.repository.sparseIndex === false &&
|
|
241
|
+
snapshot.repository.splitIndex === false &&
|
|
242
|
+
snapshot.indexEntryCountType === 'full',
|
|
243
|
+
},
|
|
244
|
+
];
|
|
245
|
+
const worktreeSlowness = slowness?.worktree;
|
|
246
|
+
const worktreeSlownessObserved = (worktreeSlowness?.count ?? 0) > 0;
|
|
247
|
+
if (largeWorkingTree || worktreeSlownessObserved) {
|
|
248
|
+
const evidence: Pick<GitHealthFinding, 'reason' | 'value' | 'threshold'> = largeWorkingTree
|
|
249
|
+
? trackedFilesEvidence
|
|
250
|
+
: {
|
|
251
|
+
reason: 'worktreeSlowness',
|
|
252
|
+
value: worktreeSlowness?.maxDurationMs ?? 0,
|
|
253
|
+
threshold: slowCommandThresholdMs,
|
|
254
|
+
};
|
|
255
|
+
for (const lever of workingTreeLevers) {
|
|
256
|
+
if (!lever.eligible || !supported(lever.id)) continue;
|
|
257
|
+
|
|
258
|
+
findings.push({
|
|
259
|
+
tier: lever.tier,
|
|
260
|
+
...evidence,
|
|
261
|
+
action: { kind: 'optimization', id: lever.id },
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Ask tier — system-scheduled background maintenance. Suggested for a not-yet-registered repo that's
|
|
267
|
+
// clearly large. Passive slowness is deliberately NOT enough: worktree slowness maps to the worktree
|
|
268
|
+
// levers above, history is already covered by demand commit-graph writes, and ref/object maintenance has
|
|
269
|
+
// its own measured auto-tier conditions. A generic scheduler recommendation would promise the wrong fix.
|
|
270
|
+
// `=== false`, not falsy: `undefined` means the registration list couldn't be read, and an unknown state
|
|
271
|
+
// must never be suggested away — that's how a user's own registration gets silently reclaimed by Undo.
|
|
272
|
+
if (clearlyLarge && snapshot.maintenanceRegistered === false && supported('backgroundMaintenance')) {
|
|
273
|
+
// Resolve the evidence once so reason/value/threshold can never drift apart. Ordered so each
|
|
274
|
+
// suggestion argues from its most DISTINCTIVE signal rather than whichever crossed first: a repo
|
|
275
|
+
// large by both file count and pack bytes would otherwise attach the same tracked-files evidence to
|
|
276
|
+
// this finding as to the fsmonitor/manyFiles findings, and the view would show two identical meters.
|
|
277
|
+
let evidence: Pick<GitHealthFinding, 'reason' | 'value' | 'threshold'>;
|
|
278
|
+
if (snapshot.packBytes >= largePackBytesThreshold) {
|
|
279
|
+
evidence = { reason: 'largePacks', value: snapshot.packBytes, threshold: largePackBytesThreshold };
|
|
280
|
+
} else {
|
|
281
|
+
evidence = trackedFilesEvidence;
|
|
282
|
+
}
|
|
283
|
+
findings.push({
|
|
284
|
+
tier: 'ask',
|
|
285
|
+
...evidence,
|
|
286
|
+
action: { kind: 'optimization', id: 'backgroundMaintenance' },
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
findings: findings,
|
|
292
|
+
repository: snapshot.repository,
|
|
293
|
+
clearlyLarge: clearlyLarge,
|
|
294
|
+
estimatedLooseObjects: estimatedLooseObjects,
|
|
295
|
+
looseRefs: snapshot.looseRefs,
|
|
296
|
+
estimatedTrackedFiles: estimatedTrackedFiles,
|
|
297
|
+
trackedFilesExact: trackedFilesExact,
|
|
298
|
+
trackedFilesScope: trackedFilesScope,
|
|
299
|
+
packCount: snapshot.packCount,
|
|
300
|
+
packsOutsideMultiPackIndex: snapshot.packsOutsideMultiPackIndex,
|
|
301
|
+
packBytes: snapshot.packBytes,
|
|
302
|
+
commitGraph: snapshot.commitGraph,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* What the view shows for one lever. Ownership is the load-bearing distinction: `applied` offers Undo
|
|
308
|
+
* because GitLens recorded the exact prior; `userEnabled` offers nothing at all — there is nothing for
|
|
309
|
+
* GitLens to undo, and a disabled control would imply otherwise.
|
|
310
|
+
*/
|
|
311
|
+
export type GitHealthLeverStatus =
|
|
312
|
+
| 'suggested'
|
|
313
|
+
| 'applied'
|
|
314
|
+
| 'userEnabled'
|
|
315
|
+
/** Supported and usable here, just not worth recommending for this repo — NOT a failure. */
|
|
316
|
+
| 'available'
|
|
317
|
+
| 'unavailable'
|
|
318
|
+
| 'notApplicable';
|
|
319
|
+
|
|
320
|
+
export interface GitHealthLever {
|
|
321
|
+
readonly id: GitOptimizationId;
|
|
322
|
+
readonly status: GitHealthLeverStatus;
|
|
323
|
+
readonly tier: GitOptimizationTier;
|
|
324
|
+
/** Why it can't be used here — rendered verbatim, never flattened to "unsupported". */
|
|
325
|
+
readonly reason?: string;
|
|
326
|
+
/** A consequence the person must see BEFORE choosing, not after. */
|
|
327
|
+
readonly note?: string;
|
|
328
|
+
/**
|
|
329
|
+
* `true` when this lever's state could not be DETERMINED (e.g. the registration list was unreadable), as
|
|
330
|
+
* opposed to a lever this git/platform doesn't support.
|
|
331
|
+
*/
|
|
332
|
+
readonly checkFailed?: boolean;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** Consequences that reach beyond GitLens, surfaced on the apply action itself. */
|
|
336
|
+
const leverNotes: Partial<Record<GitOptimizationId, string>> = {
|
|
337
|
+
backgroundMaintenance:
|
|
338
|
+
'Adds a task to your operating system’s scheduler. Undo unregisters this repository, but leaves the scheduler itself in place.',
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Collapses a snapshot + capabilities + report into the per-lever rows the Git Health view renders.
|
|
343
|
+
* Pure, so the webview never re-derives ownership or eligibility rules that already live here.
|
|
344
|
+
*/
|
|
345
|
+
export function computeLevers(
|
|
346
|
+
snapshot: GitHealthSnapshot,
|
|
347
|
+
capabilities: readonly GitOptimizationCapability[],
|
|
348
|
+
report: GitHealthReport,
|
|
349
|
+
): GitHealthLever[] {
|
|
350
|
+
const suggested = new Set<GitOptimizationId>(
|
|
351
|
+
report.findings.flatMap(f => (f.action.kind === 'optimization' ? [f.action.id] : [])),
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
const enabled: Record<GitOptimizationId, boolean> = {
|
|
355
|
+
untrackedCache: snapshot.config.untrackedCache,
|
|
356
|
+
fsmonitor: snapshot.config.fsmonitor,
|
|
357
|
+
manyFiles: snapshot.config.manyFiles,
|
|
358
|
+
// `=== true`, not the raw (possibly `undefined`) value — an unreadable registration is handled as its
|
|
359
|
+
// own status below, and must never fall through to "enabled".
|
|
360
|
+
backgroundMaintenance: snapshot.maintenanceRegistered === true,
|
|
361
|
+
sparseIndex: snapshot.repository.sparseIndex === true,
|
|
362
|
+
};
|
|
363
|
+
const blocked: Partial<Record<GitOptimizationId, boolean>> = {
|
|
364
|
+
untrackedCache: snapshot.untrackedCacheNotApplicable,
|
|
365
|
+
fsmonitor: snapshot.fsmonitorNotApplicable,
|
|
366
|
+
manyFiles: snapshot.untrackedCacheNotApplicable && !snapshot.config.untrackedCacheConfigured,
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
return capabilities.map<GitHealthLever>(capability => {
|
|
370
|
+
const id = capability.id;
|
|
371
|
+
const note = capability.note ?? leverNotes[id];
|
|
372
|
+
// Every optimization lever is ask-tier now — the auto tier only ever runs maintenance tasks.
|
|
373
|
+
const tier: GitOptimizationTier = 'ask';
|
|
374
|
+
|
|
375
|
+
// Unavailable outranks everything: a lever the installed git or platform cannot do is never
|
|
376
|
+
// "suggested", whatever the thresholds say.
|
|
377
|
+
if (!capability.supported) {
|
|
378
|
+
return { id: id, status: 'unavailable', tier: tier, reason: capability.reason, note: note };
|
|
379
|
+
}
|
|
380
|
+
// An unreadable maintenance registration can't answer the ownership/suggestion questions below, so
|
|
381
|
+
// it gets its own status rather than falling through and being guessed as "not registered".
|
|
382
|
+
if (id === 'backgroundMaintenance' && snapshot.maintenanceRegistered === undefined) {
|
|
383
|
+
return {
|
|
384
|
+
id: id,
|
|
385
|
+
status: 'unavailable',
|
|
386
|
+
tier: tier,
|
|
387
|
+
reason: "Couldn't read Git's maintenance registration — try reopening the repository",
|
|
388
|
+
note: note,
|
|
389
|
+
checkFailed: true,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
// Then ownership, which is what decides whether Undo is offered at all.
|
|
393
|
+
if (enabled[id]) {
|
|
394
|
+
return { id: id, status: snapshot.applied[id] ? 'applied' : 'userEnabled', tier: tier, note: note };
|
|
395
|
+
}
|
|
396
|
+
if (id === 'sparseIndex') {
|
|
397
|
+
if (
|
|
398
|
+
snapshot.repository.sparseCheckout == null ||
|
|
399
|
+
snapshot.repository.sparseCheckoutCone == null ||
|
|
400
|
+
snapshot.repository.sparseIndex == null ||
|
|
401
|
+
snapshot.repository.splitIndex == null
|
|
402
|
+
) {
|
|
403
|
+
return {
|
|
404
|
+
id: id,
|
|
405
|
+
status: 'unavailable',
|
|
406
|
+
tier: tier,
|
|
407
|
+
reason: "Couldn't determine this worktree’s sparse-checkout and index configuration",
|
|
408
|
+
note: note,
|
|
409
|
+
checkFailed: true,
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
if (!snapshot.repository.sparseCheckout) {
|
|
413
|
+
return {
|
|
414
|
+
id: id,
|
|
415
|
+
status: 'notApplicable',
|
|
416
|
+
tier: tier,
|
|
417
|
+
reason: 'This worktree is not using sparse checkout.',
|
|
418
|
+
note: note,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
if (!snapshot.repository.sparseCheckoutCone) {
|
|
422
|
+
return {
|
|
423
|
+
id: id,
|
|
424
|
+
status: 'notApplicable',
|
|
425
|
+
tier: tier,
|
|
426
|
+
reason: 'Sparse indexes require cone-mode sparse checkout.',
|
|
427
|
+
note: note,
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
if (snapshot.repository.splitIndex || snapshot.indexEntryCountType === 'split') {
|
|
431
|
+
return {
|
|
432
|
+
id: id,
|
|
433
|
+
status: 'notApplicable',
|
|
434
|
+
tier: tier,
|
|
435
|
+
reason: 'This worktree uses a split index; disable it before enabling a sparse index.',
|
|
436
|
+
note: note,
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
if (snapshot.indexEntryCountType === 'conflicted') {
|
|
440
|
+
return {
|
|
441
|
+
id: id,
|
|
442
|
+
status: 'notApplicable',
|
|
443
|
+
tier: tier,
|
|
444
|
+
reason: 'Finish the current merge or rebase before enabling a sparse index.',
|
|
445
|
+
note: note,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
// `notApplicable` means GitLens TRIED here and this repo can't use it — the gk marker records that.
|
|
450
|
+
// `capability.note` is the supported-with-caveat field, not an unavailability reason, so the
|
|
451
|
+
// explanation has to come from the marker's meaning instead.
|
|
452
|
+
if (blocked[id]) {
|
|
453
|
+
return {
|
|
454
|
+
id: id,
|
|
455
|
+
status: 'notApplicable',
|
|
456
|
+
tier: tier,
|
|
457
|
+
reason:
|
|
458
|
+
id === 'fsmonitor'
|
|
459
|
+
? 'The file system monitor could not start for this repository.'
|
|
460
|
+
: id === 'manyFiles'
|
|
461
|
+
? 'This would enable the untracked cache, but this file system does not report directory changes reliably, so Git would return incorrect status results.'
|
|
462
|
+
: 'This file system does not report directory changes reliably, so Git would return incorrect status results.',
|
|
463
|
+
note: note,
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Supported and usable, just below the thresholds — `available`, never `notApplicable`. Reporting
|
|
468
|
+
// a healthy repo's levers as unavailable tells the user something false.
|
|
469
|
+
return { id: id, status: suggested.has(id) ? 'suggested' : 'available', tier: tier, note: note };
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/** Auto-tier maintenance tasks the daily pass should run right now (deduped, order-stable). */
|
|
474
|
+
export function getAutoMaintenanceTasks(report: GitHealthReport): GitMaintenanceTask[] {
|
|
475
|
+
const tasks: GitMaintenanceTask[] = [];
|
|
476
|
+
for (const finding of report.findings) {
|
|
477
|
+
if (finding.tier === 'auto' && finding.action.kind === 'maintenance' && !tasks.includes(finding.action.task)) {
|
|
478
|
+
tasks.push(finding.action.task);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return tasks;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/** Auto-tier optimization levers the daily pass should silently apply — none today; the auto tier currently
|
|
485
|
+
* applies only maintenance tasks, but this stays in place for future levers. */
|
|
486
|
+
export function getAutoOptimizations(report: GitHealthReport): GitOptimizationId[] {
|
|
487
|
+
const ids: GitOptimizationId[] = [];
|
|
488
|
+
for (const finding of report.findings) {
|
|
489
|
+
if (finding.tier === 'auto' && finding.action.kind === 'optimization' && !ids.includes(finding.action.id)) {
|
|
490
|
+
ids.push(finding.action.id);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return ids;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Banner rule: fire only when the report contains an ask-tier fix and the repo is clearly large or slow in
|
|
498
|
+
* the working-tree family. History/ref/object slowness must not advertise unrelated worktree levers.
|
|
499
|
+
*/
|
|
500
|
+
export function isBannerEligible(report: GitHealthReport, slowness: GitHealthSlowness | undefined): boolean {
|
|
501
|
+
const hasAskTierFix = report.findings.some(f => f.tier === 'ask');
|
|
502
|
+
const worktreeSlownessObserved = (slowness?.worktree?.count ?? 0) > 0;
|
|
503
|
+
return hasAskTierFix && (report.clearlyLarge || worktreeSlownessObserved);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/** Evidence-gated banner state for one repository — undefined when the banner has nothing to say. */
|
|
507
|
+
export interface GitHealthBannerState {
|
|
508
|
+
/** Show the in-graph banner strip (eligible and not explicitly dismissed). */
|
|
509
|
+
readonly banner: boolean;
|
|
510
|
+
/** Show the evidence dot on the Visualizations & Health toggle (eligible, view not visited recently). */
|
|
511
|
+
readonly indicator: boolean;
|
|
512
|
+
/** Which evidence family armed it — measured worktree slowness, or repo scale alone. */
|
|
513
|
+
readonly reason: 'slowness' | 'large';
|
|
514
|
+
/** Worst observed slow-command duration (ms); present when reason is 'slowness'. */
|
|
515
|
+
readonly maxDurationMs?: number;
|
|
516
|
+
/** Tracked-file count evidence; present when reason is 'large'. */
|
|
517
|
+
readonly trackedFiles?: number;
|
|
518
|
+
/** Whether trackedFiles is exact (report.trackedFilesScope !== 'estimate'). */
|
|
519
|
+
readonly trackedFilesExact?: boolean;
|
|
520
|
+
/** Count of levers the health view will show as "suggested" — keep in sync with its verdict. */
|
|
521
|
+
readonly suggestedCount: number;
|
|
522
|
+
}
|
package/src/git/models/search.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
type SearchOperatorsShortForm = '' | '=:' | '@:' | '#:' | '?:' | '~:' | 'is:' | '>:' | '<:' | '^:';
|
|
2
2
|
export type SearchOperatorsLongForm =
|
|
3
3
|
| 'message:'
|
|
4
|
+
| '-message:'
|
|
4
5
|
| 'author:'
|
|
6
|
+
| 'committer:'
|
|
5
7
|
| 'commit:'
|
|
6
8
|
| 'file:'
|
|
7
9
|
| 'change:'
|
|
@@ -17,8 +19,10 @@ export const searchOperators = new Set<string>([
|
|
|
17
19
|
'',
|
|
18
20
|
'=:',
|
|
19
21
|
'message:',
|
|
22
|
+
'-message:',
|
|
20
23
|
'@:',
|
|
21
24
|
'author:',
|
|
25
|
+
'committer:',
|
|
22
26
|
'#:',
|
|
23
27
|
'commit:',
|
|
24
28
|
'?:',
|
|
@@ -41,8 +45,10 @@ export const searchOperatorsToLongFormMap = new Map<SearchOperators, SearchOpera
|
|
|
41
45
|
['', 'message:'],
|
|
42
46
|
['=:', 'message:'],
|
|
43
47
|
['message:', 'message:'],
|
|
48
|
+
['-message:', '-message:'],
|
|
44
49
|
['@:', 'author:'],
|
|
45
50
|
['author:', 'author:'],
|
|
51
|
+
['committer:', 'committer:'],
|
|
46
52
|
['#:', 'commit:'],
|
|
47
53
|
['commit:', 'commit:'],
|
|
48
54
|
['?:', 'file:'],
|
|
@@ -62,11 +68,20 @@ export const searchOperatorsToLongFormMap = new Map<SearchOperators, SearchOpera
|
|
|
62
68
|
]);
|
|
63
69
|
|
|
64
70
|
export const searchOperationHelpRegex =
|
|
65
|
-
/(?:^|(\b|\s)*)((=:|message:|@:|author:|#:|commit:|\?:|file:|~:|change:|is:|type:|>:|after:|since:|<:|before:|until:|\^:|ref:)(?:"[^"]*"?|[^\s]*))(?:$|(\b|\s))/g;
|
|
71
|
+
/(?:^|(\b|\s)*)((=:|message:|-message:|@:|author:|committer:|#:|commit:|\?:|file:|~:|change:|is:|type:|>:|after:|since:|<:|before:|until:|\^:|ref:)(?:"[^"]*"?|[^\s]*))(?:$|(\b|\s))/g;
|
|
66
72
|
|
|
67
73
|
export interface SearchQuery {
|
|
68
74
|
query: string;
|
|
69
|
-
naturalLanguage?:
|
|
75
|
+
naturalLanguage?:
|
|
76
|
+
| boolean
|
|
77
|
+
| {
|
|
78
|
+
query: string;
|
|
79
|
+
processedQuery?: string;
|
|
80
|
+
error?: string;
|
|
81
|
+
explanation?: string;
|
|
82
|
+
mode?: 'highlight' | 'filter' | 'select';
|
|
83
|
+
alternates?: string[];
|
|
84
|
+
};
|
|
70
85
|
|
|
71
86
|
filter?: boolean;
|
|
72
87
|
matchAll?: boolean;
|
|
@@ -96,8 +111,8 @@ export interface ParsedSearchQuery {
|
|
|
96
111
|
export interface SearchQueryFilters {
|
|
97
112
|
/** Specifies whether the search results will be filtered to specific files */
|
|
98
113
|
files: boolean;
|
|
99
|
-
/** Specifies whether the search results will be filtered to a specific type, only `stash`, `tip`, and `
|
|
100
|
-
type?: 'stash' | 'tip' | 'wip';
|
|
114
|
+
/** Specifies whether the search results will be filtered to a specific type, only `stash`, `tip`, `wip`, and `merge` are supported */
|
|
115
|
+
type?: 'stash' | 'tip' | 'wip' | 'merge';
|
|
101
116
|
/** Specifies whether the search results will be filtered to a specific ref or ref range */
|
|
102
117
|
refs: boolean;
|
|
103
118
|
}
|
|
@@ -219,18 +219,41 @@ export async function filterDiffFiles(
|
|
|
219
219
|
.join('');
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
-
/**
|
|
222
|
+
/**
|
|
223
|
+
* Counts the added and removed lines of a parsed diff file, matching what `git diff --numstat`
|
|
224
|
+
* reports for it. Binary files and files without content changes (pure renames, mode changes)
|
|
225
|
+
* count as 0/0.
|
|
226
|
+
*/
|
|
223
227
|
export function countDiffInsertionsAndDeletions(file: ParsedGitDiffFile): { insertions: number; deletions: number } {
|
|
224
228
|
let insertions = 0;
|
|
225
229
|
let deletions = 0;
|
|
226
230
|
for (const hunk of file.hunks) {
|
|
227
|
-
|
|
228
|
-
|
|
231
|
+
// Counted off the hunk's raw content rather than its header counts (which include unchanged
|
|
232
|
+
// context) or `hunk.lines` (which merges a removed/added pair into one `changed` entry and
|
|
233
|
+
// lets a following context line overwrite the tail of an asymmetric block). Scanned in place
|
|
234
|
+
// rather than split into lines: splitting a multi-megabyte diff allocates a string per line
|
|
235
|
+
// for a single pass that only ever looks at each line's first character.
|
|
236
|
+
const { content } = hunk;
|
|
237
|
+
for (let start = 0; start < content.length;) {
|
|
238
|
+
const marker = content[start];
|
|
239
|
+
if (marker === '+') {
|
|
240
|
+
insertions++;
|
|
241
|
+
} else if (marker === '-') {
|
|
242
|
+
deletions++;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const end = content.indexOf('\n', start);
|
|
246
|
+
if (end === -1) break;
|
|
247
|
+
|
|
248
|
+
start = end + 1;
|
|
249
|
+
}
|
|
229
250
|
}
|
|
230
251
|
return { insertions: insertions, deletions: deletions };
|
|
231
252
|
}
|
|
232
253
|
|
|
233
|
-
/** Counts approximate number of changed lines in a diff file
|
|
254
|
+
/** Counts the approximate number of changed lines in a diff file — the size of both sides of its
|
|
255
|
+
* hunks, unchanged context included. Suitable for relative sizing, not for reporting a line count
|
|
256
|
+
* to the user; use {@link countDiffInsertionsAndDeletions} for that. */
|
|
234
257
|
export function countDiffLines(file: ParsedGitDiffFile): number {
|
|
235
258
|
let count = 0;
|
|
236
259
|
for (const hunk of file.hunks) {
|