@gitkraken/core-gitlens 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (451) hide show
  1. package/CHANGELOG.md +25 -1
  2. package/dist/git/cache.d.ts +49 -9
  3. package/dist/git/cache.d.ts.map +1 -1
  4. package/dist/git/cache.js +174 -14
  5. package/dist/git/cache.js.map +1 -1
  6. package/dist/git/context.d.ts +11 -2
  7. package/dist/git/context.d.ts.map +1 -1
  8. package/dist/git/context.js.map +1 -1
  9. package/dist/git/models/branch.d.ts +1 -0
  10. package/dist/git/models/branch.d.ts.map +1 -1
  11. package/dist/git/models/branch.js.map +1 -1
  12. package/dist/git/models/commit.d.ts +1 -9
  13. package/dist/git/models/commit.d.ts.map +1 -1
  14. package/dist/git/models/commit.js +4 -9
  15. package/dist/git/models/commit.js.map +1 -1
  16. package/dist/git/models/graph.d.ts +2 -4
  17. package/dist/git/models/graph.d.ts.map +1 -1
  18. package/dist/git/models/reference.d.ts +31 -0
  19. package/dist/git/models/reference.d.ts.map +1 -1
  20. package/dist/git/models/remote.d.ts.map +1 -1
  21. package/dist/git/models/remote.js.map +1 -1
  22. package/dist/git/models/repository.d.ts +20 -3
  23. package/dist/git/models/repository.d.ts.map +1 -1
  24. package/dist/git/models/repository.js +42 -5
  25. package/dist/git/models/repository.js.map +1 -1
  26. package/dist/git/models/search.d.ts +2 -2
  27. package/dist/git/models/search.d.ts.map +1 -1
  28. package/dist/git/models/worktree.d.ts +4 -1
  29. package/dist/git/models/worktree.d.ts.map +1 -1
  30. package/dist/git/models/worktree.js +2 -2
  31. package/dist/git/models/worktree.js.map +1 -1
  32. package/dist/git/parsers/diffParser.d.ts +0 -1
  33. package/dist/git/parsers/diffParser.d.ts.map +1 -1
  34. package/dist/git/parsers/diffParser.js +22 -61
  35. package/dist/git/parsers/diffParser.js.map +1 -1
  36. package/dist/git/providers/branches.d.ts +18 -4
  37. package/dist/git/providers/branches.d.ts.map +1 -1
  38. package/dist/git/providers/commits.d.ts +6 -0
  39. package/dist/git/providers/commits.d.ts.map +1 -1
  40. package/dist/git/providers/config.d.ts +6 -1
  41. package/dist/git/providers/config.d.ts.map +1 -1
  42. package/dist/git/providers/diff.d.ts +2 -0
  43. package/dist/git/providers/diff.d.ts.map +1 -1
  44. package/dist/git/providers/operations.d.ts +39 -10
  45. package/dist/git/providers/operations.d.ts.map +1 -1
  46. package/dist/git/providers/pausedOperations.d.ts +3 -1
  47. package/dist/git/providers/pausedOperations.d.ts.map +1 -1
  48. package/dist/git/providers/refs.d.ts +23 -2
  49. package/dist/git/providers/refs.d.ts.map +1 -1
  50. package/dist/git/providers/revision.d.ts +1 -1
  51. package/dist/git/providers/revision.d.ts.map +1 -1
  52. package/dist/git/providers/staging.d.ts +18 -0
  53. package/dist/git/providers/staging.d.ts.map +1 -1
  54. package/dist/git/providers/status.d.ts +4 -1
  55. package/dist/git/providers/status.d.ts.map +1 -1
  56. package/dist/git/remotes/azure-devops.d.ts.map +1 -1
  57. package/dist/git/remotes/azure-devops.js.map +1 -1
  58. package/dist/git/{exec.types.d.ts → run.types.d.ts} +22 -3
  59. package/dist/git/run.types.d.ts.map +1 -0
  60. package/dist/git/run.types.js +2 -0
  61. package/dist/git/run.types.js.map +1 -0
  62. package/dist/git/service.d.ts.map +1 -1
  63. package/dist/git/service.js.map +1 -1
  64. package/dist/git/utils/autolink.utils.d.ts.map +1 -1
  65. package/dist/git/utils/autolink.utils.js.map +1 -1
  66. package/dist/git/utils/blame.utils.d.ts.map +1 -1
  67. package/dist/git/utils/blame.utils.js.map +1 -1
  68. package/dist/git/utils/conflictResolution.utils.d.ts +6 -0
  69. package/dist/git/utils/conflictResolution.utils.d.ts.map +1 -0
  70. package/dist/git/utils/conflictResolution.utils.js +25 -0
  71. package/dist/git/utils/conflictResolution.utils.js.map +1 -0
  72. package/dist/git/utils/fileStatus.utils.d.ts +2 -1
  73. package/dist/git/utils/fileStatus.utils.d.ts.map +1 -1
  74. package/dist/git/utils/fileStatus.utils.js +4 -0
  75. package/dist/git/utils/fileStatus.utils.js.map +1 -1
  76. package/dist/git/utils/search.utils.d.ts.map +1 -1
  77. package/dist/git/utils/search.utils.js +3 -0
  78. package/dist/git/utils/search.utils.js.map +1 -1
  79. package/dist/git/utils/sorting.d.ts +14 -0
  80. package/dist/git/utils/sorting.d.ts.map +1 -1
  81. package/dist/git/utils/sorting.js +34 -0
  82. package/dist/git/utils/sorting.js.map +1 -1
  83. package/dist/git/utils/status.utils.d.ts.map +1 -1
  84. package/dist/git/utils/status.utils.js.map +1 -1
  85. package/dist/git/watching/classifyChange.d.ts.map +1 -1
  86. package/dist/git/watching/classifyChange.js +8 -0
  87. package/dist/git/watching/classifyChange.js.map +1 -1
  88. package/dist/git/watching/gitIgnoreFilter.js +1 -1
  89. package/dist/git/watching/gitIgnoreFilter.js.map +1 -1
  90. package/dist/git/watching/initWatcher.d.ts.map +1 -1
  91. package/dist/git/watching/initWatcher.js.map +1 -1
  92. package/dist/git/watching/watchGroup.d.ts.map +1 -1
  93. package/dist/git/watching/watchGroup.js.map +1 -1
  94. package/dist/git/watching/watchService.d.ts.map +1 -1
  95. package/dist/git/watching/watchService.js +24 -4
  96. package/dist/git/watching/watchService.js.map +1 -1
  97. package/dist/git/watching/watchSession.d.ts.map +1 -1
  98. package/dist/git/watching/watchSession.js.map +1 -1
  99. package/dist/git-cli/cliGitProvider.js +1 -1
  100. package/dist/git-cli/cliGitProvider.js.map +1 -1
  101. package/dist/git-cli/exec/exec.types.d.ts +1 -1
  102. package/dist/git-cli/exec/exec.types.d.ts.map +1 -1
  103. package/dist/git-cli/exec/git.d.ts +31 -6
  104. package/dist/git-cli/exec/git.d.ts.map +1 -1
  105. package/dist/git-cli/exec/git.js +70 -9
  106. package/dist/git-cli/exec/git.js.map +1 -1
  107. package/dist/git-cli/exec/gitQueue.d.ts +22 -6
  108. package/dist/git-cli/exec/gitQueue.d.ts.map +1 -1
  109. package/dist/git-cli/exec/gitQueue.js +88 -24
  110. package/dist/git-cli/exec/gitQueue.js.map +1 -1
  111. package/dist/git-cli/parsers/blameParser.d.ts.map +1 -1
  112. package/dist/git-cli/parsers/blameParser.js.map +1 -1
  113. package/dist/git-cli/parsers/logParser.d.ts +1 -0
  114. package/dist/git-cli/parsers/logParser.d.ts.map +1 -1
  115. package/dist/git-cli/parsers/logParser.js +4 -0
  116. package/dist/git-cli/parsers/logParser.js.map +1 -1
  117. package/dist/git-cli/parsers/refParser.d.ts +3 -30
  118. package/dist/git-cli/parsers/refParser.d.ts.map +1 -1
  119. package/dist/git-cli/parsers/refParser.js +30 -30
  120. package/dist/git-cli/parsers/refParser.js.map +1 -1
  121. package/dist/git-cli/providers/blame.d.ts.map +1 -1
  122. package/dist/git-cli/providers/blame.js +1 -1
  123. package/dist/git-cli/providers/blame.js.map +1 -1
  124. package/dist/git-cli/providers/branches.d.ts +17 -5
  125. package/dist/git-cli/providers/branches.d.ts.map +1 -1
  126. package/dist/git-cli/providers/branches.js +245 -162
  127. package/dist/git-cli/providers/branches.js.map +1 -1
  128. package/dist/git-cli/providers/commits.d.ts +4 -0
  129. package/dist/git-cli/providers/commits.d.ts.map +1 -1
  130. package/dist/git-cli/providers/commits.js +120 -35
  131. package/dist/git-cli/providers/commits.js.map +1 -1
  132. package/dist/git-cli/providers/config.d.ts +11 -2
  133. package/dist/git-cli/providers/config.d.ts.map +1 -1
  134. package/dist/git-cli/providers/config.js +36 -11
  135. package/dist/git-cli/providers/config.js.map +1 -1
  136. package/dist/git-cli/providers/contributors.d.ts.map +1 -1
  137. package/dist/git-cli/providers/contributors.js +7 -4
  138. package/dist/git-cli/providers/contributors.js.map +1 -1
  139. package/dist/git-cli/providers/diff.d.ts +2 -0
  140. package/dist/git-cli/providers/diff.d.ts.map +1 -1
  141. package/dist/git-cli/providers/diff.js +92 -20
  142. package/dist/git-cli/providers/diff.js.map +1 -1
  143. package/dist/git-cli/providers/graph.d.ts.map +1 -1
  144. package/dist/git-cli/providers/graph.js +49 -28
  145. package/dist/git-cli/providers/graph.js.map +1 -1
  146. package/dist/git-cli/providers/operations.d.ts +30 -11
  147. package/dist/git-cli/providers/operations.d.ts.map +1 -1
  148. package/dist/git-cli/providers/operations.js +146 -43
  149. package/dist/git-cli/providers/operations.js.map +1 -1
  150. package/dist/git-cli/providers/patch.d.ts.map +1 -1
  151. package/dist/git-cli/providers/patch.js +15 -30
  152. package/dist/git-cli/providers/patch.js.map +1 -1
  153. package/dist/git-cli/providers/pausedOperations.d.ts +3 -1
  154. package/dist/git-cli/providers/pausedOperations.d.ts.map +1 -1
  155. package/dist/git-cli/providers/pausedOperations.js +13 -7
  156. package/dist/git-cli/providers/pausedOperations.js.map +1 -1
  157. package/dist/git-cli/providers/refs.d.ts +19 -2
  158. package/dist/git-cli/providers/refs.d.ts.map +1 -1
  159. package/dist/git-cli/providers/refs.js +312 -9
  160. package/dist/git-cli/providers/refs.js.map +1 -1
  161. package/dist/git-cli/providers/remotes.js +4 -4
  162. package/dist/git-cli/providers/remotes.js.map +1 -1
  163. package/dist/git-cli/providers/revision.d.ts +1 -1
  164. package/dist/git-cli/providers/revision.d.ts.map +1 -1
  165. package/dist/git-cli/providers/revision.js +15 -11
  166. package/dist/git-cli/providers/revision.js.map +1 -1
  167. package/dist/git-cli/providers/staging.d.ts +8 -0
  168. package/dist/git-cli/providers/staging.d.ts.map +1 -1
  169. package/dist/git-cli/providers/staging.js +52 -8
  170. package/dist/git-cli/providers/staging.js.map +1 -1
  171. package/dist/git-cli/providers/stash.d.ts +0 -8
  172. package/dist/git-cli/providers/stash.d.ts.map +1 -1
  173. package/dist/git-cli/providers/stash.js +63 -119
  174. package/dist/git-cli/providers/stash.js.map +1 -1
  175. package/dist/git-cli/providers/status.d.ts +4 -1
  176. package/dist/git-cli/providers/status.d.ts.map +1 -1
  177. package/dist/git-cli/providers/status.js +10 -9
  178. package/dist/git-cli/providers/status.js.map +1 -1
  179. package/dist/git-cli/providers/tags.d.ts.map +1 -1
  180. package/dist/git-cli/providers/tags.js +13 -9
  181. package/dist/git-cli/providers/tags.js.map +1 -1
  182. package/dist/git-cli/providers/worktrees.js +3 -3
  183. package/dist/git-cli/providers/worktrees.js.map +1 -1
  184. package/dist/ipc/discovery.d.ts +27 -0
  185. package/dist/ipc/discovery.d.ts.map +1 -0
  186. package/dist/ipc/discovery.js +38 -0
  187. package/dist/ipc/discovery.js.map +1 -0
  188. package/dist/ipc/ipcServer.d.ts +20 -0
  189. package/dist/ipc/ipcServer.d.ts.map +1 -0
  190. package/dist/ipc/ipcServer.js +162 -0
  191. package/dist/ipc/ipcServer.js.map +1 -0
  192. package/dist/plus/agents/providers/claudeCodeProvider.d.ts +114 -0
  193. package/dist/plus/agents/providers/claudeCodeProvider.d.ts.map +1 -0
  194. package/dist/plus/agents/providers/claudeCodeProvider.js +1480 -0
  195. package/dist/plus/agents/providers/claudeCodeProvider.js.map +1 -0
  196. package/dist/plus/agents/providers/claudeCodeTranscript.d.ts +36 -0
  197. package/dist/plus/agents/providers/claudeCodeTranscript.d.ts.map +1 -0
  198. package/dist/plus/agents/providers/claudeCodeTranscript.js +171 -0
  199. package/dist/plus/agents/providers/claudeCodeTranscript.js.map +1 -0
  200. package/dist/plus/agents/sanitizePrompt.d.ts +15 -0
  201. package/dist/plus/agents/sanitizePrompt.d.ts.map +1 -0
  202. package/dist/plus/agents/sanitizePrompt.js +103 -0
  203. package/dist/plus/agents/sanitizePrompt.js.map +1 -0
  204. package/dist/plus/agents/stateMachine.d.ts +32 -0
  205. package/dist/plus/agents/stateMachine.d.ts.map +1 -0
  206. package/dist/plus/agents/stateMachine.js +180 -0
  207. package/dist/plus/agents/stateMachine.js.map +1 -0
  208. package/dist/plus/agents/types.d.ts +203 -0
  209. package/dist/plus/agents/types.d.ts.map +1 -0
  210. package/dist/plus/agents/types.js +49 -0
  211. package/dist/plus/agents/types.js.map +1 -0
  212. package/dist/plus/ai/constants.d.ts +2 -1
  213. package/dist/plus/ai/constants.d.ts.map +1 -1
  214. package/dist/plus/ai/constants.js +7 -0
  215. package/dist/plus/ai/constants.js.map +1 -1
  216. package/dist/plus/ai/errors.d.ts +4 -1
  217. package/dist/plus/ai/errors.d.ts.map +1 -1
  218. package/dist/plus/ai/errors.js +38 -0
  219. package/dist/plus/ai/errors.js.map +1 -1
  220. package/dist/plus/ai/models/model.d.ts +1 -1
  221. package/dist/plus/ai/models/model.d.ts.map +1 -1
  222. package/dist/plus/ai/models/model.js.map +1 -1
  223. package/dist/plus/ai/models/promptTemplates.d.ts +28 -2
  224. package/dist/plus/ai/models/promptTemplates.d.ts.map +1 -1
  225. package/dist/plus/ai/models/results.d.ts +28 -0
  226. package/dist/plus/ai/models/results.d.ts.map +1 -1
  227. package/dist/plus/ai/prompts.d.ts +4 -0
  228. package/dist/plus/ai/prompts.d.ts.map +1 -1
  229. package/dist/plus/ai/prompts.js +167 -1
  230. package/dist/plus/ai/prompts.js.map +1 -1
  231. package/dist/plus/ai/utils/ai.utils.d.ts.map +1 -1
  232. package/dist/plus/ai/utils/ai.utils.js +2 -0
  233. package/dist/plus/ai/utils/ai.utils.js.map +1 -1
  234. package/dist/plus/ai/utils/results.utils.d.ts +3 -0
  235. package/dist/plus/ai/utils/results.utils.d.ts.map +1 -1
  236. package/dist/plus/ai/utils/results.utils.js +75 -0
  237. package/dist/plus/ai/utils/results.utils.js.map +1 -1
  238. package/dist/plus/ai/utils/truncation.utils.d.ts +1 -1
  239. package/dist/plus/ai/utils/truncation.utils.d.ts.map +1 -1
  240. package/dist/plus/ai/utils/truncation.utils.js.map +1 -1
  241. package/dist/plus/git-github/api/github.d.ts +10 -1
  242. package/dist/plus/git-github/api/github.d.ts.map +1 -1
  243. package/dist/plus/git-github/api/github.js +156 -49
  244. package/dist/plus/git-github/api/github.js.map +1 -1
  245. package/dist/plus/git-github/api/types.d.ts +8 -0
  246. package/dist/plus/git-github/api/types.d.ts.map +1 -1
  247. package/dist/plus/git-github/models.d.ts +1 -1
  248. package/dist/plus/git-github/models.d.ts.map +1 -1
  249. package/dist/plus/git-github/models.js.map +1 -1
  250. package/dist/plus/git-github/providers/github/blame.d.ts.map +1 -1
  251. package/dist/plus/git-github/providers/github/blame.js.map +1 -1
  252. package/dist/plus/git-github/providers/github/branches.d.ts +5 -1
  253. package/dist/plus/git-github/providers/github/branches.d.ts.map +1 -1
  254. package/dist/plus/git-github/providers/github/branches.js +60 -22
  255. package/dist/plus/git-github/providers/github/branches.js.map +1 -1
  256. package/dist/plus/git-github/providers/github/commits.d.ts.map +1 -1
  257. package/dist/plus/git-github/providers/github/commits.js +144 -92
  258. package/dist/plus/git-github/providers/github/commits.js.map +1 -1
  259. package/dist/plus/git-github/providers/github/diff.d.ts +3 -1
  260. package/dist/plus/git-github/providers/github/diff.d.ts.map +1 -1
  261. package/dist/plus/git-github/providers/github/diff.js +23 -9
  262. package/dist/plus/git-github/providers/github/diff.js.map +1 -1
  263. package/dist/plus/git-github/providers/github/refs.d.ts +9 -1
  264. package/dist/plus/git-github/providers/github/refs.d.ts.map +1 -1
  265. package/dist/plus/git-github/providers/github/refs.js +38 -13
  266. package/dist/plus/git-github/providers/github/refs.js.map +1 -1
  267. package/dist/plus/git-github/providers/github/revision.d.ts.map +1 -1
  268. package/dist/plus/git-github/providers/github/revision.js +1 -2
  269. package/dist/plus/git-github/providers/github/revision.js.map +1 -1
  270. package/dist/plus/git-github/providers/github/status.d.ts +4 -1
  271. package/dist/plus/git-github/providers/github/status.d.ts.map +1 -1
  272. package/dist/plus/git-github/providers/github/status.js +1 -1
  273. package/dist/plus/git-github/providers/github/status.js.map +1 -1
  274. package/dist/plus/git-github/providers/github/tags.d.ts.map +1 -1
  275. package/dist/plus/git-github/providers/github/tags.js +35 -36
  276. package/dist/plus/git-github/providers/github/tags.js.map +1 -1
  277. package/dist/plus/git-github/providers/githubGitProvider.d.ts.map +1 -1
  278. package/dist/plus/git-github/providers/githubGitProvider.js.map +1 -1
  279. package/dist/utils/color.d.ts.map +1 -1
  280. package/dist/utils/color.js.map +1 -1
  281. package/dist/utils/decorators/log.js +1 -1
  282. package/dist/utils/decorators/log.js.map +1 -1
  283. package/dist/utils/decorators/sequentialize.d.ts.map +1 -1
  284. package/dist/utils/decorators/sequentialize.js +4 -2
  285. package/dist/utils/decorators/sequentialize.js.map +1 -1
  286. package/dist/utils/decorators/serializable.d.ts.map +1 -1
  287. package/dist/utils/decorators/serializable.js.map +1 -1
  288. package/dist/utils/dedupedAsyncCache.d.ts +37 -0
  289. package/dist/utils/dedupedAsyncCache.d.ts.map +1 -0
  290. package/dist/utils/dedupedAsyncCache.js +68 -0
  291. package/dist/utils/dedupedAsyncCache.js.map +1 -0
  292. package/dist/utils/diff.d.ts +2 -0
  293. package/dist/utils/diff.d.ts.map +1 -0
  294. package/dist/utils/diff.js +53 -0
  295. package/dist/utils/diff.js.map +1 -0
  296. package/dist/utils/disposable.d.ts.map +1 -1
  297. package/dist/utils/disposable.js.map +1 -1
  298. package/dist/utils/env/node/base64.js.map +1 -1
  299. package/dist/utils/env/node/exec.d.ts.map +1 -1
  300. package/dist/utils/env/node/exec.js.map +1 -1
  301. package/dist/utils/error.d.ts.map +1 -1
  302. package/dist/utils/error.js.map +1 -1
  303. package/dist/utils/formatter.d.ts.map +1 -1
  304. package/dist/utils/formatter.js +4 -10
  305. package/dist/utils/formatter.js.map +1 -1
  306. package/dist/utils/iterable.d.ts.map +1 -1
  307. package/dist/utils/iterable.js.map +1 -1
  308. package/dist/utils/logger.d.ts.map +1 -1
  309. package/dist/utils/logger.js.map +1 -1
  310. package/dist/utils/lruMap.d.ts +49 -0
  311. package/dist/utils/lruMap.d.ts.map +1 -0
  312. package/dist/utils/lruMap.js +103 -0
  313. package/dist/utils/lruMap.js.map +1 -0
  314. package/dist/utils/markdown.d.ts.map +1 -1
  315. package/dist/utils/markdown.js.map +1 -1
  316. package/dist/utils/mru.d.ts.map +1 -1
  317. package/dist/utils/mru.js.map +1 -1
  318. package/dist/utils/path.d.ts.map +1 -1
  319. package/dist/utils/path.js.map +1 -1
  320. package/dist/utils/promise.d.ts +1 -0
  321. package/dist/utils/promise.d.ts.map +1 -1
  322. package/dist/utils/promise.js +17 -0
  323. package/dist/utils/promise.js.map +1 -1
  324. package/dist/utils/promiseCache.d.ts +10 -0
  325. package/dist/utils/promiseCache.d.ts.map +1 -1
  326. package/dist/utils/promiseCache.js +22 -0
  327. package/dist/utils/promiseCache.js.map +1 -1
  328. package/dist/utils/string.d.ts.map +1 -1
  329. package/dist/utils/string.js.map +1 -1
  330. package/dist/utils/uri.d.ts.map +1 -1
  331. package/dist/utils/uri.js.map +1 -1
  332. package/dist/utils/version.d.ts +14 -0
  333. package/dist/utils/version.d.ts.map +1 -1
  334. package/dist/utils/version.js +48 -0
  335. package/dist/utils/version.js.map +1 -1
  336. package/package.json +13 -1
  337. package/src/git/cache.ts +309 -29
  338. package/src/git/context.ts +11 -2
  339. package/src/git/models/branch.ts +1 -0
  340. package/src/git/models/commit.ts +2 -17
  341. package/src/git/models/graph.ts +2 -4
  342. package/src/git/models/reference.ts +33 -0
  343. package/src/git/models/remote.ts +1 -0
  344. package/src/git/models/repository.ts +44 -6
  345. package/src/git/models/search.ts +2 -2
  346. package/src/git/models/worktree.ts +10 -2
  347. package/src/git/parsers/diffParser.ts +9 -44
  348. package/src/git/providers/branches.ts +25 -4
  349. package/src/git/providers/commits.ts +7 -0
  350. package/src/git/providers/config.ts +4 -0
  351. package/src/git/providers/diff.ts +8 -2
  352. package/src/git/providers/operations.ts +54 -3
  353. package/src/git/providers/pausedOperations.ts +1 -0
  354. package/src/git/providers/refs.ts +32 -3
  355. package/src/git/providers/revision.ts +5 -1
  356. package/src/git/providers/staging.ts +14 -0
  357. package/src/git/providers/status.ts +6 -1
  358. package/src/git/remotes/azure-devops.ts +2 -0
  359. package/src/git/{exec.types.ts → run.types.ts} +22 -2
  360. package/src/git/service.ts +1 -0
  361. package/src/git/utils/autolink.utils.ts +1 -0
  362. package/src/git/utils/blame.utils.ts +2 -0
  363. package/src/git/utils/conflictResolution.utils.ts +31 -0
  364. package/src/git/utils/fileStatus.utils.ts +7 -1
  365. package/src/git/utils/search.utils.ts +2 -0
  366. package/src/git/utils/sorting.ts +38 -0
  367. package/src/git/utils/status.utils.ts +1 -0
  368. package/src/git/watching/classifyChange.ts +9 -0
  369. package/src/git/watching/gitIgnoreFilter.ts +2 -2
  370. package/src/git/watching/initWatcher.ts +2 -0
  371. package/src/git/watching/watchGroup.ts +3 -0
  372. package/src/git/watching/watchService.ts +38 -5
  373. package/src/git/watching/watchSession.ts +10 -0
  374. package/src/git-cli/cliGitProvider.ts +1 -1
  375. package/src/git-cli/exec/exec.types.ts +2 -2
  376. package/src/git-cli/exec/git.ts +76 -23
  377. package/src/git-cli/exec/gitQueue.ts +98 -25
  378. package/src/git-cli/parsers/blameParser.ts +1 -0
  379. package/src/git-cli/parsers/logParser.ts +6 -1
  380. package/src/git-cli/parsers/refParser.ts +33 -34
  381. package/src/git-cli/providers/blame.ts +2 -1
  382. package/src/git-cli/providers/branches.ts +341 -196
  383. package/src/git-cli/providers/commits.ts +145 -46
  384. package/src/git-cli/providers/config.ts +50 -16
  385. package/src/git-cli/providers/contributors.ts +7 -12
  386. package/src/git-cli/providers/diff.ts +107 -20
  387. package/src/git-cli/providers/graph.ts +58 -29
  388. package/src/git-cli/providers/operations.ts +253 -57
  389. package/src/git-cli/providers/patch.ts +16 -37
  390. package/src/git-cli/providers/pausedOperations.ts +14 -6
  391. package/src/git-cli/providers/refs.ts +274 -11
  392. package/src/git-cli/providers/remotes.ts +4 -4
  393. package/src/git-cli/providers/revision.ts +22 -15
  394. package/src/git-cli/providers/staging.ts +51 -8
  395. package/src/git-cli/providers/stash.ts +67 -134
  396. package/src/git-cli/providers/status.ts +21 -10
  397. package/src/git-cli/providers/tags.ts +18 -19
  398. package/src/git-cli/providers/worktrees.ts +3 -3
  399. package/src/ipc/discovery.ts +56 -0
  400. package/src/ipc/ipcServer.ts +171 -0
  401. package/src/plus/agents/providers/claudeCodeProvider.ts +1782 -0
  402. package/src/plus/agents/providers/claudeCodeTranscript.ts +214 -0
  403. package/src/plus/agents/sanitizePrompt.ts +109 -0
  404. package/src/plus/agents/stateMachine.ts +197 -0
  405. package/src/plus/agents/types.ts +277 -0
  406. package/src/plus/ai/constants.ts +8 -0
  407. package/src/plus/ai/errors.ts +40 -0
  408. package/src/plus/ai/models/model.ts +1 -0
  409. package/src/plus/ai/models/promptTemplates.ts +42 -0
  410. package/src/plus/ai/models/results.ts +30 -0
  411. package/src/plus/ai/prompts.ts +171 -1
  412. package/src/plus/ai/utils/ai.utils.ts +2 -0
  413. package/src/plus/ai/utils/results.utils.ts +101 -0
  414. package/src/plus/ai/utils/truncation.utils.ts +3 -1
  415. package/src/plus/git-github/api/github.ts +199 -53
  416. package/src/plus/git-github/api/types.ts +15 -0
  417. package/src/plus/git-github/models.ts +1 -1
  418. package/src/plus/git-github/providers/github/blame.ts +2 -0
  419. package/src/plus/git-github/providers/github/branches.ts +75 -33
  420. package/src/plus/git-github/providers/github/commits.ts +231 -160
  421. package/src/plus/git-github/providers/github/diff.ts +35 -21
  422. package/src/plus/git-github/providers/github/refs.ts +57 -22
  423. package/src/plus/git-github/providers/github/revision.ts +1 -2
  424. package/src/plus/git-github/providers/github/status.ts +6 -1
  425. package/src/plus/git-github/providers/github/tags.ts +61 -62
  426. package/src/plus/git-github/providers/githubGitProvider.ts +1 -0
  427. package/src/utils/color.ts +2 -0
  428. package/src/utils/decorators/log.ts +1 -1
  429. package/src/utils/decorators/sequentialize.ts +8 -6
  430. package/src/utils/decorators/serializable.ts +1 -0
  431. package/src/utils/dedupedAsyncCache.ts +74 -0
  432. package/src/utils/diff.ts +61 -0
  433. package/src/utils/disposable.ts +2 -0
  434. package/src/utils/env/node/base64.ts +1 -1
  435. package/src/utils/env/node/exec.ts +4 -2
  436. package/src/utils/error.ts +1 -0
  437. package/src/utils/formatter.ts +4 -11
  438. package/src/utils/iterable.ts +4 -1
  439. package/src/utils/logger.ts +1 -0
  440. package/src/utils/lruMap.ts +112 -0
  441. package/src/utils/markdown.ts +1 -0
  442. package/src/utils/mru.ts +2 -0
  443. package/src/utils/path.ts +1 -0
  444. package/src/utils/promise.ts +22 -0
  445. package/src/utils/promiseCache.ts +24 -0
  446. package/src/utils/string.ts +2 -0
  447. package/src/utils/uri.ts +1 -0
  448. package/src/utils/version.ts +49 -0
  449. package/dist/git/exec.types.d.ts.map +0 -1
  450. package/dist/git/exec.types.js +0 -2
  451. package/dist/git/exec.types.js.map +0 -1
