@gitkraken/core-gitlens 0.5.110 → 0.5.111
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 +16 -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
|
@@ -24,11 +24,13 @@ import type {
|
|
|
24
24
|
SearchCommitsResult,
|
|
25
25
|
} from '../../git/providers/commits.js';
|
|
26
26
|
import type { DiffRange } from '../../git/providers/types.js';
|
|
27
|
+
import type { GitCommandPriority } from '../../git/run.types.js';
|
|
27
28
|
import { createUncommittedChangesCommit } from '../../git/utils/commit.utils.js';
|
|
28
29
|
import { isRevisionRange, isSha, isUncommitted, isUncommittedStaged } from '../../git/utils/revision.utils.js';
|
|
29
30
|
import { parseSearchQueryGitCommand } from '../../git/utils/search.utils.js';
|
|
30
31
|
import { compareReachableRefs } from '../../git/utils/sorting.js';
|
|
31
32
|
import { isUserMatch } from '../../git/utils/user.utils.js';
|
|
33
|
+
import { chunk as chunkArray } from '../../utils/array.js';
|
|
32
34
|
import { CancellationError, isCancellationError } from '../../utils/cancellation.js';
|
|
33
35
|
import { debug, trace } from '../../utils/decorators/log.js';
|
|
34
36
|
import { createDisposable } from '../../utils/disposable.js';
|
|
@@ -37,7 +39,7 @@ import { getScopedLogger } from '../../utils/logger.scoped.js';
|
|
|
37
39
|
import { isFolderGlob, normalizePath, splitPath, stripFolderGlob } from '../../utils/path.js';
|
|
38
40
|
import type { CacheController } from '../../utils/promiseCache.js';
|
|
39
41
|
import { maybeStopWatch } from '../../utils/stopwatch.js';
|
|
40
|
-
import { escapeRegex } from '../../utils/string.js';
|
|
42
|
+
import { escapeRegex, iterateByDelimiter } from '../../utils/string.js';
|
|
41
43
|
import type { Uri } from '../../utils/uri.js';
|
|
42
44
|
import { fileUri, joinUriPath, toFsPath } from '../../utils/uri.js';
|
|
43
45
|
import type { CliGitProviderInternal } from '../cliGitProvider.js';
|
|
@@ -67,6 +69,8 @@ import { convertStashesToStdin } from './stash.js';
|
|
|
67
69
|
|
|
68
70
|
const emptyPromise: Promise<GitBlame | ParsedGitDiffHunks | GitLog | undefined> = Promise.resolve(undefined);
|
|
69
71
|
const reflogCommands = ['merge', 'pull'];
|
|
72
|
+
/** Keeps `filterUnpublishedShas` well inside Windows' ~32K command-line cap (40 hex chars + separator each). */
|
|
73
|
+
const maxShasPerRevListSpawn = 500;
|
|
70
74
|
|
|
71
75
|
export class CommitsGitSubProvider implements GitCommitsSubProvider {
|
|
72
76
|
constructor(
|
|
@@ -114,23 +118,48 @@ export class CommitsGitSubProvider implements GitCommitsSubProvider {
|
|
|
114
118
|
}
|
|
115
119
|
|
|
116
120
|
@debug({ exit: true })
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
'rev-list',
|
|
121
|
-
'--count',
|
|
121
|
+
getCommitCount(repoPath: string, rev: string, cancellation?: AbortSignal): Promise<number | undefined> {
|
|
122
|
+
return this.cache.commitCount.getOrCreate(
|
|
123
|
+
repoPath,
|
|
122
124
|
rev,
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
async (cacheable, signal) => {
|
|
126
|
+
// Bind the shared spawn to the aggregate `signal` (fires only when ALL current callers
|
|
127
|
+
// abort), not this-caller's `cancellation` — otherwise a superseded caller's abort would
|
|
128
|
+
// kill the shared command and reject concurrent riders that never cancelled.
|
|
129
|
+
const result = await this.git.run(
|
|
130
|
+
{ cwd: repoPath, cancellation: signal ?? cancellation, errors: 'ignore' },
|
|
131
|
+
'rev-list',
|
|
132
|
+
'--count',
|
|
133
|
+
rev,
|
|
134
|
+
'--',
|
|
135
|
+
);
|
|
136
|
+
if (result.completion.status === 'cancelled' || signal?.aborted) {
|
|
137
|
+
throw new CancellationError();
|
|
138
|
+
}
|
|
128
139
|
|
|
129
|
-
|
|
130
|
-
|
|
140
|
+
// `errors: 'ignore'` turns a failed run (unknown rev, a transient `index.lock`) into an
|
|
141
|
+
// empty stdout rather than a throw. That's indistinguishable from a real answer here, so
|
|
142
|
+
// don't let it become a cached one — this entry has a sliding hour-long TTL, and repeated
|
|
143
|
+
// access would keep a transient failure alive indefinitely. The pre-cache implementation
|
|
144
|
+
// simply retried on the next call; invalidating restores that.
|
|
145
|
+
//
|
|
146
|
+
// BOTH conditions, not either alone. `exitCode !== 0` catches "git ran and said no" (a bad
|
|
147
|
+
// rev exits 128); `status !== 'exited'` catches "git never answered" — a queue rejection or
|
|
148
|
+
// spawn failure (no exit code at all), or a swallowed `GitWarnings` match, which carries git's
|
|
149
|
+
// real code and so can slip past the exit-code test on its own.
|
|
150
|
+
if (result.completion.status !== 'exited' || result.exitCode !== 0) {
|
|
151
|
+
cacheable.invalidate();
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const data = result.stdout.trim();
|
|
156
|
+
if (!data) return undefined;
|
|
131
157
|
|
|
132
|
-
|
|
133
|
-
|
|
158
|
+
const count = parseInt(data, 10);
|
|
159
|
+
return isNaN(count) ? undefined : count;
|
|
160
|
+
},
|
|
161
|
+
{ cancellation: cancellation },
|
|
162
|
+
);
|
|
134
163
|
}
|
|
135
164
|
|
|
136
165
|
@debug({ exit: true })
|
|
@@ -157,6 +186,68 @@ export class CommitsGitSubProvider implements GitCommitsSubProvider {
|
|
|
157
186
|
return result.stdout.trim().length > 0;
|
|
158
187
|
}
|
|
159
188
|
|
|
189
|
+
@debug({
|
|
190
|
+
args: (repoPath, shas) => ({ repoPath: repoPath, shas: `${shas.length} value(s)` }),
|
|
191
|
+
exit: r => `${r.size} unpublished`,
|
|
192
|
+
})
|
|
193
|
+
async filterUnpublishedShas(
|
|
194
|
+
repoPath: string,
|
|
195
|
+
shas: readonly string[],
|
|
196
|
+
options?: { priority?: GitCommandPriority },
|
|
197
|
+
cancellation?: AbortSignal,
|
|
198
|
+
): Promise<Set<string>> {
|
|
199
|
+
const unpublished = new Set<string>();
|
|
200
|
+
if (!shas.length) return unpublished;
|
|
201
|
+
|
|
202
|
+
// One walk answers every sha: `<shas…> --not --remotes` yields the commits reachable from any of them
|
|
203
|
+
// but not from any remote-tracking ref, so a sha is unpublished exactly when it appears in the output.
|
|
204
|
+
// Callers pass tips from sibling worktrees — safe from a single `cwd` because the object store and
|
|
205
|
+
// `refs/remotes` are shared across a repo's worktrees (unlike the index/working tree, which are not).
|
|
206
|
+
//
|
|
207
|
+
// NOT `--no-walk`: it "has no effect if a range is specified", and `--not --remotes` makes this a range,
|
|
208
|
+
// so it would silently walk anyway and return the whole unpublished history instead of just these tips.
|
|
209
|
+
// The walk is bounded by divergence from the remotes, not by repo size; callers skip this entirely when
|
|
210
|
+
// the repo has no remotes (where every local commit would qualify).
|
|
211
|
+
// Chunked so a repo with a very large number of worktrees can't overflow the platform's argument
|
|
212
|
+
// limit (Windows' `CreateProcess` caps the whole command line at ~32K characters).
|
|
213
|
+
// Deliberately NOT `errors: 'ignore'`: this answers for EVERY sha at once, so a swallowed failure
|
|
214
|
+
// would return an empty set the caller can't distinguish from "nothing is unpublished", publishing a
|
|
215
|
+
// confident `hasUnpushed: false` across every worktree in the batch. Letting it throw degrades the
|
|
216
|
+
// caller to "unknown", which omits the field and leaves the client's last-known value in place.
|
|
217
|
+
for (const chunk of chunkArray([...shas], maxShasPerRevListSpawn)) {
|
|
218
|
+
const result = await this.git.run(
|
|
219
|
+
{
|
|
220
|
+
cwd: repoPath,
|
|
221
|
+
cancellation: cancellation,
|
|
222
|
+
...(options?.priority != null ? { priority: options.priority } : undefined),
|
|
223
|
+
},
|
|
224
|
+
'rev-list',
|
|
225
|
+
...chunk,
|
|
226
|
+
'--not',
|
|
227
|
+
'--remotes',
|
|
228
|
+
'--',
|
|
229
|
+
);
|
|
230
|
+
// Anything but a clean exit leaves empty stdout, which this function would otherwise read as
|
|
231
|
+
// "every sha is published" — publishing a definite, wrong `hasUnpushed: false` for the whole
|
|
232
|
+
// batch. Dropping `errors: 'ignore'` was not enough on its own: a stderr matching `GitWarnings`
|
|
233
|
+
// (a repo path that briefly vanished, an unknown rev) is swallowed by the default handler and
|
|
234
|
+
// resolves rather than throwing. Rethrow so the caller degrades to "unknown".
|
|
235
|
+
if (result.completion.status !== 'exited') throw result.completion.error;
|
|
236
|
+
if (cancellation?.aborted) throw new CancellationError();
|
|
237
|
+
|
|
238
|
+
const candidates = new Set(chunk);
|
|
239
|
+
for (const line of iterateByDelimiter(result.stdout, '\n')) {
|
|
240
|
+
const sha = line.trim();
|
|
241
|
+
// The walk emits ancestors too; keep only the tips we were asked about.
|
|
242
|
+
if (sha.length && candidates.has(sha)) {
|
|
243
|
+
unpublished.add(sha);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return unpublished;
|
|
249
|
+
}
|
|
250
|
+
|
|
160
251
|
@debug({ exit: true })
|
|
161
252
|
async getCommitDates(
|
|
162
253
|
repoPath: string,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { promises as fs } from 'fs';
|
|
2
2
|
import { hostname, userInfo } from 'os';
|
|
3
3
|
import { env as process_env } from 'process';
|
|
4
|
-
import type { Cache, GkConfigInvalidationTarget } from '../../git/cache.js';
|
|
4
|
+
import type { Cache, GkConfigInvalidationTarget, GkReconcileOutcome } from '../../git/cache.js';
|
|
5
5
|
import type { GitServiceContext } from '../../git/context.js';
|
|
6
6
|
import { WorkspaceUntrustedError } from '../../git/errors.js';
|
|
7
7
|
import type { GitDir } from '../../git/models/repository.js';
|
|
@@ -16,6 +16,7 @@ import type {
|
|
|
16
16
|
} from '../../git/providers/config.js';
|
|
17
17
|
import { gate } from '../../utils/decorators/gate.js';
|
|
18
18
|
import { debug, trace } from '../../utils/decorators/log.js';
|
|
19
|
+
import { first, some } from '../../utils/iterable.js';
|
|
19
20
|
import { Logger } from '../../utils/logger.js';
|
|
20
21
|
import { getScopedLogger } from '../../utils/logger.scoped.js';
|
|
21
22
|
import { dirname, getBestPath, isAbsolute, joinPaths, normalizePath } from '../../utils/path.js';
|
|
@@ -79,7 +80,17 @@ export class ConfigGitSubProvider implements GitConfigSubProvider {
|
|
|
79
80
|
private readonly git: Git,
|
|
80
81
|
private readonly cache: Cache,
|
|
81
82
|
private readonly provider: CliGitProviderInternal,
|
|
82
|
-
) {
|
|
83
|
+
) {
|
|
84
|
+
this.cache.setGkConfigReconciler((repoPath, priorSnapshot) => this.reconcileGkConfig(repoPath, priorSnapshot));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* In-flight gk-config reconciles keyed by commonPath. `pending` records that another `'gkConfig'`
|
|
89
|
+
* event landed mid-pass, so one trailing pass runs after the current one settles. `paths` holds every
|
|
90
|
+
* worktree path that reported the change, mapped to its close generation at that moment — the pass
|
|
91
|
+
* reads through one that's still open and notifies all of them.
|
|
92
|
+
*/
|
|
93
|
+
private readonly _gkConfigReconciles = new Map<string, { pending: boolean; paths: Map<string, number> }>();
|
|
83
94
|
|
|
84
95
|
/**
|
|
85
96
|
* Pending `setGkConfig` writes keyed by `(commonPath, key, value, skip-option)`. Concurrent
|
|
@@ -358,13 +369,40 @@ export class ConfigGitSubProvider implements GitConfigSubProvider {
|
|
|
358
369
|
// clearCaches('gkConfig'), which would otherwise evict the in-flight gkConfigMap entry the
|
|
359
370
|
// cache wrapper synchronously stored and force a redundant bulk re-read.
|
|
360
371
|
await this.migrateGkConfigFromGitConfig(this.cache.getCommonPath(repoPath));
|
|
361
|
-
return this.cache.getGkConfigMap(repoPath,
|
|
372
|
+
return this.cache.getGkConfigMap(repoPath, async cacheable => {
|
|
373
|
+
const { map, failed } = await this.getGkConfigRegexCore(repoPath, gkConfigAllPattern);
|
|
374
|
+
// This map has no TTL and everything gk-related reads through it, so a failed read cached as an
|
|
375
|
+
// empty map would report every branch as having no stored base or merge target for the session —
|
|
376
|
+
// and would make the section-cleanup pre-checks skip real work. Only cache a read that happened.
|
|
377
|
+
if (failed) {
|
|
378
|
+
cacheable.invalidate();
|
|
379
|
+
}
|
|
380
|
+
return map;
|
|
381
|
+
});
|
|
362
382
|
}
|
|
363
383
|
|
|
364
|
-
private async getGkConfigRegexCore(
|
|
384
|
+
private async getGkConfigRegexCore(
|
|
385
|
+
repoPath: string,
|
|
386
|
+
pattern: string,
|
|
387
|
+
): Promise<{ map: Map<string, string>; failed: boolean }> {
|
|
365
388
|
const gkConfigPath = await this.getGkConfigPath(repoPath);
|
|
366
|
-
|
|
367
|
-
|
|
389
|
+
// No `.git/gk/config` yet is a real answer, not a failure — a repo GitLens has never written to.
|
|
390
|
+
if (!gkConfigPath) return { map: new Map(), failed: false };
|
|
391
|
+
|
|
392
|
+
const result = await this.git.run(
|
|
393
|
+
{ cwd: repoPath, errors: 'ignore', runLocally: true },
|
|
394
|
+
'config',
|
|
395
|
+
'--get-regex',
|
|
396
|
+
'-f',
|
|
397
|
+
gkConfigPath,
|
|
398
|
+
pattern,
|
|
399
|
+
);
|
|
400
|
+
// `--get-regex` exits 1 when nothing matches, which IS an answer (an empty gk config).
|
|
401
|
+
const exited = result.completion.status === 'exited';
|
|
402
|
+
return {
|
|
403
|
+
map: parseConfigRegexOutput(result.stdout.trim()),
|
|
404
|
+
failed: !exited || (result.exitCode !== 0 && result.exitCode !== 1),
|
|
405
|
+
};
|
|
368
406
|
}
|
|
369
407
|
|
|
370
408
|
@debug()
|
|
@@ -412,9 +450,192 @@ export class ConfigGitSubProvider implements GitConfigSubProvider {
|
|
|
412
450
|
await this.setConfigCore(repoPath, key, value, { file: gkConfigPath });
|
|
413
451
|
|
|
414
452
|
// Invalidate the bulk map (so the next read re-fetches) and the derived caches for this key's
|
|
415
|
-
// ref. The `.git/gk/config` file-watcher also fires `'gkConfig'` shortly after, which
|
|
416
|
-
// the same
|
|
453
|
+
// ref. The `.git/gk/config` file-watcher also fires `'gkConfig'` shortly after, which reconciles
|
|
454
|
+
// the same change (see `reconcileGkConfig`) — recording our own write here keeps that later
|
|
455
|
+
// reconcile from re-diffing this key as an external change.
|
|
417
456
|
this.cache.deleteGkConfig(repoPath, key, options);
|
|
457
|
+
this.cache.recordGkConfigWrite(repoPath, key, value);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
@debug()
|
|
461
|
+
async removeGkConfigBranchSection(repoPath: string, ref: string): Promise<void> {
|
|
462
|
+
await this.runGkConfigSectionCommand(repoPath, ref, '--remove-section', `branch.${ref}`);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
@debug()
|
|
466
|
+
async renameGkConfigBranchSection(repoPath: string, oldRef: string, newRef: string): Promise<void> {
|
|
467
|
+
// Clear the destination first, unconditionally. `--rename-section` onto a section that already
|
|
468
|
+
// exists APPENDS rather than replaces, leaving duplicate keys — and git resolves a duplicated key
|
|
469
|
+
// to its LAST value, so the orphan left by an earlier branch of this name would win over the
|
|
470
|
+
// metadata being moved. Doing this outside `runGkConfigSectionCommand` also covers the case where
|
|
471
|
+
// the source has no gk keys at all, where its pre-check would otherwise skip the whole call and
|
|
472
|
+
// leave the orphan in place for the renamed branch to inherit.
|
|
473
|
+
await this.removeGkConfigBranchSection(repoPath, newRef);
|
|
474
|
+
await this.runGkConfigSectionCommand(
|
|
475
|
+
repoPath,
|
|
476
|
+
oldRef,
|
|
477
|
+
'--rename-section',
|
|
478
|
+
`branch.${oldRef}`,
|
|
479
|
+
`branch.${newRef}`,
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Runs a section-level `git config` against `.git/gk/config` for `ref`'s section, skipping the
|
|
485
|
+
* subprocess entirely when the bulk map (already cached on any warm path) shows that ref has no gk
|
|
486
|
+
* keys — the overwhelmingly common case, since most branches never get gk metadata written at all.
|
|
487
|
+
*
|
|
488
|
+
* Never throws: callers invoke this as bookkeeping after a branch op has already succeeded, so a
|
|
489
|
+
* failure here must not be reported as (or roll back) the branch/checkout/worktree operation itself.
|
|
490
|
+
* The worst case is metadata that stays behind until the next op on that name.
|
|
491
|
+
*/
|
|
492
|
+
private async runGkConfigSectionCommand(repoPath: string, ref: string, ...args: string[]): Promise<void> {
|
|
493
|
+
const scope = getScopedLogger();
|
|
494
|
+
try {
|
|
495
|
+
const prefix = `branch.${ref}.`;
|
|
496
|
+
const map = await this.getGkConfigMap(repoPath);
|
|
497
|
+
// The shortcut needs a map with CONTENT to be trustworthy. A failed bulk read resolves empty
|
|
498
|
+
// (see `getGkConfigMap`) — the cache refuses it, but this caller still holds it — and an empty map
|
|
499
|
+
// read as authoritative would skip the removal entirely, leaving a deleted branch's base,
|
|
500
|
+
// disposition and issue links behind for the next branch reusing that name. When it's empty, run
|
|
501
|
+
// the command: worst case is a no-op on a section that isn't there.
|
|
502
|
+
if (map.size && !some(map.keys(), k => k.startsWith(prefix))) return;
|
|
503
|
+
|
|
504
|
+
const gkConfigPath = await this.getGkConfigPath(repoPath);
|
|
505
|
+
if (!gkConfigPath) return;
|
|
506
|
+
// `getGkConfigPath` only joins a path, so check the file: with no `.git/gk/config` there is
|
|
507
|
+
// genuinely nothing to remove, and running the command would fail with a fatal we'd then warn
|
|
508
|
+
// about on every branch delete in a repo that has never had gk metadata.
|
|
509
|
+
if (!(await fsExists(gkConfigPath))) return;
|
|
510
|
+
|
|
511
|
+
const result = await this.git.run(
|
|
512
|
+
{ cwd: repoPath, runLocally: true, errors: 'ignore' },
|
|
513
|
+
'config',
|
|
514
|
+
'-f',
|
|
515
|
+
gkConfigPath,
|
|
516
|
+
...args,
|
|
517
|
+
);
|
|
518
|
+
// `errors: 'ignore'` is deliberate — see the note above on why this must never throw — but that
|
|
519
|
+
// also means the `catch` below never sees a failed write, so without this the metadata silently
|
|
520
|
+
// stays behind (or, on a rename, stays under the old name) with nothing recorded anywhere.
|
|
521
|
+
// Reported, not thrown: the branch op it follows has already succeeded.
|
|
522
|
+
if (result.completion.status !== 'exited' || result.exitCode !== 0) {
|
|
523
|
+
scope?.warn(
|
|
524
|
+
`Failed to update gk config section for '${ref}' in '${repoPath}': ${
|
|
525
|
+
result.completion.status === 'exited'
|
|
526
|
+
? `git config exited ${result.exitCode}`
|
|
527
|
+
: `${result.completion.status} · ${result.completion.error.message}`
|
|
528
|
+
}`,
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
// A whole section moved or vanished, which the per-key `deleteGkConfig` cascade can't express.
|
|
532
|
+
// Drop the bulk map so the next read hits disk; the derived caches for the affected ref are
|
|
533
|
+
// evicted by the caller's own `deleteBaseBranchName` plus the `'branches'` cascade it fires.
|
|
534
|
+
this.cache.deleteGkConfigMap(repoPath);
|
|
535
|
+
} catch (ex) {
|
|
536
|
+
scope?.error(ex, `Failed to update gk config section for '${ref}' in '${repoPath}'`);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Reconciles a watcher-observed `'gkConfig'` change — registered with the shared `Cache` as its
|
|
542
|
+
* gk-config reconciler (see the constructor). Re-reads the bulk map (hard-evicted by
|
|
543
|
+
* `Cache.handleGkConfigChanged`, so this hits disk) and diffs it against `priorSnapshot` to cascade
|
|
544
|
+
* `baseBranchName`/`branchOverviews` only for refs whose merge-relevant keys actually changed.
|
|
545
|
+
*
|
|
546
|
+
* Serialized per commonPath: a reconcile is a read-then-diff against a single shared baseline, so
|
|
547
|
+
* two overlapping passes would both diff against the same snapshot and the later change could fall
|
|
548
|
+
* out entirely. An event arriving mid-pass instead queues one trailing pass, which re-reads and
|
|
549
|
+
* diffs against the baseline the previous pass just established.
|
|
550
|
+
*
|
|
551
|
+
* `.git/gk/config` lives in the common dir, so the watcher delivers one event per worktree sharing it
|
|
552
|
+
* (see `WatchGroup.onCommonEvent`). Siblings never run concurrently — the later ones fold into the
|
|
553
|
+
* in-flight pass — but folding in arms the trailing pass, so a fan-out costs one extra read that
|
|
554
|
+
* re-reads and diffs an unchanged file. However many siblings land while a pass is running, they
|
|
555
|
+
* coalesce into that single trailing pass; the cost is the price of never dropping a real change.
|
|
556
|
+
* Every reporting path is retained because `Repository` matches the follow-up change event on exact
|
|
557
|
+
* path, and a sibling worktree that never receives one would sit on stale derived values.
|
|
558
|
+
*/
|
|
559
|
+
private reconcileGkConfig(
|
|
560
|
+
repoPath: string,
|
|
561
|
+
priorSnapshot: ReadonlyMap<string, string> | undefined,
|
|
562
|
+
): GkReconcileOutcome | Promise<GkReconcileOutcome> {
|
|
563
|
+
const commonPath = this.cache.getCommonPath(repoPath);
|
|
564
|
+
|
|
565
|
+
const inflight = this._gkConfigReconciles.get(commonPath);
|
|
566
|
+
if (inflight != null) {
|
|
567
|
+
inflight.paths.set(repoPath, this.cache.getCloseGeneration(repoPath));
|
|
568
|
+
inflight.pending = true;
|
|
569
|
+
// Handed to the in-flight pass's trailing loop, so it IS being reconciled — just not by this
|
|
570
|
+
// call. Reporting a failure here would coarse-clear on every write in a burst.
|
|
571
|
+
return 'deferred';
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
const entry = { pending: false, paths: new Map([[repoPath, this.cache.getCloseGeneration(repoPath)]]) };
|
|
575
|
+
this._gkConfigReconciles.set(commonPath, entry);
|
|
576
|
+
return this.runGkConfigReconcile(commonPath, priorSnapshot, entry);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/** Drops paths that closed since they reported the change; returns whichever remain. */
|
|
580
|
+
private pruneClosedReconcilePaths(paths: Map<string, number>): Map<string, number> {
|
|
581
|
+
for (const [path, generation] of paths) {
|
|
582
|
+
if (this.cache.getCloseGeneration(path) !== generation) {
|
|
583
|
+
paths.delete(path);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
return paths;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
private async runGkConfigReconcile(
|
|
590
|
+
commonPath: string,
|
|
591
|
+
priorSnapshot: ReadonlyMap<string, string> | undefined,
|
|
592
|
+
entry: { pending: boolean; paths: Map<string, number> },
|
|
593
|
+
): Promise<GkReconcileOutcome> {
|
|
594
|
+
const scope = getScopedLogger();
|
|
595
|
+
let snapshot = priorSnapshot;
|
|
596
|
+
|
|
597
|
+
try {
|
|
598
|
+
do {
|
|
599
|
+
entry.pending = false;
|
|
600
|
+
|
|
601
|
+
// Read through a path that's still open — one worktree closing must not drop the event for
|
|
602
|
+
// its still-open siblings, which is why this reads from the surviving set rather than from
|
|
603
|
+
// whichever path happened to report first.
|
|
604
|
+
const readPath = first(this.pruneClosedReconcilePaths(entry.paths).keys());
|
|
605
|
+
// Every path that reported the change has since closed — nothing left to reconcile, and
|
|
606
|
+
// nothing left holding a stale derived value either.
|
|
607
|
+
if (readPath == null) return 'reconciled';
|
|
608
|
+
|
|
609
|
+
// A trailing pass must not reuse the entry the pass before it installed.
|
|
610
|
+
this.cache.deleteGkConfigMap(readPath);
|
|
611
|
+
const freshMap = await this.getGkConfigMap(readPath);
|
|
612
|
+
// Paths may have closed while the re-read was in flight; if every one did, there's nothing
|
|
613
|
+
// left to reconcile against.
|
|
614
|
+
if (!this.pruneClosedReconcilePaths(entry.paths).size) return 'reconciled';
|
|
615
|
+
|
|
616
|
+
if (this.cache.reconcileGkConfigMap(readPath, snapshot, freshMap)) {
|
|
617
|
+
// Fire a follow-up change: a consumer that re-read on the original event may have done so
|
|
618
|
+
// before this async cascade landed, so it would otherwise hold a stale derived value with
|
|
619
|
+
// nothing left to prompt another re-read.
|
|
620
|
+
for (const path of entry.paths.keys()) {
|
|
621
|
+
this.context.hooks?.repository?.onChanged?.(path, ['gkConfig']);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// `reconcileGkConfigMap` advanced the stored baseline to what we just read; a trailing pass
|
|
626
|
+
// diffs its own read against that.
|
|
627
|
+
snapshot = this.cache.getGkConfigMergeSnapshot(readPath);
|
|
628
|
+
} while (entry.pending);
|
|
629
|
+
|
|
630
|
+
return 'reconciled';
|
|
631
|
+
} catch (ex) {
|
|
632
|
+
scope?.error(ex, `Failed to reconcile gk config for '${commonPath}'`);
|
|
633
|
+
// The caller falls back to the coarse clear — derived caches are otherwise left holding
|
|
634
|
+
// pre-change values with nothing to correct them.
|
|
635
|
+
return 'failed';
|
|
636
|
+
} finally {
|
|
637
|
+
this._gkConfigReconciles.delete(commonPath);
|
|
638
|
+
}
|
|
418
639
|
}
|
|
419
640
|
|
|
420
641
|
@debug()
|
|
@@ -301,7 +301,11 @@ export class ContributorsGitSubProvider implements GitContributorsSubProvider {
|
|
|
301
301
|
|
|
302
302
|
const scope = getScopedLogger();
|
|
303
303
|
|
|
304
|
-
const getCore = async (
|
|
304
|
+
const getCore = async (
|
|
305
|
+
commonPath: string,
|
|
306
|
+
_cacheable?: CacheController,
|
|
307
|
+
signal?: AbortSignal,
|
|
308
|
+
): Promise<GitContributorsStats | undefined> => {
|
|
305
309
|
try {
|
|
306
310
|
const args = ['shortlog', '-s', '--all'];
|
|
307
311
|
|
|
@@ -221,22 +221,27 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
|
|
|
221
221
|
/**
|
|
222
222
|
* Current ref tips (canonical refname → PEELED commit sha), for the R6b fast path's gate. Peeled so an
|
|
223
223
|
* annotated tag maps to the commit its badge sits on — the same convention {@link GraphIncrementalSeed.tips}
|
|
224
|
-
* uses — and so the seed↔current diff is by commit identity.
|
|
225
|
-
*
|
|
224
|
+
* uses — and so the seed↔current diff is by commit identity. Covers heads/remotes/tags/replace (all the
|
|
225
|
+
* namespaces the gate consumers read); the caller filters to the ref classes it cares about.
|
|
226
226
|
*/
|
|
227
227
|
private async getCurrentRefTips(repoPath: string, cancellation?: AbortSignal): Promise<Map<string, string>> {
|
|
228
|
-
const result = await this.git.run(
|
|
229
|
-
{ cwd: repoPath, configs: gitConfigsLog, cancellation: cancellation, errors: 'ignore' },
|
|
230
|
-
'for-each-ref',
|
|
231
|
-
'--format=%(objectname) %(*objectname) %(refname)',
|
|
232
|
-
);
|
|
233
228
|
const tips = new Map<string, string>();
|
|
234
|
-
|
|
235
|
-
//
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
229
|
+
try {
|
|
230
|
+
// `force: true` invalidates the shared refs cache so this spawns fresh (this gate needs a
|
|
231
|
+
// point-in-time read, not a possibly-stale cached one) while ALSO repopulating that cache —
|
|
232
|
+
// one `for-each-ref` serves both this gate and any sibling `getRefs`/`getBranches`/`getTags`
|
|
233
|
+
// call racing it (they attach to the same in-flight spawn).
|
|
234
|
+
const records = await this.provider.refs.getRefs(repoPath, cancellation, { force: true });
|
|
235
|
+
for (const record of records) {
|
|
236
|
+
if (!record.name) continue;
|
|
237
|
+
|
|
238
|
+
// Peeled objectname is set only for annotated tags; everything else uses objectname.
|
|
239
|
+
tips.set(record.name, record.peeledObjectname || record.objectname);
|
|
240
|
+
}
|
|
241
|
+
} catch {
|
|
242
|
+
// Preserve the prelude's "never rejects" contract (see the bare `await` at the call site):
|
|
243
|
+
// a per-caller cancellation race through the shared cache (or any other failure) degrades
|
|
244
|
+
// to no tips, same as the direct `errors: 'ignore'` spawn this replaced.
|
|
240
245
|
}
|
|
241
246
|
return tips;
|
|
242
247
|
}
|
|
@@ -388,6 +393,14 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
|
|
|
388
393
|
// single `for-each-ref`, off the rows-walk critical path). Attached to the returned graph as `refTips`
|
|
389
394
|
// so the host can seed the NEXT rebuild's tip diff; the R6b fast path also reuses this same map for its
|
|
390
395
|
// own tip-diff gate (so there's exactly one `for-each-ref` per call whether it goes fast or full).
|
|
396
|
+
// ALWAYS forced, including on a cold build. It's tempting to skip the force when there's no seed to
|
|
397
|
+
// diff against — the cold build only records these tips for the NEXT build — but the tips it records
|
|
398
|
+
// must describe the same repo state the rows walk observed, and the rows walk always reads live git.
|
|
399
|
+
// A cached (stale) enumeration breaks that pairing in a way a later build can't detect: if a ref moves
|
|
400
|
+
// A→B before the cold load and is then reverted B→A, the seed says A, the next forced read says A, the
|
|
401
|
+
// tip diff sees no change and takes the fast path — while the cached rows were built at B and still
|
|
402
|
+
// carry the discarded commit. Staleness here is not "more work, never wrong"; only the seed side can
|
|
403
|
+
// be stale, so a move-and-revert admits a genuinely wrong fast path.
|
|
391
404
|
const refTipsPromise = this.getCurrentRefTips(repoPath, cancellation);
|
|
392
405
|
|
|
393
406
|
// Shallow state (peeled off the same prelude, off the rows-walk critical path) — stamped on the returned
|
|
@@ -70,6 +70,10 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
|
|
|
70
70
|
|
|
71
71
|
try {
|
|
72
72
|
await this.git.run({ cwd: repoPath, errors: 'throw', ...runOptions }, ...params);
|
|
73
|
+
if (options?.createBranch) {
|
|
74
|
+
// Evict the cached base only — see `createBranch` on why the persisted keys stay.
|
|
75
|
+
this.cache.deleteBaseBranchName(repoPath, options.createBranch);
|
|
76
|
+
}
|
|
73
77
|
this.context.hooks?.cache?.onReset?.(repoPath, 'branches', 'status');
|
|
74
78
|
this.context.hooks?.repository?.onChanged?.(repoPath, ['head', 'heads', 'index']);
|
|
75
79
|
} catch (ex) {
|
|
@@ -393,15 +397,23 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
|
|
|
393
397
|
);
|
|
394
398
|
if (worktree != null) {
|
|
395
399
|
// Branch is checked out in a worktree — run git pull in that worktree's directory
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
400
|
+
// Any rebase state (e.g. from `pull.rebase=true`) appears under the worktree path
|
|
401
|
+
const worktreePath = normalizePath(worktree.uri.fsPath);
|
|
402
|
+
this.context.hooks?.operations?.onRebaseCapableOperation?.(worktreePath, 'pull', 'started');
|
|
403
|
+
try {
|
|
404
|
+
await this.pullCore(
|
|
405
|
+
worktreePath,
|
|
406
|
+
{
|
|
407
|
+
rebase: options?.rebase,
|
|
408
|
+
tags: options?.tags,
|
|
409
|
+
source: options?.source,
|
|
410
|
+
},
|
|
411
|
+
runOptions,
|
|
412
|
+
);
|
|
413
|
+
} finally {
|
|
414
|
+
this.context.hooks?.operations?.onRebaseCapableOperation?.(worktreePath, 'pull', 'ended');
|
|
415
|
+
}
|
|
416
|
+
|
|
405
417
|
this.context.hooks?.cache?.onReset?.(repoPath, 'branches', 'status', 'tags');
|
|
406
418
|
this.context.hooks?.repository?.onChanged?.(repoPath, ['head', 'heads', 'remotes', 'index']);
|
|
407
419
|
} else {
|
|
@@ -411,15 +423,20 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
|
|
|
411
423
|
return;
|
|
412
424
|
}
|
|
413
425
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
426
|
+
this.context.hooks?.operations?.onRebaseCapableOperation?.(repoPath, 'pull', 'started');
|
|
427
|
+
try {
|
|
428
|
+
await this.pullCore(
|
|
429
|
+
repoPath,
|
|
430
|
+
{
|
|
431
|
+
rebase: options?.rebase,
|
|
432
|
+
tags: options?.tags,
|
|
433
|
+
source: options?.source,
|
|
434
|
+
},
|
|
435
|
+
runOptions,
|
|
436
|
+
);
|
|
437
|
+
} finally {
|
|
438
|
+
this.context.hooks?.operations?.onRebaseCapableOperation?.(repoPath, 'pull', 'ended');
|
|
439
|
+
}
|
|
423
440
|
|
|
424
441
|
this.context.hooks?.cache?.onReset?.(repoPath, 'branches', 'status', 'tags');
|
|
425
442
|
this.context.hooks?.repository?.onChanged?.(repoPath, ['head', 'heads', 'remotes', 'index']);
|
|
@@ -721,8 +738,10 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
|
|
|
721
738
|
env = { ...env, GIT_EDITOR: options.messageEditor };
|
|
722
739
|
}
|
|
723
740
|
|
|
724
|
-
if (options?.updateRefs) {
|
|
741
|
+
if (options?.updateRefs === true) {
|
|
725
742
|
args.push('--update-refs');
|
|
743
|
+
} else if (options?.updateRefs === false) {
|
|
744
|
+
args.push('--no-update-refs');
|
|
726
745
|
}
|
|
727
746
|
|
|
728
747
|
if (options?.onto) {
|
|
@@ -735,6 +754,9 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
|
|
|
735
754
|
args.push(options.branch);
|
|
736
755
|
}
|
|
737
756
|
|
|
757
|
+
// Fired before the process runs — `git rebase` exits 0 when stopping at `edit`/`break`,
|
|
758
|
+
// so the result can't be used to detect that a rebase started
|
|
759
|
+
this.context.hooks?.operations?.onRebaseCapableOperation?.(repoPath, 'rebase', 'started');
|
|
738
760
|
try {
|
|
739
761
|
await this.git.run(
|
|
740
762
|
// Avoid a timeout since rebases can take a long time (set to 0 to disable)
|
|
@@ -761,9 +783,12 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
|
|
|
761
783
|
),
|
|
762
784
|
);
|
|
763
785
|
if (RebaseError.is(mapped, 'conflicts')) {
|
|
764
|
-
return this.createConflictResult(repoPath, 'rebase');
|
|
786
|
+
return await this.createConflictResult(repoPath, 'rebase');
|
|
765
787
|
}
|
|
766
788
|
throw mapped;
|
|
789
|
+
} finally {
|
|
790
|
+
// The command exited (success, pause, or failure) — any rebase it left behind is on disk now
|
|
791
|
+
this.context.hooks?.operations?.onRebaseCapableOperation?.(repoPath, 'rebase', 'ended');
|
|
767
792
|
}
|
|
768
793
|
}
|
|
769
794
|
|