@gitkraken/core-gitlens 0.5.110 → 0.5.112
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 +19 -1
- package/dist/git/cache.d.ts +111 -6
- package/dist/git/cache.d.ts.map +1 -1
- package/dist/git/cache.js +312 -43
- package/dist/git/cache.js.map +1 -1
- package/dist/git/context.d.ts +11 -0
- package/dist/git/context.d.ts.map +1 -1
- package/dist/git/context.js.map +1 -1
- package/dist/git/models/fileStatus.d.ts +1 -0
- package/dist/git/models/fileStatus.d.ts.map +1 -1
- package/dist/git/models/fileStatus.js +1 -0
- package/dist/git/models/fileStatus.js.map +1 -1
- package/dist/git/models/issue.d.ts +4 -0
- package/dist/git/models/issue.d.ts.map +1 -1
- package/dist/git/models/mergeConflicts.d.ts +2 -0
- package/dist/git/models/mergeConflicts.d.ts.map +1 -1
- package/dist/git/models/pullRequest.d.ts +38 -2
- package/dist/git/models/pullRequest.d.ts.map +1 -1
- package/dist/git/models/pullRequest.js +12 -1
- package/dist/git/models/pullRequest.js.map +1 -1
- package/dist/git/models/repository.d.ts +32 -5
- package/dist/git/models/repository.d.ts.map +1 -1
- package/dist/git/models/repository.js +58 -12
- package/dist/git/models/repository.js.map +1 -1
- package/dist/git/models/statusFile.d.ts.map +1 -1
- package/dist/git/models/statusFile.js +3 -0
- package/dist/git/models/statusFile.js.map +1 -1
- package/dist/git/providers/branches.d.ts +2 -0
- package/dist/git/providers/branches.d.ts.map +1 -1
- package/dist/git/providers/commits.d.ts +8 -0
- package/dist/git/providers/commits.d.ts.map +1 -1
- package/dist/git/providers/config.d.ts +19 -1
- package/dist/git/providers/config.d.ts.map +1 -1
- package/dist/git/providers/operations.d.ts +1 -0
- package/dist/git/providers/operations.d.ts.map +1 -1
- package/dist/git/providers/pausedOperations.d.ts +2 -0
- package/dist/git/providers/pausedOperations.d.ts.map +1 -1
- package/dist/git/providers/status.d.ts +1 -0
- package/dist/git/providers/status.d.ts.map +1 -1
- package/dist/git/utils/config.utils.d.ts +3 -0
- package/dist/git/utils/config.utils.d.ts.map +1 -0
- package/dist/git/utils/config.utils.js +8 -0
- package/dist/git/utils/config.utils.js.map +1 -0
- package/dist/git/utils/issue.utils.d.ts.map +1 -1
- package/dist/git/utils/issue.utils.js +2 -0
- package/dist/git/utils/issue.utils.js.map +1 -1
- package/dist/git/utils/pausedOperationStatus.utils.d.ts +14 -0
- package/dist/git/utils/pausedOperationStatus.utils.d.ts.map +1 -1
- package/dist/git/utils/pausedOperationStatus.utils.js +23 -0
- package/dist/git/utils/pausedOperationStatus.utils.js.map +1 -1
- package/dist/git/utils/pullRequest.utils.d.ts +14 -1
- package/dist/git/utils/pullRequest.utils.d.ts.map +1 -1
- package/dist/git/utils/pullRequest.utils.js +31 -0
- package/dist/git/utils/pullRequest.utils.js.map +1 -1
- package/dist/git/watching/changeEvent.d.ts +6 -1
- package/dist/git/watching/changeEvent.d.ts.map +1 -1
- package/dist/git/watching/classifyChange.d.ts.map +1 -1
- package/dist/git/watching/classifyChange.js +14 -1
- package/dist/git/watching/classifyChange.js.map +1 -1
- package/dist/git/watching/watchService.d.ts.map +1 -1
- package/dist/git/watching/watchService.js +18 -3
- package/dist/git/watching/watchService.js.map +1 -1
- package/dist/git/watching/watchSession.d.ts +8 -1
- package/dist/git/watching/watchSession.d.ts.map +1 -1
- package/dist/git/watching/watchSession.js +38 -6
- package/dist/git/watching/watchSession.js.map +1 -1
- package/dist/git/watching/watcherPatterns.d.ts +2 -2
- package/dist/git/watching/watcherPatterns.d.ts.map +1 -1
- package/dist/git/watching/watcherPatterns.js +1 -1
- package/dist/git/watching/watcherPatterns.js.map +1 -1
- package/dist/git-cli/exec/git.d.ts +1 -1
- package/dist/git-cli/exec/git.d.ts.map +1 -1
- package/dist/git-cli/exec/git.js +13 -3
- package/dist/git-cli/exec/git.js.map +1 -1
- package/dist/git-cli/providers/branches.d.ts +19 -0
- package/dist/git-cli/providers/branches.d.ts.map +1 -1
- package/dist/git-cli/providers/branches.js +282 -78
- 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 +84 -11
- package/dist/git-cli/providers/commits.js.map +1 -1
- package/dist/git-cli/providers/config.d.ts +42 -0
- package/dist/git-cli/providers/config.d.ts.map +1 -1
- package/dist/git-cli/providers/config.js +186 -5
- 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 +1 -1
- package/dist/git-cli/providers/contributors.js.map +1 -1
- package/dist/git-cli/providers/graph.d.ts +2 -2
- package/dist/git-cli/providers/graph.d.ts.map +1 -1
- package/dist/git-cli/providers/graph.js +27 -9
- package/dist/git-cli/providers/graph.js.map +1 -1
- package/dist/git-cli/providers/operations.d.ts.map +1 -1
- package/dist/git-cli/providers/operations.js +40 -12
- package/dist/git-cli/providers/operations.js.map +1 -1
- package/dist/git-cli/providers/pausedOperations.d.ts +5 -0
- package/dist/git-cli/providers/pausedOperations.d.ts.map +1 -1
- package/dist/git-cli/providers/pausedOperations.js +87 -16
- package/dist/git-cli/providers/pausedOperations.js.map +1 -1
- package/dist/git-cli/providers/refs.d.ts +5 -3
- package/dist/git-cli/providers/refs.d.ts.map +1 -1
- package/dist/git-cli/providers/refs.js +33 -4
- package/dist/git-cli/providers/refs.js.map +1 -1
- package/dist/git-cli/providers/status.d.ts +1 -0
- package/dist/git-cli/providers/status.d.ts.map +1 -1
- package/dist/git-cli/providers/status.js +18 -3
- package/dist/git-cli/providers/status.js.map +1 -1
- package/dist/git-cli/providers/worktrees.d.ts.map +1 -1
- package/dist/git-cli/providers/worktrees.js +4 -0
- package/dist/git-cli/providers/worktrees.js.map +1 -1
- package/dist/git-cli/service.d.ts.map +1 -1
- package/dist/git-cli/service.js.map +1 -1
- package/dist/plus/agents/providers/claudeCodeProvider.d.ts +39 -1
- package/dist/plus/agents/providers/claudeCodeProvider.d.ts.map +1 -1
- package/dist/plus/agents/providers/claudeCodeProvider.js +978 -71
- package/dist/plus/agents/providers/claudeCodeProvider.js.map +1 -1
- package/dist/plus/agents/providers/claudeCodeTranscript.d.ts +24 -2
- package/dist/plus/agents/providers/claudeCodeTranscript.d.ts.map +1 -1
- package/dist/plus/agents/providers/claudeCodeTranscript.js +84 -9
- package/dist/plus/agents/providers/claudeCodeTranscript.js.map +1 -1
- package/dist/plus/agents/types.d.ts +37 -2
- package/dist/plus/agents/types.d.ts.map +1 -1
- package/dist/plus/agents/types.js +2 -0
- package/dist/plus/agents/types.js.map +1 -1
- package/dist/plus/ai/errors.d.ts +10 -0
- package/dist/plus/ai/errors.d.ts.map +1 -1
- package/dist/plus/ai/errors.js +29 -0
- package/dist/plus/ai/errors.js.map +1 -1
- package/dist/plus/ai/models/model.d.ts +4 -0
- package/dist/plus/ai/models/model.d.ts.map +1 -1
- package/dist/plus/ai/models/model.js +51 -6
- package/dist/plus/ai/models/model.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/models/provider.d.ts +60 -2
- package/dist/plus/ai/models/provider.d.ts.map +1 -1
- package/dist/plus/ai/prompts.d.ts +12 -0
- package/dist/plus/ai/prompts.d.ts.map +1 -1
- package/dist/plus/ai/prompts.js +177 -61
- package/dist/plus/ai/prompts.js.map +1 -1
- package/dist/plus/ai/providers/anthropicProvider.d.ts +10 -2
- package/dist/plus/ai/providers/anthropicProvider.d.ts.map +1 -1
- package/dist/plus/ai/providers/anthropicProvider.js +139 -46
- package/dist/plus/ai/providers/anthropicProvider.js.map +1 -1
- package/dist/plus/ai/providers/deepSeekProvider.d.ts +2 -0
- package/dist/plus/ai/providers/deepSeekProvider.d.ts.map +1 -1
- package/dist/plus/ai/providers/deepSeekProvider.js +10 -21
- package/dist/plus/ai/providers/deepSeekProvider.js.map +1 -1
- package/dist/plus/ai/providers/geminiProvider.d.ts +1 -0
- package/dist/plus/ai/providers/geminiProvider.d.ts.map +1 -1
- package/dist/plus/ai/providers/geminiProvider.js +19 -0
- package/dist/plus/ai/providers/geminiProvider.js.map +1 -1
- package/dist/plus/ai/providers/gitkrakenProvider.d.ts +5 -1
- package/dist/plus/ai/providers/gitkrakenProvider.d.ts.map +1 -1
- package/dist/plus/ai/providers/gitkrakenProvider.js +118 -3
- package/dist/plus/ai/providers/gitkrakenProvider.js.map +1 -1
- package/dist/plus/ai/providers/huggingFaceProvider.d.ts +1 -0
- package/dist/plus/ai/providers/huggingFaceProvider.d.ts.map +1 -1
- package/dist/plus/ai/providers/huggingFaceProvider.js +5 -0
- package/dist/plus/ai/providers/huggingFaceProvider.js.map +1 -1
- package/dist/plus/ai/providers/mistralProvider.d.ts +3 -1
- package/dist/plus/ai/providers/mistralProvider.d.ts.map +1 -1
- package/dist/plus/ai/providers/mistralProvider.js +99 -19
- package/dist/plus/ai/providers/mistralProvider.js.map +1 -1
- package/dist/plus/ai/providers/ollamaProvider.d.ts +4 -3
- package/dist/plus/ai/providers/ollamaProvider.d.ts.map +1 -1
- package/dist/plus/ai/providers/ollamaProvider.js +38 -3
- package/dist/plus/ai/providers/ollamaProvider.js.map +1 -1
- package/dist/plus/ai/providers/openAICompatibleProvider.d.ts +1 -0
- package/dist/plus/ai/providers/openAICompatibleProvider.d.ts.map +1 -1
- package/dist/plus/ai/providers/openAICompatibleProvider.js +4 -0
- package/dist/plus/ai/providers/openAICompatibleProvider.js.map +1 -1
- package/dist/plus/ai/providers/openAICompatibleProviderBase.d.ts +154 -5
- package/dist/plus/ai/providers/openAICompatibleProviderBase.d.ts.map +1 -1
- package/dist/plus/ai/providers/openAICompatibleProviderBase.js +259 -10
- package/dist/plus/ai/providers/openAICompatibleProviderBase.js.map +1 -1
- package/dist/plus/ai/providers/openRouterProvider.d.ts +4 -0
- package/dist/plus/ai/providers/openRouterProvider.d.ts.map +1 -1
- package/dist/plus/ai/providers/openRouterProvider.js +9 -0
- package/dist/plus/ai/providers/openRouterProvider.js.map +1 -1
- package/dist/plus/ai/providers/openaiProvider.d.ts +1 -0
- package/dist/plus/ai/providers/openaiProvider.d.ts.map +1 -1
- package/dist/plus/ai/providers/openaiProvider.js +1 -0
- package/dist/plus/ai/providers/openaiProvider.js.map +1 -1
- package/dist/plus/ai/providers/responseFormatCache.d.ts +8 -0
- package/dist/plus/ai/providers/responseFormatCache.d.ts.map +1 -0
- package/dist/plus/ai/providers/responseFormatCache.js +25 -0
- package/dist/plus/ai/providers/responseFormatCache.js.map +1 -0
- package/dist/plus/ai/providers/xaiProvider.d.ts +1 -0
- package/dist/plus/ai/providers/xaiProvider.d.ts.map +1 -1
- package/dist/plus/ai/providers/xaiProvider.js +10 -1
- package/dist/plus/ai/providers/xaiProvider.js.map +1 -1
- package/dist/plus/ai/utils/ai.utils.d.ts +3 -0
- package/dist/plus/ai/utils/ai.utils.d.ts.map +1 -1
- package/dist/plus/ai/utils/ai.utils.js +13 -0
- package/dist/plus/ai/utils/ai.utils.js.map +1 -1
- package/dist/plus/ai/utils/results.utils.d.ts +16 -2
- package/dist/plus/ai/utils/results.utils.d.ts.map +1 -1
- package/dist/plus/ai/utils/results.utils.js +204 -28
- package/dist/plus/ai/utils/results.utils.js.map +1 -1
- package/dist/plus/git-github/api/github.d.ts +55 -0
- package/dist/plus/git-github/api/github.d.ts.map +1 -1
- package/dist/plus/git-github/api/github.js +177 -4
- package/dist/plus/git-github/api/github.js.map +1 -1
- package/dist/plus/git-github/models.d.ts +16 -0
- package/dist/plus/git-github/models.d.ts.map +1 -1
- package/dist/plus/git-github/models.js +29 -3
- package/dist/plus/git-github/models.js.map +1 -1
- package/dist/plus/git-github/providers/github/status.d.ts +1 -0
- package/dist/plus/git-github/providers/github/status.d.ts.map +1 -1
- package/dist/plus/git-github/providers/github/status.js.map +1 -1
- package/dist/plus/integrations/context.d.ts +4 -0
- package/dist/plus/integrations/context.d.ts.map +1 -1
- package/dist/plus/integrations/index.d.ts.map +1 -1
- package/dist/plus/integrations/index.js +2 -0
- package/dist/plus/integrations/index.js.map +1 -1
- package/dist/plus/integrations/models/gitHostIntegration.d.ts +6 -3
- package/dist/plus/integrations/models/gitHostIntegration.d.ts.map +1 -1
- package/dist/plus/integrations/models/gitHostIntegration.js +12 -2
- package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
- package/dist/plus/integrations/models/integration.d.ts +3 -1
- package/dist/plus/integrations/models/integration.d.ts.map +1 -1
- package/dist/plus/integrations/models/integration.js +2 -2
- package/dist/plus/integrations/models/integration.js.map +1 -1
- package/dist/plus/integrations/providers/accounts.d.ts.map +1 -1
- package/dist/plus/integrations/providers/accounts.js +4 -0
- package/dist/plus/integrations/providers/accounts.js.map +1 -1
- package/dist/plus/integrations/providers/azure/models.d.ts.map +1 -1
- package/dist/plus/integrations/providers/azure/models.js +3 -0
- package/dist/plus/integrations/providers/azure/models.js.map +1 -1
- package/dist/plus/integrations/providers/bitbucket/models.d.ts.map +1 -1
- package/dist/plus/integrations/providers/bitbucket/models.js +1 -0
- package/dist/plus/integrations/providers/bitbucket/models.js.map +1 -1
- package/dist/plus/integrations/providers/github.d.ts +14 -2
- package/dist/plus/integrations/providers/github.d.ts.map +1 -1
- package/dist/plus/integrations/providers/github.js +55 -3
- package/dist/plus/integrations/providers/github.js.map +1 -1
- package/dist/plus/integrations/providers/gitlab.d.ts.map +1 -1
- package/dist/plus/integrations/providers/gitlab.js.map +1 -1
- package/dist/plus/integrations/providers/models.d.ts.map +1 -1
- package/dist/plus/integrations/providers/models.js +30 -4
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/reads/broaden.d.ts.map +1 -1
- package/dist/plus/integrations/reads/broaden.js +6 -1
- package/dist/plus/integrations/reads/broaden.js.map +1 -1
- package/dist/plus/integrations/reads/drains.d.ts.map +1 -1
- package/dist/plus/integrations/reads/drains.js +7 -4
- package/dist/plus/integrations/reads/drains.js.map +1 -1
- package/dist/plus/integrations/reads/hierarchy.d.ts.map +1 -1
- package/dist/plus/integrations/reads/hierarchy.js +3 -1
- package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
- package/dist/plus/integrations/reads/issueTracker.d.ts.map +1 -1
- package/dist/plus/integrations/reads/issueTracker.js +8 -3
- package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
- package/dist/plus/integrations/reads/issues.d.ts.map +1 -1
- package/dist/plus/integrations/reads/issues.js +9 -4
- package/dist/plus/integrations/reads/issues.js.map +1 -1
- package/dist/plus/integrations/reads/pullRequests.d.ts.map +1 -1
- package/dist/plus/integrations/reads/pullRequests.js +9 -4
- package/dist/plus/integrations/reads/pullRequests.js.map +1 -1
- package/dist/utils/env/browser/base64.d.ts +1 -1
- package/dist/utils/env/browser/base64.d.ts.map +1 -1
- package/dist/utils/env/node/base64.d.ts +1 -1
- package/dist/utils/env/node/base64.d.ts.map +1 -1
- package/dist/utils/env/node/base64.js +2 -0
- package/dist/utils/env/node/base64.js.map +1 -1
- package/dist/utils/keys/chord.d.ts +52 -0
- package/dist/utils/keys/chord.d.ts.map +1 -0
- package/dist/utils/keys/chord.js +135 -0
- package/dist/utils/keys/chord.js.map +1 -0
- package/dist/utils/keys/keybinding.d.ts +53 -0
- package/dist/utils/keys/keybinding.d.ts.map +1 -0
- package/dist/utils/keys/keybinding.js +58 -0
- package/dist/utils/keys/keybinding.js.map +1 -0
- package/dist/utils/path.d.ts +11 -0
- package/dist/utils/path.d.ts.map +1 -1
- package/dist/utils/path.js +13 -0
- package/dist/utils/path.js.map +1 -1
- package/package.json +13 -5
- package/src/git/cache.ts +383 -47
- package/src/git/context.ts +11 -0
- package/src/git/models/fileStatus.ts +1 -0
- package/src/git/models/issue.ts +4 -0
- package/src/git/models/mergeConflicts.ts +3 -2
- package/src/git/models/pullRequest.ts +33 -1
- package/src/git/models/repository.ts +58 -17
- package/src/git/models/statusFile.ts +3 -0
- package/src/git/providers/branches.ts +6 -0
- package/src/git/providers/commits.ts +11 -0
- package/src/git/providers/config.ts +18 -0
- package/src/git/providers/operations.ts +1 -0
- package/src/git/providers/pausedOperations.ts +2 -0
- package/src/git/providers/status.ts +1 -0
- package/src/git/utils/config.utils.ts +8 -0
- package/src/git/utils/issue.utils.ts +2 -0
- package/src/git/utils/pausedOperationStatus.utils.ts +28 -0
- package/src/git/utils/pullRequest.utils.ts +36 -0
- package/src/git/watching/changeEvent.ts +6 -1
- package/src/git/watching/classifyChange.ts +15 -1
- package/src/git/watching/watchService.ts +20 -3
- package/src/git/watching/watchSession.ts +41 -6
- package/src/git/watching/watcherPatterns.ts +1 -1
- package/src/git-cli/exec/git.ts +13 -3
- package/src/git-cli/providers/branches.ts +384 -132
- package/src/git-cli/providers/commits.ts +106 -15
- package/src/git-cli/providers/config.ts +229 -8
- package/src/git-cli/providers/contributors.ts +5 -1
- package/src/git-cli/providers/graph.ts +26 -13
- package/src/git-cli/providers/operations.ts +45 -20
- package/src/git-cli/providers/pausedOperations.ts +109 -17
- package/src/git-cli/providers/refs.ts +33 -3
- package/src/git-cli/providers/status.ts +25 -4
- package/src/git-cli/providers/worktrees.ts +5 -0
- package/src/git-cli/service.ts +0 -1
- package/src/plus/agents/providers/claudeCodeProvider.ts +1130 -87
- package/src/plus/agents/providers/claudeCodeTranscript.ts +95 -9
- package/src/plus/agents/types.ts +46 -2
- package/src/plus/ai/errors.ts +30 -0
- package/src/plus/ai/models/model.ts +56 -6
- package/src/plus/ai/models/promptTemplates.ts +1 -1
- package/src/plus/ai/models/provider.ts +69 -2
- package/src/plus/ai/prompts.ts +186 -61
- package/src/plus/ai/providers/anthropicProvider.ts +155 -47
- package/src/plus/ai/providers/deepSeekProvider.ts +11 -21
- package/src/plus/ai/providers/geminiProvider.ts +19 -0
- package/src/plus/ai/providers/gitkrakenProvider.ts +126 -3
- package/src/plus/ai/providers/huggingFaceProvider.ts +6 -0
- package/src/plus/ai/providers/mistralProvider.ts +102 -19
- package/src/plus/ai/providers/ollamaProvider.ts +55 -4
- package/src/plus/ai/providers/openAICompatibleProvider.ts +5 -0
- package/src/plus/ai/providers/openAICompatibleProviderBase.ts +360 -17
- package/src/plus/ai/providers/openRouterProvider.ts +17 -0
- package/src/plus/ai/providers/openaiProvider.ts +1 -0
- package/src/plus/ai/providers/responseFormatCache.ts +39 -0
- package/src/plus/ai/providers/xaiProvider.ts +10 -1
- package/src/plus/ai/utils/ai.utils.ts +14 -0
- package/src/plus/ai/utils/results.utils.ts +255 -31
- package/src/plus/git-github/api/github.ts +291 -7
- package/src/plus/git-github/models.ts +48 -2
- package/src/plus/git-github/providers/github/status.ts +1 -0
- package/src/plus/integrations/context.ts +4 -0
- package/src/plus/integrations/index.ts +2 -0
- package/src/plus/integrations/models/gitHostIntegration.ts +25 -2
- package/src/plus/integrations/models/integration.ts +23 -13
- package/src/plus/integrations/providers/accounts.ts +4 -0
- package/src/plus/integrations/providers/azure/models.ts +3 -0
- package/src/plus/integrations/providers/bitbucket/models.ts +1 -0
- package/src/plus/integrations/providers/github.ts +85 -1
- package/src/plus/integrations/providers/gitlab.ts +2 -10
- package/src/plus/integrations/providers/models.ts +36 -4
- package/src/plus/integrations/reads/broaden.ts +6 -1
- package/src/plus/integrations/reads/drains.ts +7 -4
- package/src/plus/integrations/reads/hierarchy.ts +3 -1
- package/src/plus/integrations/reads/issueTracker.ts +9 -3
- package/src/plus/integrations/reads/issues.ts +9 -4
- package/src/plus/integrations/reads/pullRequests.ts +9 -4
- package/src/utils/env/browser/base64.ts +1 -1
- package/src/utils/env/node/base64.ts +3 -1
- package/src/utils/keys/chord.ts +184 -0
- package/src/utils/keys/keybinding.ts +116 -0
- package/src/utils/path.ts +14 -0
package/src/git/cache.ts
CHANGED
|
@@ -61,7 +61,26 @@ export type CachedGitTypes =
|
|
|
61
61
|
| 'tracking'
|
|
62
62
|
| 'worktrees';
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
/**
|
|
65
|
+
* What a gk-config reconcile did, so `Cache` knows whether the coarse fallback is still owed.
|
|
66
|
+
*
|
|
67
|
+
* `'deferred'` is NOT a failure — a pass already in flight folds the change into its trailing pass — and
|
|
68
|
+
* must not trigger the fallback, or a burst of gk writes would coarse-clear on every one, which is exactly
|
|
69
|
+
* the churn the key-aware reconcile replaced.
|
|
70
|
+
*/
|
|
71
|
+
export type GkReconcileOutcome = 'reconciled' | 'deferred' | 'failed';
|
|
72
|
+
|
|
73
|
+
export type ConflictDetectionCacheKey =
|
|
74
|
+
| `apply:${string}:${string}:${string}`
|
|
75
|
+
| `merge:${string}:${string}`
|
|
76
|
+
| `reapply:${string}:${string}`;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Ceiling on how long a derived branch base is trusted. Long enough that repeated reads during normal use
|
|
80
|
+
* are served from cache, short enough that a base that changed by a route with no eviction signal (an
|
|
81
|
+
* external delete-and-recreate of a branch that is never checked out) self-heals without a reload.
|
|
82
|
+
*/
|
|
83
|
+
const baseBranchNameTTL = 5 * 60 * 1000; // 5 minutes
|
|
65
84
|
|
|
66
85
|
/**
|
|
67
86
|
* gkConfig keys that participate in the `branchOverviews` cache's mergeTarget/mergeBase lineage.
|
|
@@ -215,6 +234,29 @@ export class Cache implements Disposable {
|
|
|
215
234
|
private _worktreesByCommonPath = new Map<string, Set<RepoPath>>();
|
|
216
235
|
/** Monotonic per-worktree "status clock" — see {@link getStatusGeneration}. */
|
|
217
236
|
private _statusGenerations = new Map<RepoPath, number>();
|
|
237
|
+
/** Monotonic per-path "close clock" — see {@link getCloseGeneration}. */
|
|
238
|
+
private _closeGenerations = new Map<RepoPath, number>();
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Per-commonPath snapshot of the merge-relevant subset of `.git/gk/config` (keys matching
|
|
242
|
+
* {@link branchOverviewGkConfigKeysRegex}), used by {@link reconcileGkConfigMap} to diff a watcher
|
|
243
|
+
* `'gkConfig'` event down to only the keys that actually changed, instead of coarsely re-deriving
|
|
244
|
+
* `baseBranchName`/`branchOverviews` on every gk write. Absent means "never observed".
|
|
245
|
+
*/
|
|
246
|
+
private _gkConfigMergeSnapshots = new Map<string, Map<string, string>>();
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Reconciler registered by the CLI config sub-provider (see {@link setGkConfigReconciler}),
|
|
250
|
+
* invoked on a watcher-observed `'gkConfig'` change instead of the coarse `clearCaches(repoPath,
|
|
251
|
+
* 'gkConfig')` cascade. `undefined` (e.g. the GitHub provider, which has no `.git/gk/config`)
|
|
252
|
+
* falls back to the coarse clear.
|
|
253
|
+
*/
|
|
254
|
+
private _gkConfigReconciler:
|
|
255
|
+
| ((
|
|
256
|
+
repoPath: string,
|
|
257
|
+
priorSnapshot: ReadonlyMap<string, string> | undefined,
|
|
258
|
+
) => PromiseOrValue<GkReconcileOutcome>)
|
|
259
|
+
| undefined;
|
|
218
260
|
|
|
219
261
|
[Symbol.dispose](): void {
|
|
220
262
|
this.dispose();
|
|
@@ -526,9 +568,25 @@ export class Cache implements Disposable {
|
|
|
526
568
|
keysToClear.add('resolvedRevisions');
|
|
527
569
|
}
|
|
528
570
|
|
|
529
|
-
// Shared branch caches (branch list, metadata)
|
|
530
|
-
|
|
571
|
+
// Shared branch caches (branch list, metadata). `baseBranchName` is deliberately NOT cleared
|
|
572
|
+
// here — a branch's base is set at creation and only changes when its stored key changes
|
|
573
|
+
// (`gkConfig`/`config` below) or the branch itself is created/renamed/deleted. Every op that
|
|
574
|
+
// does one of those calls `deleteBaseBranchName` and moves or drops the branch's persisted
|
|
575
|
+
// `branch.<ref>.gk-*` keys: `createBranch`/`deleteLocalBranch`/`renameBranch`, plus the two
|
|
576
|
+
// paths that create a branch as a side effect (`checkout -b`, `worktree add -b`). Tip movement
|
|
577
|
+
// can't change a base, so clearing on every commit only forced a `git reflog` re-derivation.
|
|
578
|
+
if (types.includes('branch')) {
|
|
579
|
+
// `'branch'` comes from a HEAD change — a checkout — which appends `checkout: moving from X
|
|
580
|
+
// to Y` to the reflog, exactly the entry `getBaseBranchName` greps for when nothing is
|
|
581
|
+
// stored. A base derived as "none" BEFORE that entry existed is cached with no TTL, so
|
|
582
|
+
// without this it stays "none" for the session even though the answer now exists.
|
|
583
|
+
//
|
|
584
|
+
// Deliberately not under `'branches'`: a tip move can't change a branch's base, so re-deriving
|
|
585
|
+
// on every commit would be pure cost. Checkouts are user-driven and rare by comparison.
|
|
531
586
|
keysToClear.add('baseBranchName');
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
if (types.includes('branches')) {
|
|
532
590
|
keysToClear.add('branchMetadataMap');
|
|
533
591
|
keysToClear.add('branches');
|
|
534
592
|
keysToClear.add('sharedBranches');
|
|
@@ -540,6 +598,10 @@ export class Cache implements Disposable {
|
|
|
540
598
|
}
|
|
541
599
|
|
|
542
600
|
if (types.includes('config')) {
|
|
601
|
+
// `getBaseBranchName` falls back to `branch.<ref>.vscode-merge-base` (written by VS Code's
|
|
602
|
+
// built-in Git), which lives in `.git/config` — so a config change is the only signal that
|
|
603
|
+
// source changed. Cheap: config events are rare.
|
|
604
|
+
keysToClear.add('baseBranchName');
|
|
543
605
|
keysToClear.add('configKeys');
|
|
544
606
|
keysToClear.add('configPatterns');
|
|
545
607
|
keysToClear.add('currentBranchReference');
|
|
@@ -601,6 +663,11 @@ export class Cache implements Disposable {
|
|
|
601
663
|
keysToClear.add('tags');
|
|
602
664
|
keysToClear.add('refs');
|
|
603
665
|
keysToClear.add('refTips');
|
|
666
|
+
// `commit`/`commitCount` can be keyed by a tag name (e.g. the tag node's load-more count),
|
|
667
|
+
// which re-points on a force-moved or deleted-and-recreated tag — same drift the
|
|
668
|
+
// `branch`/`branches` cascade above clears for.
|
|
669
|
+
keysToClear.add('commit');
|
|
670
|
+
keysToClear.add('commitCount');
|
|
604
671
|
}
|
|
605
672
|
|
|
606
673
|
if (types.includes('tracking')) {
|
|
@@ -625,35 +692,44 @@ export class Cache implements Disposable {
|
|
|
625
692
|
if (repoPath == null) {
|
|
626
693
|
cache.clear();
|
|
627
694
|
} else if (sharedCacheKeys.has(key)) {
|
|
628
|
-
|
|
629
|
-
// For shared caches, prefer `invalidate` where supported so in-flight work is
|
|
630
|
-
// shared across new callers and self-evicts on settle rather than spawning a
|
|
631
|
-
// duplicate factory. `invalidate` does the right thing per-entry: entries with
|
|
632
|
-
// a `CacheController` (created via `getOrCreate`) are marked invalidated; entries
|
|
633
|
-
// without one (created via plain `.set()`, e.g. per-worktree mapper results) are
|
|
634
|
-
// hard-deleted. Caches that don't support `invalidate` fall back to `delete`.
|
|
635
|
-
const invalidate = (cache as { invalidate?: (k: string) => void }).invalidate;
|
|
636
|
-
if (typeof invalidate === 'function') {
|
|
637
|
-
invalidate.call(cache, commonPath);
|
|
638
|
-
for (const worktreePath of this.getWorktreePaths(commonPath)) {
|
|
639
|
-
if (worktreePath === commonPath) continue;
|
|
640
|
-
|
|
641
|
-
invalidate.call(cache, worktreePath);
|
|
642
|
-
}
|
|
643
|
-
} else {
|
|
644
|
-
cache.delete(commonPath);
|
|
645
|
-
for (const worktreePath of this.getWorktreePaths(commonPath)) {
|
|
646
|
-
if (worktreePath === commonPath) continue;
|
|
647
|
-
|
|
648
|
-
cache.delete(worktreePath);
|
|
649
|
-
}
|
|
650
|
-
}
|
|
695
|
+
this.evictShared(key, repoPath);
|
|
651
696
|
} else {
|
|
652
697
|
cache.delete(repoPath);
|
|
653
698
|
}
|
|
654
699
|
}
|
|
655
700
|
}
|
|
656
701
|
|
|
702
|
+
/**
|
|
703
|
+
* Evicts a single shared (commonPath-keyed) cache entry for `repoPath` and its sibling worktrees.
|
|
704
|
+
* Prefers `invalidate` where supported so in-flight work is shared across new callers and
|
|
705
|
+
* self-evicts on settle rather than spawning a duplicate factory. `invalidate` does the right
|
|
706
|
+
* thing per-entry: entries with a `CacheController` (created via `getOrCreate`) are marked
|
|
707
|
+
* invalidated; entries without one (created via plain `.set()`, e.g. per-worktree mapper results)
|
|
708
|
+
* are hard-deleted. Caches that don't support `invalidate` fall back to `delete`.
|
|
709
|
+
*/
|
|
710
|
+
private evictShared(key: keyof AllCaches, repoPath: string): void {
|
|
711
|
+
const cache = this._caches[key];
|
|
712
|
+
if (cache == null) return;
|
|
713
|
+
|
|
714
|
+
const commonPath = this.getCommonPath(repoPath);
|
|
715
|
+
const invalidate = (cache as { invalidate?: (k: string) => void }).invalidate;
|
|
716
|
+
if (typeof invalidate === 'function') {
|
|
717
|
+
invalidate.call(cache, commonPath);
|
|
718
|
+
for (const worktreePath of this.getWorktreePaths(commonPath)) {
|
|
719
|
+
if (worktreePath === commonPath) continue;
|
|
720
|
+
|
|
721
|
+
invalidate.call(cache, worktreePath);
|
|
722
|
+
}
|
|
723
|
+
} else {
|
|
724
|
+
cache.delete(commonPath);
|
|
725
|
+
for (const worktreePath of this.getWorktreePaths(commonPath)) {
|
|
726
|
+
if (worktreePath === commonPath) continue;
|
|
727
|
+
|
|
728
|
+
cache.delete(worktreePath);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
657
733
|
/**
|
|
658
734
|
* Monotonic per-worktree counter, advanced whenever anything that can change `git status` output is
|
|
659
735
|
* observed. `git status` and its siblings are point-in-time reads of mutable state, so deduplicating them
|
|
@@ -714,6 +790,22 @@ export class Cache implements Disposable {
|
|
|
714
790
|
return commonPath != null && commonPath !== repoPath;
|
|
715
791
|
}
|
|
716
792
|
|
|
793
|
+
/** Whether `repoPath` is currently registered (via {@link registerRepoPath}, not yet unregistered). */
|
|
794
|
+
isRegistered(repoPath: string): boolean {
|
|
795
|
+
return this._commonPathRegistry.has(repoPath);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* Monotonic per-path counter advanced whenever `repoPath` is unregistered (repo closed, worktree
|
|
800
|
+
* removed). Lets an async post-read step — e.g. the gk-config reconcile — detect that its target was
|
|
801
|
+
* closed (and possibly reopened) mid-flight and bail. Unlike an `isRegistered` check it doesn't
|
|
802
|
+
* require the caller to have registered in the first place, so it stays correct for consumers of
|
|
803
|
+
* this package that don't wire up the host's repo lifecycle.
|
|
804
|
+
*/
|
|
805
|
+
getCloseGeneration(repoPath: string): number {
|
|
806
|
+
return this._closeGenerations.get(repoPath) ?? 0;
|
|
807
|
+
}
|
|
808
|
+
|
|
717
809
|
/** Clears file-scoped caches (blame, diff, fileLog) for a specific path within a repo */
|
|
718
810
|
clearForPath(repoPath: string, path: string, ...types: UriScopedCachedGitTypes[]): void {
|
|
719
811
|
const prefix = `${normalizePath(path)}:`;
|
|
@@ -765,6 +857,9 @@ export class Cache implements Disposable {
|
|
|
765
857
|
// Advance the status clock on close so a status read still in flight from this registration can't be
|
|
766
858
|
// joined by a read issued after the path is reopened (which would otherwise reuse the same generation).
|
|
767
859
|
this.incrementStatusGeneration(repoPath);
|
|
860
|
+
// Advance the close clock so an async step that captured it before its read (see `getCloseGeneration`)
|
|
861
|
+
// can tell its target went away mid-flight.
|
|
862
|
+
this._closeGenerations.set(repoPath, this.getCloseGeneration(repoPath) + 1);
|
|
768
863
|
|
|
769
864
|
const commonPath = this._commonPathRegistry.get(repoPath) ?? repoPath;
|
|
770
865
|
const worktrees = this._worktreesByCommonPath.get(commonPath);
|
|
@@ -799,6 +894,11 @@ export class Cache implements Disposable {
|
|
|
799
894
|
}
|
|
800
895
|
}
|
|
801
896
|
|
|
897
|
+
// Shared, commonPath-keyed like `gkConfigMap` — only evict once no worktree still depends on it.
|
|
898
|
+
if (isLastWorktree) {
|
|
899
|
+
this._gkConfigMergeSnapshots.delete(commonPath);
|
|
900
|
+
}
|
|
901
|
+
|
|
802
902
|
this._commonPathRegistry.delete(repoPath);
|
|
803
903
|
|
|
804
904
|
if (worktrees != null) {
|
|
@@ -811,9 +911,20 @@ export class Cache implements Disposable {
|
|
|
811
911
|
|
|
812
912
|
@debug({ onlyExit: true })
|
|
813
913
|
reset(): void {
|
|
914
|
+
// Advance the close clock BEFORE dropping the registry it reads from. Advancing rather than
|
|
915
|
+
// clearing is the point: an async step that captured a generation must see a mismatch and bail,
|
|
916
|
+
// where a cleared map would hand it a fresh `0` that matches and let it repopulate what was just
|
|
917
|
+
// torn down. Union of registered paths and any already carrying a generation, mirroring
|
|
918
|
+
// `incrementStatusGenerations`.
|
|
919
|
+
for (const path of new Set([...this._commonPathRegistry.keys(), ...this._closeGenerations.keys()])) {
|
|
920
|
+
this._closeGenerations.set(path, this.getCloseGeneration(path) + 1);
|
|
921
|
+
}
|
|
922
|
+
|
|
814
923
|
this._commonPathRegistry.clear();
|
|
815
924
|
this._worktreesByCommonPath.clear();
|
|
816
925
|
this._statusGenerations.clear();
|
|
926
|
+
this._gkConfigMergeSnapshots.clear();
|
|
927
|
+
this._gkConfigReconciler = undefined;
|
|
817
928
|
this._caches = createEmptyCaches();
|
|
818
929
|
}
|
|
819
930
|
|
|
@@ -874,7 +985,10 @@ export class Cache implements Disposable {
|
|
|
874
985
|
}
|
|
875
986
|
|
|
876
987
|
if (hasAny('gkConfig')) {
|
|
877
|
-
types
|
|
988
|
+
// Handled separately (not folded into `types`/`clearCaches` below) — a gk write is usually a
|
|
989
|
+
// bookkeeping timestamp that can't affect `baseBranchName`/`branchOverviews`, so this reconciles
|
|
990
|
+
// down to only the keys that actually changed instead of coarsely clearing both on every write.
|
|
991
|
+
this.handleGkConfigChanged(repoPath);
|
|
878
992
|
}
|
|
879
993
|
|
|
880
994
|
if (hasAny('lastFetched')) {
|
|
@@ -907,6 +1021,55 @@ export class Cache implements Disposable {
|
|
|
907
1021
|
}
|
|
908
1022
|
}
|
|
909
1023
|
|
|
1024
|
+
/**
|
|
1025
|
+
* Handles a watcher-observed `'gkConfig'` change. Always evicts the cached bulk map (forcing the
|
|
1026
|
+
* next read to hit disk). If a reconciler is registered (see {@link setGkConfigReconciler}),
|
|
1027
|
+
* defers to it instead of coarsely clearing `baseBranchName`/`branchOverviews` — it re-reads the
|
|
1028
|
+
* map and diffs it against the snapshot captured here (i.e. as it stood right before this change),
|
|
1029
|
+
* cascading only the refs whose merge-relevant keys actually changed. Falls back to the coarse
|
|
1030
|
+
* clear when no reconciler is registered (e.g. the GitHub provider) so nothing can go stale.
|
|
1031
|
+
*/
|
|
1032
|
+
private handleGkConfigChanged(repoPath: string): void {
|
|
1033
|
+
const commonPath = this.getCommonPath(repoPath);
|
|
1034
|
+
// Hard-delete rather than `evictShared`'s soft-invalidate: the reconcile below needs a
|
|
1035
|
+
// point-in-time read of the post-change file, and a soft-invalidated entry stays joinable — the
|
|
1036
|
+
// re-read would ride a bulk read that started before this change and diff it as unchanged.
|
|
1037
|
+
// Same reasoning as `refs.ts`'s `force: true` path and `deleteGkConfig`'s own hard delete.
|
|
1038
|
+
this._caches.gkConfigMap?.delete(commonPath);
|
|
1039
|
+
|
|
1040
|
+
if (this._gkConfigReconciler == null) {
|
|
1041
|
+
this.clearCaches(repoPath, 'gkConfig');
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
const priorSnapshot = this._gkConfigMergeSnapshots.get(commonPath);
|
|
1046
|
+
// Fire-and-forget, but the outcome still decides whether the coarse clear is owed: a reconcile that
|
|
1047
|
+
// FAILED leaves every derived cache holding pre-change values with nothing else to correct them, so
|
|
1048
|
+
// it falls back to the blunt behavior that registering a reconciler replaced. Kept here rather than
|
|
1049
|
+
// in the reconciler so a bail path added later can't silently skip it — the return type makes each
|
|
1050
|
+
// one say what it did.
|
|
1051
|
+
void Promise.resolve(this._gkConfigReconciler(repoPath, priorSnapshot)).then(
|
|
1052
|
+
outcome => {
|
|
1053
|
+
if (outcome === 'failed') {
|
|
1054
|
+
this.clearCaches(repoPath, 'gkConfig');
|
|
1055
|
+
}
|
|
1056
|
+
},
|
|
1057
|
+
// An exception escaping the reconciler is a failure by definition — and one its own `catch`
|
|
1058
|
+
// never saw, so nothing else will have cascaded.
|
|
1059
|
+
() => this.clearCaches(repoPath, 'gkConfig'),
|
|
1060
|
+
);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
/** Hard-evicts the cached bulk `.git/gk/config` map so the next read hits disk. */
|
|
1064
|
+
deleteGkConfigMap(repoPath: string): void {
|
|
1065
|
+
this._caches.gkConfigMap?.delete(this.getCommonPath(repoPath));
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
/** The merge-relevant gk config snapshot currently serving as the reconcile baseline. */
|
|
1069
|
+
getGkConfigMergeSnapshot(repoPath: string): ReadonlyMap<string, string> | undefined {
|
|
1070
|
+
return this._gkConfigMergeSnapshots.get(this.getCommonPath(repoPath));
|
|
1071
|
+
}
|
|
1072
|
+
|
|
910
1073
|
getBranches(
|
|
911
1074
|
repoPath: string,
|
|
912
1075
|
factory: (
|
|
@@ -1013,10 +1176,38 @@ export class Cache implements Disposable {
|
|
|
1013
1176
|
/**
|
|
1014
1177
|
* The whole `.git/gk/config` is read once and cached as a single map per commonPath; per-key and
|
|
1015
1178
|
* per-namespace gk lookups are served from it in-memory (see `getGkConfigMap` in the CLI config
|
|
1016
|
-
* sub-provider). Invalidated by `deleteGkConfig` (write) and
|
|
1179
|
+
* sub-provider). Invalidated by `deleteGkConfig` (write) and, on a watcher `'gkConfig'` event, by
|
|
1180
|
+
* `handleGkConfigChanged` (reconciled precisely when a reconciler is registered, or the coarse
|
|
1181
|
+
* clear otherwise). The first fresh read seeds the merge-relevant snapshot used by
|
|
1182
|
+
* `reconcileGkConfigMap`; later refreshes of that snapshot are owned by the reconcile itself.
|
|
1017
1183
|
*/
|
|
1018
|
-
getGkConfigMap(
|
|
1019
|
-
|
|
1184
|
+
getGkConfigMap(
|
|
1185
|
+
repoPath: string,
|
|
1186
|
+
factory: (cacheable: CacheController) => PromiseOrValue<Map<string, string>>,
|
|
1187
|
+
): Promise<Map<string, string>> {
|
|
1188
|
+
return this.getSharedSimple(this.gkConfigMap, repoPath, async (commonPath, cacheable) => {
|
|
1189
|
+
const map = await factory(cacheable);
|
|
1190
|
+
// Seed the reconcile baseline on first observation only. Refreshes belong to
|
|
1191
|
+
// `reconcileGkConfigMap` (after it has cascaded) and `recordGkConfigWrite` (our own writes):
|
|
1192
|
+
// an incidental read that happens to land between an external write and the watcher event
|
|
1193
|
+
// would otherwise advance the baseline to the post-change value, so the reconcile would diff
|
|
1194
|
+
// that value against itself and silently drop the change.
|
|
1195
|
+
if (!this._gkConfigMergeSnapshots.has(commonPath)) {
|
|
1196
|
+
this._gkConfigMergeSnapshots.set(commonPath, this.extractMergeRelevantGkConfig(map));
|
|
1197
|
+
}
|
|
1198
|
+
return map;
|
|
1199
|
+
});
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
/** Extracts the subset of a gk config map whose keys participate in the `branchOverviews`/`baseBranchName` lineage. */
|
|
1203
|
+
private extractMergeRelevantGkConfig(map: ReadonlyMap<string, string>): Map<string, string> {
|
|
1204
|
+
const subset = new Map<string, string>();
|
|
1205
|
+
for (const [key, value] of map) {
|
|
1206
|
+
if (branchOverviewGkConfigKeysRegex.test(key)) {
|
|
1207
|
+
subset.set(key, value);
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
return subset;
|
|
1020
1211
|
}
|
|
1021
1212
|
|
|
1022
1213
|
/**
|
|
@@ -1035,20 +1226,106 @@ export class Cache implements Disposable {
|
|
|
1035
1226
|
key: string,
|
|
1036
1227
|
options?: { skipInvalidation?: readonly GkConfigInvalidationTarget[] },
|
|
1037
1228
|
): void {
|
|
1038
|
-
const
|
|
1039
|
-
this._caches.gkConfigMap?.delete(
|
|
1229
|
+
const commonPath = this.getCommonPath(repoPath);
|
|
1230
|
+
this._caches.gkConfigMap?.delete(commonPath);
|
|
1231
|
+
this.cascadeGkConfigKeyChange(commonPath, key, options?.skipInvalidation);
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
/**
|
|
1235
|
+
* Keeps the merge-relevant gk config snapshot in sync with OUR OWN writes, so the next
|
|
1236
|
+
* watcher-triggered `reconcileGkConfigMap` diffs a self-write as unchanged rather than as an
|
|
1237
|
+
* external change (which would otherwise defeat `skipInvalidation`). No-ops for keys outside the
|
|
1238
|
+
* merge-relevant subset, and if no snapshot exists yet for this repo — nothing to keep in sync;
|
|
1239
|
+
* the next real read (`getGkConfigMap`) populates one from scratch.
|
|
1240
|
+
*/
|
|
1241
|
+
recordGkConfigWrite(repoPath: string, key: string, value: string | undefined): void {
|
|
1242
|
+
if (!branchOverviewGkConfigKeysRegex.test(key)) return;
|
|
1243
|
+
|
|
1244
|
+
const commonPath = this.getCommonPath(repoPath);
|
|
1245
|
+
const snapshot = this._gkConfigMergeSnapshots.get(commonPath);
|
|
1246
|
+
if (snapshot == null) return;
|
|
1247
|
+
|
|
1248
|
+
// Copy-on-write: a reconcile in flight is holding this map as its `priorSnapshot` baseline, so
|
|
1249
|
+
// mutating it in place would silently move that baseline mid-diff.
|
|
1250
|
+
const next = new Map(snapshot);
|
|
1251
|
+
if (value == null) {
|
|
1252
|
+
next.delete(key);
|
|
1253
|
+
} else {
|
|
1254
|
+
next.set(key, value);
|
|
1255
|
+
}
|
|
1256
|
+
this._gkConfigMergeSnapshots.set(commonPath, next);
|
|
1257
|
+
}
|
|
1040
1258
|
|
|
1259
|
+
/**
|
|
1260
|
+
* Registers the reconciler invoked by {@link handleGkConfigChanged} on a watcher-observed
|
|
1261
|
+
* `'gkConfig'` change. Only one reconciler is kept — later registrations replace earlier ones,
|
|
1262
|
+
* which is harmless since every CLI config sub-provider sharing this `Cache` reconciles the same
|
|
1263
|
+
* underlying `.git/gk/config` file identically.
|
|
1264
|
+
*/
|
|
1265
|
+
setGkConfigReconciler(
|
|
1266
|
+
reconciler: (
|
|
1267
|
+
repoPath: string,
|
|
1268
|
+
priorSnapshot: ReadonlyMap<string, string> | undefined,
|
|
1269
|
+
) => PromiseOrValue<GkReconcileOutcome>,
|
|
1270
|
+
): void {
|
|
1271
|
+
this._gkConfigReconciler = reconciler;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
/**
|
|
1275
|
+
* Diffs a freshly-read gk config map's merge-relevant subset against `priorSnapshot` (captured by
|
|
1276
|
+
* `handleGkConfigChanged` before the reconcile started, so it reflects the pre-change state even
|
|
1277
|
+
* though this call's own re-read already raced ahead and refreshed the live snapshot), cascading
|
|
1278
|
+
* `baseBranchName`/`branchOverviews` for every ref whose merge-relevant key actually changed.
|
|
1279
|
+
* `priorSnapshot` of `undefined` (never observed before this reconcile) is treated as "everything
|
|
1280
|
+
* may have changed" — a full coarse clear, same as the pre-existing watcher behavior. Returns
|
|
1281
|
+
* whether anything changed (i.e. whether a follow-up repository-changed event is warranted).
|
|
1282
|
+
*/
|
|
1283
|
+
reconcileGkConfigMap(
|
|
1284
|
+
repoPath: string,
|
|
1285
|
+
priorSnapshot: ReadonlyMap<string, string> | undefined,
|
|
1286
|
+
freshMap: ReadonlyMap<string, string>,
|
|
1287
|
+
): boolean {
|
|
1288
|
+
const commonPath = this.getCommonPath(repoPath);
|
|
1289
|
+
const newSubset = this.extractMergeRelevantGkConfig(freshMap);
|
|
1290
|
+
this._gkConfigMergeSnapshots.set(commonPath, newSubset);
|
|
1291
|
+
|
|
1292
|
+
if (priorSnapshot == null) {
|
|
1293
|
+
this.evictShared('baseBranchName', repoPath);
|
|
1294
|
+
this.evictShared('branchOverviews', repoPath);
|
|
1295
|
+
return true;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
let changed = false;
|
|
1299
|
+
for (const key of new Set([...priorSnapshot.keys(), ...newSubset.keys()])) {
|
|
1300
|
+
if (priorSnapshot.get(key) !== newSubset.get(key)) {
|
|
1301
|
+
changed = true;
|
|
1302
|
+
this.cascadeGkConfigKeyChange(commonPath, key);
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
return changed;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
/**
|
|
1309
|
+
* Shared per-ref cascade for a changed `branch.<ref>.gk-...` key: invalidates `baseBranchName` (for
|
|
1310
|
+
* `gk-merge-base`) and `branchOverviews` (for any merge-relevant key), honoring `skipInvalidation`.
|
|
1311
|
+
* Used by both a direct write (`deleteGkConfig`) and a reconciled external change
|
|
1312
|
+
* (`reconcileGkConfigMap`). No-ops for keys outside the merge-relevant subset.
|
|
1313
|
+
*/
|
|
1314
|
+
private cascadeGkConfigKeyChange(
|
|
1315
|
+
commonPath: string,
|
|
1316
|
+
key: string,
|
|
1317
|
+
skip?: readonly GkConfigInvalidationTarget[],
|
|
1318
|
+
): void {
|
|
1041
1319
|
const refMatch = key.match(branchOverviewGkConfigKeysRegex);
|
|
1042
1320
|
if (refMatch == null) return;
|
|
1043
1321
|
|
|
1044
1322
|
const ref = refMatch[1];
|
|
1045
|
-
const skip = options?.skipInvalidation;
|
|
1046
1323
|
|
|
1047
1324
|
// `getBaseBranchName` reads `branch.<ref>.gk-merge-base` and caches the resolved value
|
|
1048
|
-
// per-ref. A
|
|
1049
|
-
// `getBranchContributionsOverview` will resolve `mergeTarget` against the pre-
|
|
1325
|
+
// per-ref. A change to that key must invalidate the cached base or Tier 3 of
|
|
1326
|
+
// `getBranchContributionsOverview` will resolve `mergeTarget` against the pre-change value.
|
|
1050
1327
|
if (key.endsWith('.gk-merge-base') && !skip?.includes('baseBranchName')) {
|
|
1051
|
-
this._caches.baseBranchName?.delete(
|
|
1328
|
+
this._caches.baseBranchName?.delete(commonPath, ref);
|
|
1052
1329
|
}
|
|
1053
1330
|
|
|
1054
1331
|
if (skip?.includes('branchOverviews')) return;
|
|
@@ -1059,7 +1336,18 @@ export class Cache implements Disposable {
|
|
|
1059
1336
|
// invalidate every entry for the affected ref regardless of which target it resolved to.
|
|
1060
1337
|
// Uses `invalidateByKeyPrefix` (not `deleteByKeyPrefix`) so an in-flight factory is still
|
|
1061
1338
|
// shared with new callers instead of triggering a duplicate fetch.
|
|
1062
|
-
|
|
1339
|
+
//
|
|
1340
|
+
// Sibling worktrees get their own bucket, not just the shared one: `getSharedOrCreateWithKey`
|
|
1341
|
+
// stores a per-worktree *mapped* entry under that worktree's own path, and prefix-invalidation
|
|
1342
|
+
// only reaches one bucket. The in-flight case self-propagates on settle, but an already-settled
|
|
1343
|
+
// overview would otherwise survive here — which the coarse `evictShared` path this replaced did
|
|
1344
|
+
// clear, since it iterated the worktrees.
|
|
1345
|
+
this._caches.branchOverviews?.invalidateByKeyPrefix(commonPath, `${ref}|`);
|
|
1346
|
+
for (const worktreePath of this.getWorktreePaths(commonPath)) {
|
|
1347
|
+
if (worktreePath === commonPath) continue;
|
|
1348
|
+
|
|
1349
|
+
this._caches.branchOverviews?.invalidateByKeyPrefix(worktreePath, `${ref}|`);
|
|
1350
|
+
}
|
|
1063
1351
|
}
|
|
1064
1352
|
|
|
1065
1353
|
async getBranchOverview(
|
|
@@ -1135,7 +1423,11 @@ export class Cache implements Disposable {
|
|
|
1135
1423
|
getContributorsStats(
|
|
1136
1424
|
repoPath: string,
|
|
1137
1425
|
cacheKey: string,
|
|
1138
|
-
factory: (
|
|
1426
|
+
factory: (
|
|
1427
|
+
commonPath: string,
|
|
1428
|
+
cacheable: CacheController,
|
|
1429
|
+
cancellation?: AbortSignal,
|
|
1430
|
+
) => PromiseOrValue<GitContributorsStats | undefined>,
|
|
1139
1431
|
options?: { accessTTL?: number; cancellation?: AbortSignal },
|
|
1140
1432
|
): Promise<GitContributorsStats | undefined> {
|
|
1141
1433
|
return this.getSharedSimpleWithKey(this.contributorsStats, repoPath, cacheKey, factory, options);
|
|
@@ -1144,14 +1436,41 @@ export class Cache implements Disposable {
|
|
|
1144
1436
|
getBaseBranchName(
|
|
1145
1437
|
repoPath: string,
|
|
1146
1438
|
ref: string,
|
|
1147
|
-
factory: (
|
|
1439
|
+
factory: (
|
|
1440
|
+
commonPath: string,
|
|
1441
|
+
cacheable: CacheController,
|
|
1442
|
+
cancellation?: AbortSignal,
|
|
1443
|
+
) => PromiseOrValue<string | undefined>,
|
|
1148
1444
|
cancellation?: AbortSignal,
|
|
1149
1445
|
): Promise<string | undefined> {
|
|
1150
1446
|
return this.getSharedSimpleWithKey(this.baseBranchName, repoPath, ref, factory, {
|
|
1447
|
+
// A bounded ceiling, because the eviction signals can't cover every way a base changes. The
|
|
1448
|
+
// `'branches'` cascade deliberately doesn't clear this — a tip move can't change a base, and
|
|
1449
|
+
// re-deriving per commit is the cost this exists to avoid — but that leaves a hole: a branch
|
|
1450
|
+
// deleted and recreated OUTSIDE GitLens, and never checked out, emits only `heads`. A base
|
|
1451
|
+
// derived as "none" before that would otherwise stay "none" for the session.
|
|
1452
|
+
//
|
|
1453
|
+
// `createTTL`, NOT `accessTTL`: the sliding variant resets on every read, so a branch card
|
|
1454
|
+
// polling this entry would hold it alive forever and never re-derive — the exact case the
|
|
1455
|
+
// ceiling exists for. Absolute from creation bounds staleness regardless of read frequency.
|
|
1456
|
+
createTTL: baseBranchNameTTL,
|
|
1151
1457
|
cancellation: cancellation,
|
|
1152
1458
|
});
|
|
1153
1459
|
}
|
|
1154
1460
|
|
|
1461
|
+
/**
|
|
1462
|
+
* Clears the cached base branch for `ref`. Call from ops that create/rename/delete a branch — the
|
|
1463
|
+
* `'branches'` cascade no longer clears these, since tip movement can't change a branch's base.
|
|
1464
|
+
*
|
|
1465
|
+
* Only clears the cache. A branch's base is *persisted* as `branch.<ref>.gk-merge-base` and read back
|
|
1466
|
+
* before the reflog fallback, so an op that ends a branch's identity under a name must drop or move
|
|
1467
|
+
* that key too (`removeGkConfigBranchSection`/`renameGkConfigBranchSection`) — otherwise the
|
|
1468
|
+
* re-derivation this triggers just reads the stale value straight back.
|
|
1469
|
+
*/
|
|
1470
|
+
deleteBaseBranchName(repoPath: string, ref: string): void {
|
|
1471
|
+
this._caches.baseBranchName?.delete(this.getCommonPath(repoPath), ref);
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1155
1474
|
getBranchMergedStatus(
|
|
1156
1475
|
repoPath: string,
|
|
1157
1476
|
cacheKey: string,
|
|
@@ -1198,9 +1517,21 @@ export class Cache implements Disposable {
|
|
|
1198
1517
|
getDefaultBranchName(
|
|
1199
1518
|
repoPath: string,
|
|
1200
1519
|
remote: string,
|
|
1201
|
-
factory: (
|
|
1520
|
+
factory: (
|
|
1521
|
+
commonPath: string,
|
|
1522
|
+
cacheable: CacheController,
|
|
1523
|
+
cancellation?: AbortSignal,
|
|
1524
|
+
) => PromiseOrValue<string | undefined>,
|
|
1525
|
+
cancellation?: AbortSignal,
|
|
1202
1526
|
): Promise<string | undefined> {
|
|
1203
|
-
return this.getSharedSimpleWithKey(this.defaultBranchName, repoPath, remote, factory
|
|
1527
|
+
return this.getSharedSimpleWithKey(this.defaultBranchName, repoPath, remote, factory, {
|
|
1528
|
+
cancellation: cancellation,
|
|
1529
|
+
});
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
/** Clears the cached default branch for one `remote` key (the networked lookup or its `:local` variant). */
|
|
1533
|
+
deleteDefaultBranchName(repoPath: string, remote: string): void {
|
|
1534
|
+
this._caches.defaultBranchName?.delete(this.getCommonPath(repoPath), remote);
|
|
1204
1535
|
}
|
|
1205
1536
|
|
|
1206
1537
|
getInitialCommitSha(
|
|
@@ -1455,7 +1786,9 @@ export class Cache implements Disposable {
|
|
|
1455
1786
|
private async getSharedSimple<T>(
|
|
1456
1787
|
cache: PromiseMap<string, T> | PromiseCache<string, T>,
|
|
1457
1788
|
repoPath: string,
|
|
1458
|
-
factory
|
|
1789
|
+
// `cacheable` is forwarded so a factory can refuse the entry when the read FAILED rather than
|
|
1790
|
+
// genuinely produced nothing — see `getSharedSimpleWithKey` for why that distinction matters.
|
|
1791
|
+
factory: (commonPath: string, cacheable: CacheController) => PromiseOrValue<T>,
|
|
1459
1792
|
): Promise<T> {
|
|
1460
1793
|
const commonPath = this.getCommonPath(repoPath);
|
|
1461
1794
|
|
|
@@ -1463,24 +1796,27 @@ export class Cache implements Disposable {
|
|
|
1463
1796
|
// `PromiseCache.getOrCreate(key, factory, options?)` have different 3rd-argument shapes,
|
|
1464
1797
|
// so we dispatch on cache type. Both register a `CacheController` so `invalidate` works.
|
|
1465
1798
|
if (cache instanceof PromiseCache) {
|
|
1466
|
-
return cache.getOrCreate(commonPath,
|
|
1799
|
+
return cache.getOrCreate(commonPath, cacheable => Promise.resolve(factory(commonPath, cacheable)));
|
|
1467
1800
|
}
|
|
1468
|
-
return cache.getOrCreate(commonPath,
|
|
1801
|
+
return cache.getOrCreate(commonPath, cacheable => Promise.resolve(factory(commonPath, cacheable)));
|
|
1469
1802
|
}
|
|
1470
1803
|
|
|
1471
1804
|
private async getSharedSimpleWithKey<T>(
|
|
1472
1805
|
cache: RepoPromiseCacheMap<string, T>,
|
|
1473
1806
|
repoPath: string,
|
|
1474
1807
|
cacheKey: string,
|
|
1475
|
-
factory
|
|
1476
|
-
|
|
1808
|
+
// `cacheable` is forwarded so a factory can distinguish "the answer is genuinely nothing" from "the
|
|
1809
|
+
// read failed": callers here set no TTL by default, so a failure resolved as `undefined` would
|
|
1810
|
+
// otherwise be served as a real answer until something explicitly evicts it.
|
|
1811
|
+
factory: (commonPath: string, cacheable: CacheController, cancellation?: AbortSignal) => PromiseOrValue<T>,
|
|
1812
|
+
options?: { accessTTL?: number; createTTL?: number; cancellation?: AbortSignal },
|
|
1477
1813
|
): Promise<T> {
|
|
1478
1814
|
const commonPath = this.getCommonPath(repoPath);
|
|
1479
1815
|
|
|
1480
1816
|
return cache.getOrCreate(
|
|
1481
1817
|
commonPath,
|
|
1482
1818
|
cacheKey,
|
|
1483
|
-
(
|
|
1819
|
+
(cacheable, signal) => Promise.resolve(factory(commonPath, cacheable, signal)),
|
|
1484
1820
|
options,
|
|
1485
1821
|
);
|
|
1486
1822
|
}
|
package/src/git/context.ts
CHANGED
|
@@ -159,6 +159,17 @@ export interface GitServiceHooks {
|
|
|
159
159
|
onConflicted?(command: GitConflictCommand, conflicts?: GitConflictFile[]): void;
|
|
160
160
|
/** Called when getGitDir resolves to a non-existent .git directory or rev-parse fails */
|
|
161
161
|
onGitDirResolveFailed?(repoPath: string, gitDir: string, errorMessage: string): void;
|
|
162
|
+
/**
|
|
163
|
+
* Called when a git operation that can start a rebase begins (`'started'`, before the
|
|
164
|
+
* process runs) and again when the command exits (`'ended'`, on success and failure alike).
|
|
165
|
+
* Fired at start (not from the result) because `git rebase` exits 0 when stopping at
|
|
166
|
+
* `edit`/`break`, and fired for every pull because `pull.rebase=true` config can start
|
|
167
|
+
* a rebase the host can't predict from the arguments. The `'ended'` phase lets the host
|
|
168
|
+
* check whether the operation actually left a rebase in progress.
|
|
169
|
+
* @param repoPath The path the rebase state will appear under (the worktree path for
|
|
170
|
+
* worktree-targeted pulls)
|
|
171
|
+
*/
|
|
172
|
+
onRebaseCapableOperation?(repoPath: string, command: 'rebase' | 'pull', phase: 'started' | 'ended'): void;
|
|
162
173
|
};
|
|
163
174
|
}
|
|
164
175
|
|
package/src/git/models/issue.ts
CHANGED
|
@@ -227,6 +227,10 @@ export interface IssueMember {
|
|
|
227
227
|
* Each consumer picks its own presentation.
|
|
228
228
|
*/
|
|
229
229
|
name?: string;
|
|
230
|
+
/** The provider's handle for this person, when it has one — GitHub's login, Azure's `uniqueName` (a UPN,
|
|
231
|
+
* so an email), Bitbucket's mutable `nickname`. Display/labelling only: it is neither guaranteed present
|
|
232
|
+
* (GitLab's native mapper has none) nor a stable identity, so never key a match off it. */
|
|
233
|
+
username?: string;
|
|
230
234
|
avatarUrl?: string;
|
|
231
235
|
url?: string;
|
|
232
236
|
}
|
|
@@ -13,6 +13,7 @@ export interface MergeConflictFile {
|
|
|
13
13
|
export type ConflictDetectionErrorReason = 'unsupported' | 'noParent' | 'noMergeBase' | 'refNotFound' | 'other';
|
|
14
14
|
|
|
15
15
|
export type ConflictDetectionResult =
|
|
16
|
-
|
|
17
|
-
| { status: '
|
|
16
|
+
// `treeOid` is the tree the simulated integration produced, when the provider computed one — callers layer further simulations onto it
|
|
17
|
+
| { status: 'clean'; treeOid?: string }
|
|
18
|
+
| { status: 'conflicts'; conflict: MergeConflicts; stoppedOnFirstConflict?: boolean; treeOid?: string }
|
|
18
19
|
| { status: 'error'; reason: ConflictDetectionErrorReason; message: string };
|