@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
|
@@ -159,6 +159,13 @@ export class BranchesGitSubProvider {
|
|
|
159
159
|
const worktreePathSupported = supported.includes('git:for-each-ref:worktreePath');
|
|
160
160
|
// If we don't get any data, assume the repo doesn't have any commits yet so check if we have a current branch
|
|
161
161
|
if (!records.length) {
|
|
162
|
+
// Never CACHE that conclusion. `getRefs` reports a failed enumeration the same way as a
|
|
163
|
+
// genuinely ref-less repo — an empty array (it invalidates its own entry but doesn't
|
|
164
|
+
// throw) — so a transient `for-each-ref` failure would otherwise pin this degraded
|
|
165
|
+
// current-branch-only list in a map with no TTL, and every branch consumer would see a
|
|
166
|
+
// one-branch repo for the session. A truly empty repo re-reads cheaply, so refusing the
|
|
167
|
+
// entry costs nothing in the case this is actually meant to serve.
|
|
168
|
+
cacheable?.invalidate();
|
|
162
169
|
const current = await this.getCurrentBranch(commonPath, metadataMap, options?.ordering ?? this.context.config?.commits.ordering ?? undefined, signal);
|
|
163
170
|
return current != null ? { values: [current] } : emptyPagedResult;
|
|
164
171
|
}
|
|
@@ -375,14 +382,20 @@ export class BranchesGitSubProvider {
|
|
|
375
382
|
if (branch == null)
|
|
376
383
|
return undefined;
|
|
377
384
|
mergeTarget = `${branch.remoteName}/${pr.refs.base.branch}`;
|
|
378
|
-
//
|
|
379
|
-
//
|
|
380
|
-
//
|
|
381
|
-
//
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
385
|
+
// A stacked PR's base is the layer below it, which is deleted when the stack merges.
|
|
386
|
+
// Storing it would leave a stored target naming a branch that no longer exists — and
|
|
387
|
+
// a stored target beats every other tier, so it would keep winning until cleared by
|
|
388
|
+
// hand. Use it for this lookup, but don't persist it.
|
|
389
|
+
if (pr.stack == null) {
|
|
390
|
+
// Store for future reuse — turns the next call into a Tier 1 path. Skip the
|
|
391
|
+
// `branchOverviews` invalidation that the write would otherwise trigger: we're
|
|
392
|
+
// about to populate `${ref}|${mergeTarget}` with this exact target, so evicting
|
|
393
|
+
// it would just force the next caller to redo the body. (gk-merge-target writes
|
|
394
|
+
// do not affect `baseBranchName`, so no need to skip that target.)
|
|
395
|
+
void this.storeMergeTargetBranchName(repoPath, ref, mergeTarget, {
|
|
396
|
+
skipInvalidation: ['branchOverviews'],
|
|
397
|
+
});
|
|
398
|
+
}
|
|
386
399
|
}
|
|
387
400
|
}
|
|
388
401
|
// Tier 3: Base branch, then default branch.
|
|
@@ -539,37 +552,62 @@ export class BranchesGitSubProvider {
|
|
|
539
552
|
remote ??= 'origin';
|
|
540
553
|
const priority = options?.priority;
|
|
541
554
|
const priorityOpts = priority != null ? { priority: priority } : undefined;
|
|
542
|
-
// Local-only: read just the existing local symref, never contacting the remote.
|
|
543
|
-
//
|
|
544
|
-
//
|
|
545
|
-
//
|
|
555
|
+
// Local-only: read just the existing local symref, never contacting the remote. Cached under a
|
|
556
|
+
// `${remote}:local` key — a colon can't appear in a refname component, so it can never collide
|
|
557
|
+
// with a real remote name used by the networked variant below. Invalidated by the same
|
|
558
|
+
// remotes/branches cache cascades as the networked lookup.
|
|
546
559
|
if (options?.local) {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
560
|
+
return this.cache.getDefaultBranchName(repoPath, `${remote}:local`, async (commonPath, cacheable, signal) => {
|
|
561
|
+
try {
|
|
562
|
+
// Bind to the aggregate `signal` (fires only when ALL current callers abort), not
|
|
563
|
+
// this-caller's `cancellation` — otherwise a superseded caller's abort would kill the
|
|
564
|
+
// shared command and reject concurrent riders that never cancelled.
|
|
565
|
+
const result = await this.git.run({ cwd: commonPath, cancellation: signal ?? cancellation, ...priorityOpts }, 'symbolic-ref', '--short', `refs/remotes/${remote}/HEAD`);
|
|
566
|
+
return result.stdout.trim() || undefined;
|
|
567
|
+
}
|
|
568
|
+
catch (ex) {
|
|
569
|
+
if (isCancellationError(ex))
|
|
570
|
+
throw ex;
|
|
571
|
+
// `symbolic-ref` on an absent `refs/remotes/<remote>/HEAD` exits fatal rather than
|
|
572
|
+
// empty, and that message matches no `GitWarnings` entry, so it arrives here as a
|
|
573
|
+
// throw. It is still an ANSWER — the ref genuinely isn't set — and the overwhelmingly
|
|
574
|
+
// common one, so cache it. Invalidating instead means every caller re-probes and the
|
|
575
|
+
// whole point of caching this lookup is lost.
|
|
576
|
+
const stderr = ex.stderr ?? '';
|
|
577
|
+
if (stderr.includes('is not a symbolic ref'))
|
|
578
|
+
return undefined;
|
|
579
|
+
// Anything else never produced an answer (spawn failure, queue rejection). This entry
|
|
580
|
+
// has no TTL, so caching that would pin "no default branch" for the session.
|
|
581
|
+
cacheable.invalidate();
|
|
582
|
+
return undefined;
|
|
583
|
+
}
|
|
584
|
+
}, cancellation);
|
|
556
585
|
}
|
|
557
|
-
return this.cache.getDefaultBranchName(repoPath, remote, async (commonPath) => {
|
|
586
|
+
return this.cache.getDefaultBranchName(repoPath, remote, async (commonPath, cacheable, signal) => {
|
|
558
587
|
let retried = false;
|
|
559
588
|
while (true) {
|
|
560
589
|
try {
|
|
561
|
-
const result = await this.git.run({ cwd: commonPath, cancellation: cancellation, ...priorityOpts }, 'symbolic-ref', '--short', `refs/remotes/${remote}/HEAD`);
|
|
590
|
+
const result = await this.git.run({ cwd: commonPath, cancellation: signal ?? cancellation, ...priorityOpts }, 'symbolic-ref', '--short', `refs/remotes/${remote}/HEAD`);
|
|
562
591
|
return result.stdout.trim() || undefined;
|
|
563
592
|
}
|
|
564
593
|
catch (ex) {
|
|
594
|
+
// Only "no HEAD ref configured" is an ANSWER here; every other throw means the lookup
|
|
595
|
+
// never happened. This entry has no TTL, so caching a failure — an offline `ls-remote`
|
|
596
|
+
// being the likely one — would pin "no default branch" for the session.
|
|
597
|
+
let answered = false;
|
|
565
598
|
if (/is not a symbolic ref/.test(ex.stderr)) {
|
|
566
599
|
try {
|
|
567
600
|
if (!retried) {
|
|
568
601
|
retried = true;
|
|
569
|
-
await this.git.run({ cwd: commonPath, cancellation: cancellation, ...priorityOpts }, 'remote', 'set-head', '-a', remote);
|
|
602
|
+
await this.git.run({ cwd: commonPath, cancellation: signal ?? cancellation, ...priorityOpts }, 'remote', 'set-head', '-a', remote);
|
|
603
|
+
// This just created `refs/remotes/<remote>/HEAD`, which the `local` variant
|
|
604
|
+
// may already have cached a miss for. The watcher would also catch the new
|
|
605
|
+
// ref, but evict directly so consumers of this package that don't wire up a
|
|
606
|
+
// watcher aren't stuck with that miss.
|
|
607
|
+
this.cache.deleteDefaultBranchName(commonPath, `${remote}:local`);
|
|
570
608
|
continue;
|
|
571
609
|
}
|
|
572
|
-
const result = await this.git.run({ cwd: commonPath, cancellation: cancellation, ...priorityOpts }, 'ls-remote', '--symref', remote, 'HEAD');
|
|
610
|
+
const result = await this.git.run({ cwd: commonPath, cancellation: signal ?? cancellation, ...priorityOpts }, 'ls-remote', '--symref', remote, 'HEAD');
|
|
573
611
|
if (result.stdout) {
|
|
574
612
|
const match = /ref:\s(\S+)\s+HEAD/m.exec(result.stdout);
|
|
575
613
|
if (match != null) {
|
|
@@ -577,16 +615,21 @@ export class BranchesGitSubProvider {
|
|
|
577
615
|
return `${remote}/${branch.substring('refs/heads/'.length).trim()}`;
|
|
578
616
|
}
|
|
579
617
|
}
|
|
618
|
+
// `ls-remote` ran and the remote advertised no HEAD — a real "none".
|
|
619
|
+
answered = true;
|
|
580
620
|
}
|
|
581
621
|
catch {
|
|
582
622
|
if (isCancellationError(ex))
|
|
583
623
|
throw ex;
|
|
584
624
|
}
|
|
585
625
|
}
|
|
626
|
+
if (!answered) {
|
|
627
|
+
cacheable.invalidate();
|
|
628
|
+
}
|
|
586
629
|
return undefined;
|
|
587
630
|
}
|
|
588
631
|
}
|
|
589
|
-
});
|
|
632
|
+
}, cancellation);
|
|
590
633
|
}
|
|
591
634
|
async createBranch(repoPath, name, ref, options) {
|
|
592
635
|
const args = ['branch', name, ref];
|
|
@@ -595,6 +638,12 @@ export class BranchesGitSubProvider {
|
|
|
595
638
|
}
|
|
596
639
|
try {
|
|
597
640
|
await this.git.run({ cwd: repoPath }, ...args);
|
|
641
|
+
// Evict the cached base so the new branch re-derives its own. The PERSISTED `branch.<ref>.gk-*`
|
|
642
|
+
// keys are left alone: they're dropped when GitLens deletes a branch, never when one is created.
|
|
643
|
+
// Removing them here would mean treating a reused name as proof of a different branch, which
|
|
644
|
+
// nothing can establish — so the trade is that a branch reusing a name deleted outside GitLens
|
|
645
|
+
// inherits the orphaned section.
|
|
646
|
+
this.cache.deleteBaseBranchName(repoPath, name);
|
|
598
647
|
this.context.hooks?.cache?.onReset?.(repoPath, 'branches');
|
|
599
648
|
this.context.hooks?.repository?.onChanged?.(repoPath, ['heads']);
|
|
600
649
|
}
|
|
@@ -612,10 +661,30 @@ export class BranchesGitSubProvider {
|
|
|
612
661
|
const args = ['branch', options?.force ? '-D' : '-d', ...branches];
|
|
613
662
|
try {
|
|
614
663
|
await this.git.run({ cwd: repoPath }, ...args);
|
|
664
|
+
for (const branch of branches) {
|
|
665
|
+
this.cache.deleteBaseBranchName(repoPath, branch);
|
|
666
|
+
// Drop the persisted gk metadata too, not just the cached resolution. `getBaseBranchName`
|
|
667
|
+
// reads `branch.<ref>.gk-merge-base` before falling back to the reflog, so leaving it behind
|
|
668
|
+
// would hand a later branch reusing this name its predecessor's base — evicting the cache
|
|
669
|
+
// alone just forces a re-derivation that reads the same stale value back.
|
|
670
|
+
await this.provider.config.removeGkConfigBranchSection(repoPath, branch);
|
|
671
|
+
}
|
|
615
672
|
this.context.hooks?.cache?.onReset?.(repoPath, 'branches');
|
|
616
673
|
this.context.hooks?.repository?.onChanged?.(repoPath, ['heads']);
|
|
617
674
|
}
|
|
618
675
|
catch (ex) {
|
|
676
|
+
// A multi-name delete partially succeeds: `git branch -d a bad c` removes `a` and `c`, refuses
|
|
677
|
+
// `bad`, and still exits non-zero — so the loop above never ran for the branches that did go.
|
|
678
|
+
// Clean those up here rather than leave persisted gk metadata behind for a name a later branch
|
|
679
|
+
// can reuse.
|
|
680
|
+
const anyDeleted = await this.cleanupDeletedBranchMetadata(repoPath, branches);
|
|
681
|
+
// The success path's notifications never ran, but some branches DID go — without these the
|
|
682
|
+
// branch caches stay stale and consumers wait on the filesystem watcher (which never fires
|
|
683
|
+
// for a packed ref).
|
|
684
|
+
if (anyDeleted) {
|
|
685
|
+
this.context.hooks?.cache?.onReset?.(repoPath, 'branches');
|
|
686
|
+
this.context.hooks?.repository?.onChanged?.(repoPath, ['heads']);
|
|
687
|
+
}
|
|
619
688
|
if (ex instanceof BranchError) {
|
|
620
689
|
throw ex.update({
|
|
621
690
|
action: options?.force ? 'force delete' : 'delete',
|
|
@@ -630,6 +699,54 @@ export class BranchesGitSubProvider {
|
|
|
630
699
|
}, ex);
|
|
631
700
|
}
|
|
632
701
|
}
|
|
702
|
+
/**
|
|
703
|
+
* Drops the cached and persisted per-branch metadata for whichever of `names` no longer exists. Only
|
|
704
|
+
* for the partial-failure path — one extra `for-each-ref` is cheap while already handling an error,
|
|
705
|
+
* and keeps the success path free of it.
|
|
706
|
+
*
|
|
707
|
+
* Returns whether any of `names` is confirmed gone; `false` when the probe couldn't answer, so a
|
|
708
|
+
* spurious notification is never fired on an unrelated failure.
|
|
709
|
+
*/
|
|
710
|
+
async cleanupDeletedBranchMetadata(repoPath, names) {
|
|
711
|
+
try {
|
|
712
|
+
const result = await this.git.run({ cwd: repoPath, errors: 'ignore' }, 'for-each-ref',
|
|
713
|
+
// Full refname, stripped here rather than by git. NOT `%(refname:short)`: that yields the
|
|
714
|
+
// shortest UNAMBIGUOUS name, so a branch with a same-named tag comes back as `heads/<name>`,
|
|
715
|
+
// never matches the requested name, and would read as deleted — wiping a live branch's
|
|
716
|
+
// metadata. `%(refname:strip=2)` also solves that, but `%(refname)` is the oldest, plainest
|
|
717
|
+
// field and needs no assumption about which git version gained `strip`.
|
|
718
|
+
'--format=%(refname)', ...names.map(n => `refs/heads/${n}`));
|
|
719
|
+
// `errors: 'ignore'` means a failed probe RESOLVES empty, which is indistinguishable from "none
|
|
720
|
+
// of them survived" — and would delete every name's metadata, including user-owned keys like
|
|
721
|
+
// `gk-disposition` and `gk-associated-issues`. Only act on a genuine answer.
|
|
722
|
+
//
|
|
723
|
+
// BOTH conditions: `exitCode !== 0` catches a probe git ran and rejected, `status !== 'exited'`
|
|
724
|
+
// catches one that never produced an answer — a spawn failure or queue rejection (no exit code at
|
|
725
|
+
// all), or a swallowed warning (which carries git's real code, so the exit-code test alone can
|
|
726
|
+
// still read it as "none survived").
|
|
727
|
+
if (result.completion.status !== 'exited' || result.exitCode !== 0)
|
|
728
|
+
return false;
|
|
729
|
+
const prefix = 'refs/heads/';
|
|
730
|
+
const surviving = new Set(result.stdout
|
|
731
|
+
.split('\n')
|
|
732
|
+
.map(l => l.trim())
|
|
733
|
+
.filter(l => l.startsWith(prefix))
|
|
734
|
+
.map(l => l.substring(prefix.length)));
|
|
735
|
+
let anyDeleted = false;
|
|
736
|
+
for (const name of names) {
|
|
737
|
+
if (surviving.has(name))
|
|
738
|
+
continue;
|
|
739
|
+
anyDeleted = true;
|
|
740
|
+
this.cache.deleteBaseBranchName(repoPath, name);
|
|
741
|
+
await this.provider.config.removeGkConfigBranchSection(repoPath, name);
|
|
742
|
+
}
|
|
743
|
+
return anyDeleted;
|
|
744
|
+
}
|
|
745
|
+
catch {
|
|
746
|
+
// Best-effort bookkeeping while already unwinding a failure — never mask the original error.
|
|
747
|
+
return false;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
633
750
|
async deleteRemoteBranch(repoPath, names, remote) {
|
|
634
751
|
const branches = ensureArray(names);
|
|
635
752
|
const args = ['push', '-d', remote, ...branches];
|
|
@@ -831,7 +948,7 @@ export class BranchesGitSubProvider {
|
|
|
831
948
|
return { status: 'clean' };
|
|
832
949
|
const mergeConflict = parseMergeTreeConflict(data);
|
|
833
950
|
if (!mergeConflict.conflicts.length)
|
|
834
|
-
return { status: 'clean' };
|
|
951
|
+
return { status: 'clean', treeOid: mergeConflict.treeOid };
|
|
835
952
|
return {
|
|
836
953
|
status: 'conflicts',
|
|
837
954
|
conflict: {
|
|
@@ -841,9 +958,99 @@ export class BranchesGitSubProvider {
|
|
|
841
958
|
files: mergeConflict.conflicts,
|
|
842
959
|
shas: undefined, // Merge conflicts don't have a specific commit SHA
|
|
843
960
|
},
|
|
961
|
+
treeOid: mergeConflict.treeOid,
|
|
962
|
+
};
|
|
963
|
+
}, { cancellation: cancellation });
|
|
964
|
+
}
|
|
965
|
+
/** Detects conflicts when an autostash is reapplied onto the tree an integration produced */
|
|
966
|
+
async getPotentialStashReapplyConflicts(repoPath, ontoTreeOid, cancellation) {
|
|
967
|
+
// Requires Git v2.38+ for --write-tree with 3-arg form
|
|
968
|
+
if (!(await this.git.supports('git:merge-tree:write-tree'))) {
|
|
969
|
+
return createConflictDetectionError('unsupported');
|
|
970
|
+
}
|
|
971
|
+
// git stash create writes commit objects but touches neither the working tree nor the stash ref, and
|
|
972
|
+
// captures tracked changes only — which is exactly what autostash does
|
|
973
|
+
let stashSha;
|
|
974
|
+
try {
|
|
975
|
+
const result = await this.git.run({ cwd: repoPath, cancellation: cancellation, errors: 'throw' }, 'stash', 'create');
|
|
976
|
+
stashSha = result.stdout.trim();
|
|
977
|
+
}
|
|
978
|
+
catch (ex) {
|
|
979
|
+
if (isCancellationError(ex))
|
|
980
|
+
throw ex;
|
|
981
|
+
return createConflictDetectionError('other');
|
|
982
|
+
}
|
|
983
|
+
if (!stashSha)
|
|
984
|
+
return { status: 'clean' };
|
|
985
|
+
// Key on what the simulation actually consumes — the stash's tree and its first parent (the HEAD it was
|
|
986
|
+
// taken against, and so the merge base). The stash SHA itself embeds a commit timestamp, so two runs over
|
|
987
|
+
// an identical tree produce different SHAs and a SHA-keyed entry could never be hit.
|
|
988
|
+
let stashTree;
|
|
989
|
+
let stashParent;
|
|
990
|
+
try {
|
|
991
|
+
const result = await this.git.run({ cwd: repoPath, cancellation: cancellation, errors: 'throw' }, 'rev-parse', `${stashSha}^{tree}`, `${stashSha}^`);
|
|
992
|
+
[stashTree, stashParent] = result.stdout.trim().split('\n');
|
|
993
|
+
}
|
|
994
|
+
catch (ex) {
|
|
995
|
+
if (isCancellationError(ex))
|
|
996
|
+
throw ex;
|
|
997
|
+
return createConflictDetectionError('other');
|
|
998
|
+
}
|
|
999
|
+
const cacheKey = `reapply:${ontoTreeOid}:${stashTree}.${stashParent}`;
|
|
1000
|
+
return this.cache.conflictDetection.getOrCreate(repoPath, cacheKey, async (_cacheable, signal) => {
|
|
1001
|
+
// The stash commit's first parent is the HEAD it was taken against, which is the correct merge
|
|
1002
|
+
// base for a reapply — ours is the post-integration tree, theirs is the stash
|
|
1003
|
+
const step = await this.runMergeTreeStep(repoPath, `${stashSha}^`, ontoTreeOid, stashSha, signal);
|
|
1004
|
+
if (!step.ok)
|
|
1005
|
+
return createConflictDetectionError(step.reason);
|
|
1006
|
+
if (!step.result.conflicts.length)
|
|
1007
|
+
return { status: 'clean', treeOid: step.result.treeOid };
|
|
1008
|
+
return {
|
|
1009
|
+
status: 'conflicts',
|
|
1010
|
+
conflict: {
|
|
1011
|
+
repoPath: repoPath,
|
|
1012
|
+
branch: stashSha,
|
|
1013
|
+
target: ontoTreeOid,
|
|
1014
|
+
files: step.result.conflicts,
|
|
1015
|
+
shas: undefined,
|
|
1016
|
+
},
|
|
1017
|
+
treeOid: step.result.treeOid,
|
|
844
1018
|
};
|
|
845
1019
|
}, { cancellation: cancellation });
|
|
846
1020
|
}
|
|
1021
|
+
async runMergeTreeStep(repoPath, base, ours, theirs, cancellation) {
|
|
1022
|
+
const scope = getScopedLogger();
|
|
1023
|
+
let data;
|
|
1024
|
+
try {
|
|
1025
|
+
const result = await this.git.run({ cwd: repoPath, cancellation: cancellation, errors: 'throw' }, 'merge-tree', '--write-tree', '-z', '--name-only', '--no-messages', `--merge-base=${base}`, ours, theirs);
|
|
1026
|
+
data = result.stdout;
|
|
1027
|
+
}
|
|
1028
|
+
catch (ex) {
|
|
1029
|
+
if (isCancellationError(ex))
|
|
1030
|
+
throw ex;
|
|
1031
|
+
const msg = ex?.toString() ?? '';
|
|
1032
|
+
if (GitErrors.notAValidObjectName.test(msg)) {
|
|
1033
|
+
scope?.error(ex, `'${ours}' or '${theirs}' not found - ensure the branches/commits exist`);
|
|
1034
|
+
return { ok: false, reason: 'refNotFound' };
|
|
1035
|
+
}
|
|
1036
|
+
else if (GitErrors.badRevision.test(msg)) {
|
|
1037
|
+
scope?.error(ex, `Invalid revision: ${msg.slice(msg.indexOf("'"))}`);
|
|
1038
|
+
return { ok: false, reason: 'refNotFound' };
|
|
1039
|
+
}
|
|
1040
|
+
else if (GitErrors.noMergeBase.test(msg)) {
|
|
1041
|
+
scope?.error(ex, `Unable to merge '${ours}' and '${theirs}' as they have no common ancestor`);
|
|
1042
|
+
return { ok: false, reason: 'noMergeBase' };
|
|
1043
|
+
}
|
|
1044
|
+
else if (ex instanceof GitError) {
|
|
1045
|
+
data = ex.stdout;
|
|
1046
|
+
}
|
|
1047
|
+
else {
|
|
1048
|
+
scope?.error(ex, 'Failed to execute merge-tree for conflict check');
|
|
1049
|
+
return { ok: false, reason: 'other' };
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
return { ok: true, result: parseMergeTreeConflict(data ?? '') };
|
|
1053
|
+
}
|
|
847
1054
|
async checkForPotentialConflicts(repoPath, commits, targetBranch, cancellation, options) {
|
|
848
1055
|
const scope = getScopedLogger();
|
|
849
1056
|
// Requires Git v2.38+ for --write-tree with 3-arg form
|
|
@@ -867,44 +1074,16 @@ export class BranchesGitSubProvider {
|
|
|
867
1074
|
for (const commit of commits) {
|
|
868
1075
|
if (cancellation?.aborted)
|
|
869
1076
|
throw new CancellationError();
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
}
|
|
881
|
-
catch (ex) {
|
|
882
|
-
if (isCancellationError(ex))
|
|
883
|
-
throw ex;
|
|
884
|
-
const msg = ex?.toString() ?? '';
|
|
885
|
-
if (GitErrors.notAValidObjectName.test(msg)) {
|
|
886
|
-
scope?.error(ex, `'${targetBranch}' or '${commit.sha}' not found - ensure the branches/commits exist`);
|
|
887
|
-
return createConflictDetectionError('refNotFound');
|
|
888
|
-
}
|
|
889
|
-
else if (GitErrors.badRevision.test(msg)) {
|
|
890
|
-
scope?.error(ex, `Invalid revision: ${msg.slice(msg.indexOf("'"))}`);
|
|
891
|
-
return createConflictDetectionError('refNotFound');
|
|
892
|
-
}
|
|
893
|
-
else if (GitErrors.noMergeBase.test(msg)) {
|
|
894
|
-
scope?.error(ex, `Unable to merge '${commit.sha}' and '${targetBranch}' as they have no common ancestor`);
|
|
895
|
-
return createConflictDetectionError('noMergeBase');
|
|
896
|
-
}
|
|
897
|
-
else if (ex instanceof GitError) {
|
|
898
|
-
data = ex.stdout;
|
|
899
|
-
}
|
|
900
|
-
else {
|
|
901
|
-
scope?.error(ex, 'Failed to execute merge-tree for conflict check');
|
|
902
|
-
return createConflictDetectionError('other');
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
if (!data)
|
|
906
|
-
continue;
|
|
907
|
-
const mergeConflict = parseMergeTreeConflict(data);
|
|
1077
|
+
// Use merge-tree --write-tree with --merge-base to simulate cherry-pick:
|
|
1078
|
+
// merge-tree --write-tree --merge-base=<commit-parent> <current-tree> <commit>
|
|
1079
|
+
// This performs a 3-way merge where:
|
|
1080
|
+
// - base = commit.parent (where the commit started)
|
|
1081
|
+
// - ours = currentTreeOid (current state of the target)
|
|
1082
|
+
// - theirs = commit.sha (what we're cherry-picking)
|
|
1083
|
+
const step = await this.runMergeTreeStep(repoPath, commit.parent, currentTreeOid, commit.sha, cancellation);
|
|
1084
|
+
if (!step.ok)
|
|
1085
|
+
return createConflictDetectionError(step.reason);
|
|
1086
|
+
const mergeConflict = step.result;
|
|
908
1087
|
if (mergeConflict.conflicts.length) {
|
|
909
1088
|
conflictingShas.push(commit.sha);
|
|
910
1089
|
for (const file of mergeConflict.conflicts) {
|
|
@@ -921,6 +1100,7 @@ export class BranchesGitSubProvider {
|
|
|
921
1100
|
shas: [commit.sha],
|
|
922
1101
|
},
|
|
923
1102
|
stoppedOnFirstConflict: true,
|
|
1103
|
+
treeOid: mergeConflict.treeOid || undefined,
|
|
924
1104
|
};
|
|
925
1105
|
}
|
|
926
1106
|
}
|
|
@@ -941,14 +1121,15 @@ export class BranchesGitSubProvider {
|
|
|
941
1121
|
shas: conflictingShas,
|
|
942
1122
|
},
|
|
943
1123
|
stoppedOnFirstConflict: false,
|
|
1124
|
+
treeOid: currentTreeOid,
|
|
944
1125
|
};
|
|
945
1126
|
}
|
|
946
|
-
return { status: 'clean' };
|
|
1127
|
+
return { status: 'clean', treeOid: currentTreeOid };
|
|
947
1128
|
}
|
|
948
1129
|
getBaseBranchName(repoPath, ref, options, cancellation) {
|
|
949
1130
|
const priority = options?.priority;
|
|
950
1131
|
const priorityOpts = priority != null ? { priority: priority } : undefined;
|
|
951
|
-
return this.cache.getBaseBranchName(repoPath, ref, async (commonPath, signal) => {
|
|
1132
|
+
return this.cache.getBaseBranchName(repoPath, ref, async (commonPath, cacheable, signal) => {
|
|
952
1133
|
try {
|
|
953
1134
|
// getGkConfig has built-in fallback to regular config for backward compatibility
|
|
954
1135
|
let mergeBase = await this.provider.config.getGkConfig(commonPath, `branch.${ref}.gk-merge-base`);
|
|
@@ -974,17 +1155,27 @@ export class BranchesGitSubProvider {
|
|
|
974
1155
|
}
|
|
975
1156
|
}
|
|
976
1157
|
catch { }
|
|
977
|
-
const
|
|
978
|
-
if (branch != null) {
|
|
1158
|
+
const reflog = await this.getBaseBranchFromReflog(commonPath, ref, { upstream: true, priority: priority }, signal);
|
|
1159
|
+
if (reflog.branch != null) {
|
|
979
1160
|
// Self-write: see comment on the migration path above.
|
|
980
|
-
void this.storeBaseBranchName(commonPath, ref, branch, {
|
|
1161
|
+
void this.storeBaseBranchName(commonPath, ref, reflog.branch, {
|
|
981
1162
|
skipInvalidation: ['branchOverviews', 'baseBranchName'],
|
|
982
1163
|
});
|
|
983
|
-
return branch;
|
|
1164
|
+
return reflog.branch;
|
|
1165
|
+
}
|
|
1166
|
+
// This entry has no TTL and the `'branches'` cascade no longer clears it (a tip move can't
|
|
1167
|
+
// change a base), so a failed read cached as `undefined` would read as "no base branch" for
|
|
1168
|
+
// the rest of the session. Only a reflog that actually RAN and found nothing is an answer.
|
|
1169
|
+
if (reflog.failed) {
|
|
1170
|
+
cacheable.invalidate();
|
|
984
1171
|
}
|
|
985
1172
|
return undefined;
|
|
986
1173
|
}, cancellation);
|
|
987
1174
|
}
|
|
1175
|
+
/**
|
|
1176
|
+
* `failed` separates "the reflog ran and had no answer" from "the read never happened" — the caller
|
|
1177
|
+
* caches the former forever and must not cache the latter.
|
|
1178
|
+
*/
|
|
988
1179
|
async getBaseBranchFromReflog(repoPath, ref, options, cancellation) {
|
|
989
1180
|
const priority = options?.priority;
|
|
990
1181
|
const priorityOpts = priority != null ? { priority: priority } : undefined;
|
|
@@ -992,7 +1183,7 @@ export class BranchesGitSubProvider {
|
|
|
992
1183
|
let result = await this.git.run({ cwd: repoPath, cancellation: cancellation, ...priorityOpts }, 'reflog', ref, '--grep-reflog=branch: Created from *.');
|
|
993
1184
|
let entries = result.stdout.split('\n').filter(entry => Boolean(entry));
|
|
994
1185
|
if (entries.length !== 1)
|
|
995
|
-
return undefined;
|
|
1186
|
+
return { branch: undefined, failed: false };
|
|
996
1187
|
// Check if branch created from an explicit branch
|
|
997
1188
|
let match = entries[0].match(/branch: Created from (.*)$/);
|
|
998
1189
|
if (match?.length === 2) {
|
|
@@ -1001,36 +1192,39 @@ export class BranchesGitSubProvider {
|
|
|
1001
1192
|
if (options?.upstream) {
|
|
1002
1193
|
const upstream = await this.provider.refs.getSymbolicReferenceName(repoPath, `${name}@{u}`, priorityOpts);
|
|
1003
1194
|
if (upstream)
|
|
1004
|
-
return upstream;
|
|
1195
|
+
return { branch: upstream, failed: false };
|
|
1005
1196
|
}
|
|
1006
1197
|
name = await this.provider.refs.getSymbolicReferenceName(repoPath, name, priorityOpts);
|
|
1007
1198
|
if (name)
|
|
1008
|
-
return name;
|
|
1199
|
+
return { branch: name, failed: false };
|
|
1009
1200
|
}
|
|
1010
1201
|
}
|
|
1011
1202
|
// Check if branch was created from HEAD
|
|
1012
1203
|
result = await this.git.run({ cwd: repoPath, cancellation: cancellation, ...priorityOpts }, 'reflog', 'HEAD', `--grep-reflog=checkout: moving from .* to ${ref.replace('refs/heads/', '')}`);
|
|
1013
1204
|
entries = result.stdout.split('\n').filter(entry => Boolean(entry));
|
|
1014
1205
|
if (!entries.length)
|
|
1015
|
-
return undefined;
|
|
1206
|
+
return { branch: undefined, failed: false };
|
|
1016
1207
|
match = entries.at(-1).match(/checkout: moving from ([^\s]+)\s/);
|
|
1017
1208
|
if (match?.length === 2) {
|
|
1018
1209
|
let name = match[1];
|
|
1019
1210
|
if (options?.upstream) {
|
|
1020
1211
|
const upstream = await this.provider.refs.getSymbolicReferenceName(repoPath, `${name}@{u}`, priorityOpts);
|
|
1021
1212
|
if (upstream)
|
|
1022
|
-
return upstream;
|
|
1213
|
+
return { branch: upstream, failed: false };
|
|
1023
1214
|
}
|
|
1024
1215
|
name = await this.provider.refs.getSymbolicReferenceName(repoPath, name, priorityOpts);
|
|
1025
1216
|
if (name)
|
|
1026
|
-
return name;
|
|
1217
|
+
return { branch: name, failed: false };
|
|
1027
1218
|
}
|
|
1028
1219
|
}
|
|
1029
1220
|
catch (ex) {
|
|
1030
1221
|
if (isCancellationError(ex))
|
|
1031
1222
|
throw ex;
|
|
1223
|
+
// The reads use default error handling, so a spawn failure, queue rejection or swallowed
|
|
1224
|
+
// `GitWarnings` match lands here rather than producing an empty answer.
|
|
1225
|
+
return { branch: undefined, failed: true };
|
|
1032
1226
|
}
|
|
1033
|
-
return undefined;
|
|
1227
|
+
return { branch: undefined, failed: false };
|
|
1034
1228
|
}
|
|
1035
1229
|
async getStoredMergeTargetBranchName(repoPath, ref) {
|
|
1036
1230
|
const target = (await this.getStoredUserMergeTargetBranchName?.(repoPath, ref)) ??
|
|
@@ -1073,6 +1267,13 @@ export class BranchesGitSubProvider {
|
|
|
1073
1267
|
const args = ['branch', '-m', oldName, newName];
|
|
1074
1268
|
try {
|
|
1075
1269
|
await this.git.run({ cwd: repoPath }, ...args);
|
|
1270
|
+
// The branch keeps its identity across a rename, so move its stored gk metadata with it —
|
|
1271
|
+
// otherwise the new name re-derives a base it already had, and the old name is left holding
|
|
1272
|
+
// metadata for a branch that no longer exists under it.
|
|
1273
|
+
await this.provider.config.renameGkConfigBranchSection(repoPath, oldName, newName);
|
|
1274
|
+
// Old name's entry is now orphaned; the new name must not inherit a predecessor's base
|
|
1275
|
+
this.cache.deleteBaseBranchName(repoPath, oldName);
|
|
1276
|
+
this.cache.deleteBaseBranchName(repoPath, newName);
|
|
1076
1277
|
this.context.hooks?.cache?.onReset?.(repoPath, 'branches');
|
|
1077
1278
|
this.context.hooks?.repository?.onChanged?.(repoPath, ['heads']);
|
|
1078
1279
|
}
|
|
@@ -1214,6 +1415,9 @@ __decorate([
|
|
|
1214
1415
|
__decorate([
|
|
1215
1416
|
debug()
|
|
1216
1417
|
], BranchesGitSubProvider.prototype, "getPotentialMergeConflicts", null);
|
|
1418
|
+
__decorate([
|
|
1419
|
+
debug()
|
|
1420
|
+
], BranchesGitSubProvider.prototype, "getPotentialStashReapplyConflicts", null);
|
|
1217
1421
|
__decorate([
|
|
1218
1422
|
debug({ exit: true })
|
|
1219
1423
|
], BranchesGitSubProvider.prototype, "getBaseBranchName", null);
|