@@ -27,6 +27,7 @@ import type { DiffRange } from '../../git/providers/types.js';
27
27
  import { createUncommittedChangesCommit } from '../../git/utils/commit.utils.js';
28
28
  import { isRevisionRange, isSha, isUncommitted, isUncommittedStaged } from '../../git/utils/revision.utils.js';
29
29
  import { parseSearchQueryGitCommand } from '../../git/utils/search.utils.js';
30
+ import { compareReachableRefs } from '../../git/utils/sorting.js';
30
31
  import { isUserMatch } from '../../git/utils/user.utils.js';
31
32
  import { CancellationError, isCancellationError } from '../../utils/cancellation.js';
32
33
  import { debug, trace } from '../../utils/decorators/log.js';
@@ -40,7 +41,7 @@ import { escapeRegex } from '../../utils/string.js';
40
41
  import type { Uri } from '../../utils/uri.js';
41
42
  import { fileUri, joinUriPath, toFsPath } from '../../utils/uri.js';
42
43
  import type { CliGitProviderInternal } from '../cliGitProvider.js';
43
- import type { GitExecOptions, GitResult } from '../exec/exec.types.js';
44
+ import type { GitResult, GitRunOptions } from '../exec/exec.types.js';
44
45
  import type { Git } from '../exec/git.js';
45
46
  import { gitConfigsLog, gitConfigsLogWithFiles, GitErrors } from '../exec/git.js';
46
47
  import type {
@@ -49,7 +50,12 @@ import type {
49
50
  CommitsWithFilesLogParser,
50
51
  ParsedCommit,
51
52
  } from '../parsers/logParser.js';
52
- import { getCommitsLogParser, getShaAndFilesAndStatsLogParser, getShaLogParser } from '../parsers/logParser.js';
53
+ import {
54
+ getCommitsLogParser,
55
+ getShaAndDatesLogParser,
56
+ getShaAndFilesAndStatsLogParser,
57
+ getShaLogParser,
58
+ } from '../parsers/logParser.js';
53
59
  import { getReflogParser, parseGitRefLog } from '../parsers/reflogParser.js';
54
60
  import { parseSignatureOutput, signatureFormat } from '../parsers/signatureParser.js';
55
61
  import { createCommitFileset } from './commitFilesetUtils.js';
@@ -74,7 +80,7 @@ export class CommitsGitSubProvider implements GitCommitsSubProvider {
74
80
  message: string,
75
81
  cancellation?: AbortSignal,
76
82
  ): Promise<string> {
77
- const result = await this.git.exec(
83
+ const result = await this.git.run(
78
84
  { cwd: repoPath, cancellation: cancellation, errors: 'throw' },
79
85
  'commit-tree',
80
86
  tree,
@@ -105,7 +111,7 @@ export class CommitsGitSubProvider implements GitCommitsSubProvider {
105
111
 
106
112
  @debug({ exit: true })
107
113
  async getCommitCount(repoPath: string, rev: string, cancellation?: AbortSignal): Promise<number | undefined> {
108
- const result = await this.git.exec(
114
+ const result = await this.git.run(
109
115
  { cwd: repoPath, cancellation: cancellation, errors: 'ignore' },
110
116
  'rev-list',
111
117
  '--count',
@@ -123,10 +129,49 @@ export class CommitsGitSubProvider implements GitCommitsSubProvider {
123
129
  return isNaN(count) ? undefined : count;
124
130
  }
125
131
 
132
+ @debug({ exit: true })
133
+ async getCommitDates(
134
+ repoPath: string,
135
+ rev: string,
136
+ cancellation?: AbortSignal,
137
+ ): Promise<{ authorDate: Date; committerDate: Date } | undefined> {
138
+ const parser = getShaAndDatesLogParser();
139
+ const result = await this.git.run(
140
+ {
141
+ cwd: repoPath,
142
+ cancellation: cancellation,
143
+ errors: 'ignore',
144
+ // Why: full SHAs identify immutable commits (5-min TTL is safe). Non-SHA refs rely on
145
+ // gitResults being cleared on 'head'/'heads'/'remotes' events; the 60s TTL is the
146
+ // failsafe for watcher latency / web (no fs watcher).
147
+ caching: {
148
+ cache: this.cache.gitResults,
149
+ options: { accessTTL: isSha(rev) ? 5 * 60 * 1000 : 60 * 1000 },
150
+ },
151
+ },
152
+ 'log',
153
+ '-1',
154
+ ...parser.arguments,
155
+ rev,
156
+ );
157
+ if (result.cancelled || cancellation?.aborted) return undefined;
158
+
159
+ for (const entry of parser.parse(result.stdout)) {
160
+ const authorSeconds = Number(entry.authorDate);
161
+ const committerSeconds = Number(entry.committerDate);
162
+ if (!Number.isFinite(authorSeconds) || !Number.isFinite(committerSeconds)) return undefined;
163
+ return {
164
+ authorDate: new Date(authorSeconds * 1000),
165
+ committerDate: new Date(committerSeconds * 1000),
166
+ };
167
+ }
168
+ return undefined;
169
+ }
170
+
126
171
  @debug()
127
172
  async getCommitFiles(repoPath: string, rev: string, cancellation?: AbortSignal): Promise<GitFileChange[]> {
128
173
  const parser = getShaAndFilesAndStatsLogParser();
129
- const result = await this.git.exec(
174
+ const result = await this.git.run(
130
175
  { cwd: repoPath, cancellation: cancellation, configs: gitConfigsLog },
131
176
  'log',
132
177
  ...parser.arguments,
@@ -204,7 +249,7 @@ export class CommitsGitSubProvider implements GitCommitsSubProvider {
204
249
  const getCore = async (cacheable?: CacheController) => {
205
250
  try {
206
251
  // Use for-each-ref with %(HEAD) to mark current branch with *
207
- const result = await this.git.exec(
252
+ const result = await this.git.run(
208
253
  { cwd: repoPath, cancellation: cancellation, errors: 'ignore' },
209
254
  'for-each-ref',
210
255
  '--contains',
@@ -255,8 +300,9 @@ export class CommitsGitSubProvider implements GitCommitsSubProvider {
255
300
  }
256
301
  }
257
302
 
258
- // Sort to move tags to the end, preserving order within each type
259
- refs.sort((a, b) => (a.refType !== b.refType ? (a.refType === 'tag' ? 1 : -1) : 0));
303
+ // git already returns sorted by -HEAD, -committerdate, -version:refname;
304
+ // compareReachableRefs refines by current-first / local-before-remote / tag-version.
305
+ refs.sort(compareReachableRefs);
260
306
 
261
307
  return { refs: refs };
262
308
  } catch (ex) {
@@ -305,8 +351,8 @@ export class CommitsGitSubProvider implements GitCommitsSubProvider {
305
351
  }
306
352
 
307
353
  try {
308
- const result = await this.git.exec(
309
- { cwd: repoPath, cancellation: cancellation, configs: gitConfigsLog },
354
+ const result = await this.git.run(
355
+ { cwd: repoPath, cancellation: cancellation, configs: gitConfigsLog, priority: 'background' },
310
356
  'log',
311
357
  ...args,
312
358
  );
@@ -363,7 +409,7 @@ export class CommitsGitSubProvider implements GitCommitsSubProvider {
363
409
  // Initial commit SHA is shared across all worktrees
364
410
  return this.cache.getInitialCommitSha(repoPath, async commonPath => {
365
411
  try {
366
- const result = await this.git.exec(
412
+ const result = await this.git.run(
367
413
  { cwd: commonPath, cancellation: cancellation, errors: 'ignore' },
368
414
  'rev-list',
369
415
  `--max-parents=0`,
@@ -395,7 +441,7 @@ export class CommitsGitSubProvider implements GitCommitsSubProvider {
395
441
  : [];
396
442
 
397
443
  const run = async (): Promise<LeftRightCommitCountResult | undefined> => {
398
- const result = await this.git.exec(
444
+ const result = await this.git.run(
399
445
  { cwd: repoPath, cancellation: cancellation, errors: 'ignore' },
400
446
  'rev-list',
401
447
  '--left-right',
@@ -431,7 +477,7 @@ export class CommitsGitSubProvider implements GitCommitsSubProvider {
431
477
  // Cache key: range + excludeMerges. RepositoryChange events affecting branches/remotes
432
478
  // invalidate this cache via clearCaches('branches').
433
479
  const cacheKey = `${range}\x1f${options?.excludeMerges ? 'no-merges' : ''}`;
434
- return this.cache.getLeftRightCommitCount(repoPath, cacheKey, run);
480
+ return this.cache.leftRightCommitCount.getOrCreate(repoPath, cacheKey, run);
435
481
  }
436
482
 
437
483
  @debug()
@@ -525,24 +571,15 @@ export class CommitsGitSubProvider implements GitCommitsSubProvider {
525
571
  args.push(`-n${isSingleCommit ? 1 : limit + 1}`);
526
572
  }
527
573
 
528
- let stashes: Map<string, GitStashCommit> | undefined;
529
- let stdin: string | undefined;
574
+ let stashes: ReadonlyMap<string, GitStashCommit> | undefined;
530
575
 
531
- if (!isSingleCommit) {
532
- if (options?.stashes) {
533
- // There *HAS* to be a better way to get git log to return stashes, but this is the best we've found
534
- ({ stdin, stashes } = convertStashesToStdin(
535
- typeof options.stashes === 'boolean'
536
- ? await this.provider.stash?.getStash(repoPath, { reachableFrom: rev }, cancellation)
537
- : options.stashes,
538
- ));
539
- if (stashes.size) {
540
- rev ??= 'HEAD';
541
- }
542
- }
543
-
544
- if (stdin) {
545
- args.push('--stdin');
576
+ if (!isSingleCommit && options?.stashes) {
577
+ stashes =
578
+ typeof options.stashes === 'boolean'
579
+ ? (await this.provider.stash?.getStash(repoPath, { reachableFrom: rev }, cancellation))?.stashes
580
+ : options.stashes;
581
+ if (stashes?.size) {
582
+ rev ??= 'HEAD';
546
583
  }
547
584
  }
548
585
 
@@ -580,19 +617,30 @@ export class CommitsGitSubProvider implements GitCommitsSubProvider {
580
617
  const currentUser = await currentUserPromise.catch(() => undefined);
581
618
  if (cancellation?.aborted) throw new CancellationError();
582
619
 
583
- const cmdOpts: GitExecOptions = {
620
+ const cmdOpts: GitRunOptions = {
584
621
  cwd: repoPath,
585
622
  cancellation: cancellation,
586
623
  configs: gitConfigsLogWithFiles,
587
- stdin: stdin,
624
+ // Single-commit log serves user-initiated reads (commit details, hover, etc.).
625
+ // Mirrors getCommitDates: full SHAs are immutable so a 5-min TTL is safe; non-SHA refs
626
+ // rely on gitResults being cleared on head/heads/remotes events (60s is the failsafe
627
+ // for watcher latency / web with no fs watcher).
628
+ ...(isSingleCommit && rev
629
+ ? {
630
+ caching: {
631
+ cache: this.cache.gitResults,
632
+ options: { accessTTL: isSha(rev) ? 5 * 60 * 1000 : 60 * 1000 },
633
+ },
634
+ }
635
+ : undefined),
588
636
  };
589
- let { commits, count, countStashChildCommits } = await parseCommits(
637
+ let { commits, count } = await parseCommits(
590
638
  parser,
591
- isSingleCommit ? this.git.exec(cmdOpts, ...args) : this.git.stream(cmdOpts, ...args),
639
+ isSingleCommit ? this.git.run(cmdOpts, ...args) : this.git.stream(cmdOpts, ...args),
592
640
  repoPath,
593
641
  pathspec,
594
642
  limit,
595
- stashes,
643
+ undefined,
596
644
  currentUser,
597
645
  );
598
646
 
@@ -614,25 +662,67 @@ export class CommitsGitSubProvider implements GitCommitsSubProvider {
614
662
  args.splice(args.length - 1, 1, pathspec);
615
663
  }
616
664
 
617
- ({ commits, count, countStashChildCommits } = await parseCommits(
665
+ ({ commits, count } = await parseCommits(
618
666
  parser,
619
- isSingleCommit ? this.git.exec(cmdOpts, ...args) : this.git.stream(cmdOpts, ...args),
667
+ isSingleCommit ? this.git.run(cmdOpts, ...args) : this.git.stream(cmdOpts, ...args),
620
668
  repoPath,
621
669
  pathspec,
622
670
  limit,
623
- stashes,
671
+ undefined,
624
672
  currentUser,
625
673
  ));
626
674
  }
627
675
  }
628
676
 
677
+ // Merge stashes in-memory rather than via `git log --stdin <stash>` — git would
678
+ // walk each stash's parent chains and pull in pre-rebase ancestors that aren't
679
+ // reachable from <rev>. Slot each stash directly above its first parent; stashes
680
+ // whose parent isn't in the result (rebased away, or below the current limit) are
681
+ // dropped — the Stashes view still surfaces them, and they reappear here if the
682
+ // parent loads via "Load more".
683
+ if (stashes?.size) {
684
+ // `stashes` arrives in `git stash list` order (newest @{0} first), so per-parent
685
+ // groups naturally land newest-first without an explicit sort
686
+ const stashesByParent = new Map<string, GitStashCommit[]>();
687
+ for (const stash of stashes.values()) {
688
+ const parentSha = stash.parents[0];
689
+ if (parentSha == null || !commits.has(parentSha)) continue;
690
+
691
+ const group = stashesByParent.get(parentSha);
692
+ if (group != null) {
693
+ group.push(stash);
694
+ } else {
695
+ stashesByParent.set(parentSha, [stash]);
696
+ }
697
+ }
698
+
699
+ if (stashesByParent.size) {
700
+ const cap = limit > 0 ? limit + 1 : Number.POSITIVE_INFINITY;
701
+ const merged = new Map<string, GitCommit>();
702
+ outer: for (const c of commits.values()) {
703
+ const group = stashesByParent.get(c.sha);
704
+ if (group != null) {
705
+ for (const s of group) {
706
+ if (merged.size >= cap) break outer;
707
+
708
+ merged.set(s.sha, s);
709
+ }
710
+ }
711
+ if (merged.size >= cap) break;
712
+
713
+ merged.set(c.sha, c);
714
+ }
715
+ commits = merged;
716
+ }
717
+ }
718
+
629
719
  const log: GitLog = {
630
720
  repoPath: repoPath,
631
721
  commits: commits,
632
722
  sha: isRevisionRange(rev) ? undefined : rev,
633
723
  count: commits.size,
634
724
  limit: limit,
635
- hasMore: overrideHasMore ?? count - countStashChildCommits > commits.size,
725
+ hasMore: overrideHasMore ?? (limit > 0 && count > limit),
636
726
  };
637
727
 
638
728
  if (!isSingleCommit) {
@@ -897,6 +987,7 @@ export class CommitsGitSubProvider implements GitCommitsSubProvider {
897
987
  filterMap<[string, GitCommit], [string, GitCommit]>(log.commits.entries(), ([sha, c]) => {
898
988
  if (skip) {
899
989
  if (sha !== rev) return undefined;
990
+
900
991
  skip = false;
901
992
  }
902
993
 
@@ -1058,7 +1149,7 @@ export class CommitsGitSubProvider implements GitCommitsSubProvider {
1058
1149
  args.push(pathspec);
1059
1150
  }
1060
1151
 
1061
- const result = await this.git.exec(
1152
+ const result = await this.git.run(
1062
1153
  { cwd: repoPath, cancellation: cancellation, configs: gitConfigsLog },
1063
1154
  'log',
1064
1155
  ...args,
@@ -1067,6 +1158,7 @@ export class CommitsGitSubProvider implements GitCommitsSubProvider {
1067
1158
  } catch (ex) {
1068
1159
  scope?.error(ex);
1069
1160
  if (isCancellationError(ex)) throw ex;
1161
+
1070
1162
  debugger;
1071
1163
 
1072
1164
  return [];
@@ -1100,7 +1192,7 @@ export class CommitsGitSubProvider implements GitCommitsSubProvider {
1100
1192
 
1101
1193
  const parser = getShaLogParser();
1102
1194
 
1103
- const result = await this.git.exec(
1195
+ const result = await this.git.run(
1104
1196
  { cwd: repoPath, cancellation: cancellation, configs: gitConfigsLog },
1105
1197
  'log',
1106
1198
  ...parser.arguments,
@@ -1114,6 +1206,7 @@ export class CommitsGitSubProvider implements GitCommitsSubProvider {
1114
1206
  } catch (ex) {
1115
1207
  scope?.error(ex);
1116
1208
  if (isCancellationError(ex)) throw ex;
1209
+
1117
1210
  debugger;
1118
1211
 
1119
1212
  return undefined;
@@ -1131,7 +1224,7 @@ export class CommitsGitSubProvider implements GitCommitsSubProvider {
1131
1224
  async isAncestorOf(repoPath: string, rev1: string, rev2: string, cancellation?: AbortSignal): Promise<boolean> {
1132
1225
  if (repoPath == null) return false;
1133
1226
 
1134
- const result = await this.git.exec(
1227
+ const result = await this.git.run(
1135
1228
  { cwd: repoPath, cancellation: cancellation, errors: 'ignore' },
1136
1229
  'merge-base',
1137
1230
  '--is-ancestor',
@@ -1310,11 +1403,14 @@ export class CommitsGitSubProvider implements GitCommitsSubProvider {
1310
1403
  const scope = getScopedLogger();
1311
1404
 
1312
1405
  try {
1313
- const result = await this.git.exec(
1406
+ const result = await this.git.run(
1314
1407
  {
1315
1408
  cwd: repoPath,
1316
1409
  errors: 'ignore',
1317
- caching: { cache: this.cache.gitResults, options: { accessTTL: 60 * 1000 } },
1410
+ caching: {
1411
+ cache: this.cache.gitResults,
1412
+ options: { accessTTL: isSha(sha) ? 5 * 60 * 1000 : 60 * 1000 },
1413
+ },
1318
1414
  configs: gitConfigsLog,
1319
1415
  },
1320
1416
  'log',
@@ -1337,11 +1433,14 @@ export class CommitsGitSubProvider implements GitCommitsSubProvider {
1337
1433
  const scope = getScopedLogger();
1338
1434
 
1339
1435
  try {
1340
- const result = await this.git.exec(
1436
+ const result = await this.git.run(
1341
1437
  {
1342
1438
  cwd: repoPath,
1343
1439
  errors: 'ignore',
1344
- caching: { cache: this.cache.gitResults, options: { accessTTL: 60 * 1000 } },
1440
+ caching: {
1441
+ cache: this.cache.gitResults,
1442
+ options: { accessTTL: isSha(sha) ? 5 * 60 * 1000 : 60 * 1000 },
1443
+ },
1345
1444
  },
1346
1445
  'cat-file',
1347
1446
  'commit',
@@ -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 } from '../../git/cache.js';
4
+ import type { Cache, GkConfigInvalidationTarget } 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';
@@ -34,6 +34,8 @@ const emptyArray: readonly never[] = Object.freeze([]);
34
34
  */
35
35
  const gkConfigCacheableSets: readonly { match: RegExp; pattern: string }[] = [
36
36
  { match: /^branch\..+\.gk-associated-issues$/, pattern: '^branch\\..+\\.gk-associated-issues$' },
37
+ { match: /^branch\..+\.gk-merge-base$/, pattern: '^branch\\..+\\.gk-merge-base$' },
38
+ { match: /^branch\..+\.gk-merge-target$/, pattern: '^branch\\..+\\.gk-merge-target$' },
37
39
  { match: /^branch\..+\.gk-merge-target-user$/, pattern: '^branch\\..+\\.gk-merge-target-user$' },
38
40
  ];
39
41
 
@@ -59,6 +61,7 @@ function parseConfigRegexOutput(data: string | undefined): Map<string, string> {
59
61
 
60
62
  function parseGitBoolean(value: string | undefined): boolean {
61
63
  if (value == null) return false;
64
+
62
65
  const normalized = value.toLowerCase().trim();
63
66
  return normalized === 'true' || normalized === 'yes' || normalized === 'on' || normalized === '1';
64
67
  }
@@ -71,6 +74,13 @@ export class ConfigGitSubProvider implements GitConfigSubProvider {
71
74
  private readonly provider: CliGitProviderInternal,
72
75
  ) {}
73
76
 
77
+ /**
78
+ * Pending `setGkConfig` writes keyed by `(commonPath, key, value, skip-option)`. Concurrent
79
+ * callers writing the same `(key, value)` pair share the in-flight Promise so only one
80
+ * `git config --set` subprocess actually runs. Cleared on settle.
81
+ */
82
+ private readonly _pendingGkConfigWrites = new Map<string, Promise<void>>();
83
+
74
84
  @trace()
75
85
  getConfig(
76
86
  repoPath: string | undefined,
@@ -99,7 +109,7 @@ export class ConfigGitSubProvider implements GitConfigSubProvider {
99
109
  }
100
110
  args.push(key);
101
111
 
102
- const result = await this.git.exec(
112
+ const result = await this.git.run(
103
113
  { cwd: repoPath ?? '', errors: 'ignore', runLocally: options?.runGitLocally },
104
114
  ...args,
105
115
  );
@@ -131,7 +141,7 @@ export class ConfigGitSubProvider implements GitConfigSubProvider {
131
141
  }
132
142
  args.push(pattern);
133
143
 
134
- const result = await this.git.exec(
144
+ const result = await this.git.run(
135
145
  { cwd: repoPath ?? '', errors: 'ignore', runLocally: options?.runGitLocally },
136
146
  ...args,
137
147
  );
@@ -169,7 +179,7 @@ export class ConfigGitSubProvider implements GitConfigSubProvider {
169
179
  args.push(key, value);
170
180
  }
171
181
 
172
- await this.git.exec({ cwd: repoPath ?? '', runLocally: true }, ...args);
182
+ await this.git.run({ cwd: repoPath ?? '', runLocally: true }, ...args);
173
183
 
174
184
  // Only invalidate cache when not using a custom file (custom files aren't cached)
175
185
  if (!options?.file) {
@@ -218,7 +228,7 @@ export class ConfigGitSubProvider implements GitConfigSubProvider {
218
228
 
219
229
  const author = `${user.name} <${user.email}>`;
220
230
  // Check if there is a mailmap for the current user
221
- const result = await this.git.exec({ cwd: repoPath, errors: 'ignore' }, 'check-mailmap', author);
231
+ const result = await this.git.run({ cwd: repoPath, errors: 'ignore' }, 'check-mailmap', author);
222
232
 
223
233
  if (result.stdout && result.stdout !== author) {
224
234
  const match = mappedAuthorRegex.exec(result.stdout);
@@ -243,6 +253,7 @@ export class ConfigGitSubProvider implements GitConfigSubProvider {
243
253
  async getDefaultWorktreePath(repoPath: string): Promise<string | undefined> {
244
254
  const gitDir = await this.getGitDir(repoPath);
245
255
  if (gitDir == null) return undefined;
256
+
246
257
  const basePath = (gitDir.commonUri ?? gitDir.uri).fsPath;
247
258
  return getBestPath(normalizePath(joinPaths(basePath, '..')));
248
259
  }
@@ -291,6 +302,7 @@ export class ConfigGitSubProvider implements GitConfigSubProvider {
291
302
  private async getGkConfigPath(repoPath: string): Promise<string | undefined> {
292
303
  const gitDir = await this.getGitDir(repoPath);
293
304
  if (gitDir == null) return undefined;
305
+
294
306
  // Use commonUri (main .git dir) for worktrees, otherwise use uri
295
307
  const basePath = (gitDir.commonUri ?? gitDir.uri).fsPath;
296
308
  return joinPaths(basePath, 'gk', 'config');
@@ -349,6 +361,31 @@ export class ConfigGitSubProvider implements GitConfigSubProvider {
349
361
  repoPath: string,
350
362
  key: GkConfigKeys | DeprecatedGkConfigKeys,
351
363
  value: string | undefined,
364
+ options?: { skipInvalidation?: readonly GkConfigInvalidationTarget[] },
365
+ ): Promise<void> {
366
+ // Encode the skip-invalidation option in the dedup key: a self-write (with skip targets)
367
+ // and a user-driven write of the same `(key, value)` (no skip targets) have different
368
+ // cache-invalidation semantics, so sharing their Promise would silently let the user-driven
369
+ // write skip invalidation. Sort the array so callers passing the same targets in different
370
+ // orders share a single in-flight write.
371
+ const commonPath = this.cache.getCommonPath(repoPath);
372
+ const skipKey = options?.skipInvalidation?.length ? options.skipInvalidation.toSorted().join(',') : '';
373
+ const dedupKey = `${commonPath}\0${key}\0${value ?? ''}\0${skipKey}`;
374
+
375
+ const pending = this._pendingGkConfigWrites.get(dedupKey);
376
+ if (pending != null) return pending;
377
+
378
+ const promise = this.setGkConfigCore(repoPath, key, value, options);
379
+ this._pendingGkConfigWrites.set(dedupKey, promise);
380
+ void promise.finally(() => this._pendingGkConfigWrites.delete(dedupKey));
381
+ return promise;
382
+ }
383
+
384
+ private async setGkConfigCore(
385
+ repoPath: string,
386
+ key: GkConfigKeys | DeprecatedGkConfigKeys,
387
+ value: string | undefined,
388
+ options?: { skipInvalidation?: readonly GkConfigInvalidationTarget[] },
352
389
  ): Promise<void> {
353
390
  const scope = getScopedLogger();
354
391
 
@@ -362,7 +399,7 @@ export class ConfigGitSubProvider implements GitConfigSubProvider {
362
399
  await this.setConfigCore(repoPath, key, value, { file: gkConfigPath });
363
400
 
364
401
  // Invalidate the cached value for this key and clear all regex patterns for this scope
365
- this.cache.deleteGkConfig(repoPath, key);
402
+ this.cache.deleteGkConfig(repoPath, key, options);
366
403
  }
367
404
 
368
405
  @debug()
@@ -475,7 +512,7 @@ export class ConfigGitSubProvider implements GitConfigSubProvider {
475
512
  if (this.context.workspace?.isTrusted === false) {
476
513
  try {
477
514
  await fs.stat(joinPaths(cwd, 'HEAD'));
478
- result = await this.git.exec(
515
+ result = await this.git.run(
479
516
  { cwd: cwd, errors: 'throw', configs: ['-C', cwd] },
480
517
  'rev-parse',
481
518
  '--show-cdup',
@@ -490,7 +527,7 @@ export class ConfigGitSubProvider implements GitConfigSubProvider {
490
527
  }
491
528
 
492
529
  try {
493
- result = await this.git.exec(
530
+ result = await this.git.run(
494
531
  { cwd: cwd, errors: 'throw' },
495
532
  'rev-parse',
496
533
  '--show-toplevel',
@@ -551,7 +588,7 @@ export class ConfigGitSubProvider implements GitConfigSubProvider {
551
588
 
552
589
  const inDotGit = /this operation must be run in a work tree/.test(ex.stderr);
553
590
  if (inDotGit && this.context.workspace?.isTrusted !== false) {
554
- result = await this.git.exec({ cwd: cwd, errors: 'ignore' }, 'rev-parse', '--is-bare-repository');
591
+ result = await this.git.run({ cwd: cwd, errors: 'ignore' }, 'rev-parse', '--is-bare-repository');
555
592
  if (result.stdout.trim() === 'true') {
556
593
  const result = await this.revParseGitDir(cwd);
557
594
  const repoPath = result?.commonPath ?? result?.path;
@@ -578,12 +615,7 @@ export class ConfigGitSubProvider implements GitConfigSubProvider {
578
615
  }
579
616
 
580
617
  private async revParseGitDir(cwd: string): Promise<{ path: string; commonPath?: string } | undefined> {
581
- const result = await this.git.exec(
582
- { cwd: cwd, errors: 'ignore' },
583
- 'rev-parse',
584
- '--git-dir',
585
- '--git-common-dir',
586
- );
618
+ const result = await this.git.run({ cwd: cwd, errors: 'ignore' }, 'rev-parse', '--git-dir', '--git-common-dir');
587
619
  if (!result.stdout) return undefined;
588
620
 
589
621
  let [dotGitPath, commonDotGitPath] = result.stdout.split('\n').map(r => r.trimStart());
@@ -653,6 +685,7 @@ export class ConfigGitSubProvider implements GitConfigSubProvider {
653
685
  this._migratedRepos.add(repoPath);
654
686
  return;
655
687
  }
688
+
656
689
  const gkConfigFolder = joinPaths(gkConfigPath, '..');
657
690
 
658
691
  // If .git/gk/config already exists, consider migration done — it was either
@@ -667,6 +700,7 @@ export class ConfigGitSubProvider implements GitConfigSubProvider {
667
700
 
668
701
  // If .git/gk/config doesn't exist, create an empty file to prevent multiple migration attempts in future sessions
669
702
  if (!(await this.ensureGkConfigFolder(gkConfigFolder, scope))) return;
703
+
670
704
  try {
671
705
  await fs.writeFile(gkConfigPath, new Uint8Array());
672
706
  } catch (ex) {
@@ -704,7 +738,7 @@ export class ConfigGitSubProvider implements GitConfigSubProvider {
704
738
  // Remove legacy keys from regular git config (use --unset-all defensively)
705
739
  for (const [key] of migrateConfig) {
706
740
  try {
707
- await this.git.exec(
741
+ await this.git.run(
708
742
  { cwd: repoPath, errors: 'ignore', runLocally: true },
709
743
  'config',
710
744
  '--local',
@@ -260,7 +260,7 @@ export class ContributorsGitSubProvider implements GitContributorsSubProvider {
260
260
  }
261
261
 
262
262
  const currentUserPromise = this.provider.config.getCurrentUser(repoPath).catch(() => undefined);
263
- const result = await this.git.exec({ cwd: repoPath, cancellation: signal }, ...args);
263
+ const result = await this.git.run({ cwd: repoPath, cancellation: signal }, ...args);
264
264
  if (!result.stdout) return [];
265
265
 
266
266
  const shortlog = parseShortlog(result.stdout, commonPath, await currentUserPromise);
@@ -301,7 +301,7 @@ export class ContributorsGitSubProvider implements GitContributorsSubProvider {
301
301
 
302
302
  const scope = getScopedLogger();
303
303
 
304
- const getCore = async (commonPath: string): Promise<GitContributorsStats | undefined> => {
304
+ const getCore = async (commonPath: string, signal?: AbortSignal): Promise<GitContributorsStats | undefined> => {
305
305
  try {
306
306
  const args = ['shortlog', '-s', '--all'];
307
307
 
@@ -316,10 +316,7 @@ export class ContributorsGitSubProvider implements GitContributorsSubProvider {
316
316
  args.push(`--since=${options.since}`);
317
317
  }
318
318
 
319
- const result = await this.git.exec(
320
- { cwd: commonPath, cancellation: cancellation, timeout: timeout },
321
- ...args,
322
- );
319
+ const result = await this.git.run({ cwd: commonPath, cancellation: signal, timeout: timeout }, ...args);
323
320
  if (!result.stdout) return undefined;
324
321
 
325
322
  const contributions = result.stdout
@@ -351,11 +348,9 @@ export class ContributorsGitSubProvider implements GitContributorsSubProvider {
351
348
  customCacheTTL = timeout * 2;
352
349
  }
353
350
 
354
- return this.cache.getContributorsStats(
355
- repoPath,
356
- cacheKey,
357
- getCore,
358
- customCacheTTL ? { accessTTL: customCacheTTL } : undefined,
359
- );
351
+ return this.cache.getContributorsStats(repoPath, cacheKey, getCore, {
352
+ accessTTL: customCacheTTL,
353
+ cancellation: cancellation,
354
+ });
360
355
  }
361
356
  }