@gitkraken/core-gitlens 0.2.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 (361) hide show
  1. package/CHANGELOG.md +4 -1
  2. package/dist/git/cache.d.ts +28 -4
  3. package/dist/git/cache.d.ts.map +1 -1
  4. package/dist/git/cache.js +82 -18
  5. package/dist/git/cache.js.map +1 -1
  6. package/dist/git/models/commit.d.ts.map +1 -1
  7. package/dist/git/models/commit.js.map +1 -1
  8. package/dist/git/models/reference.d.ts +31 -0
  9. package/dist/git/models/reference.d.ts.map +1 -1
  10. package/dist/git/models/remote.d.ts.map +1 -1
  11. package/dist/git/models/remote.js.map +1 -1
  12. package/dist/git/models/repository.d.ts +9 -1
  13. package/dist/git/models/repository.d.ts.map +1 -1
  14. package/dist/git/models/repository.js +19 -3
  15. package/dist/git/models/repository.js.map +1 -1
  16. package/dist/git/models/search.d.ts +2 -2
  17. package/dist/git/models/search.d.ts.map +1 -1
  18. package/dist/git/models/worktree.d.ts +4 -1
  19. package/dist/git/models/worktree.d.ts.map +1 -1
  20. package/dist/git/models/worktree.js +2 -2
  21. package/dist/git/models/worktree.js.map +1 -1
  22. package/dist/git/parsers/diffParser.d.ts.map +1 -1
  23. package/dist/git/parsers/diffParser.js.map +1 -1
  24. package/dist/git/providers/branches.d.ts +15 -4
  25. package/dist/git/providers/branches.d.ts.map +1 -1
  26. package/dist/git/providers/config.d.ts +4 -1
  27. package/dist/git/providers/config.d.ts.map +1 -1
  28. package/dist/git/providers/operations.d.ts +33 -10
  29. package/dist/git/providers/operations.d.ts.map +1 -1
  30. package/dist/git/providers/pausedOperations.d.ts +3 -1
  31. package/dist/git/providers/pausedOperations.d.ts.map +1 -1
  32. package/dist/git/providers/refs.d.ts +23 -2
  33. package/dist/git/providers/refs.d.ts.map +1 -1
  34. package/dist/git/providers/staging.d.ts +16 -0
  35. package/dist/git/providers/staging.d.ts.map +1 -1
  36. package/dist/git/providers/status.d.ts +4 -1
  37. package/dist/git/providers/status.d.ts.map +1 -1
  38. package/dist/git/remotes/azure-devops.d.ts.map +1 -1
  39. package/dist/git/remotes/azure-devops.js.map +1 -1
  40. package/dist/git/{exec.types.d.ts → run.types.d.ts} +22 -3
  41. package/dist/git/run.types.d.ts.map +1 -0
  42. package/dist/git/run.types.js +2 -0
  43. package/dist/git/run.types.js.map +1 -0
  44. package/dist/git/service.d.ts.map +1 -1
  45. package/dist/git/service.js.map +1 -1
  46. package/dist/git/utils/autolink.utils.d.ts.map +1 -1
  47. package/dist/git/utils/autolink.utils.js.map +1 -1
  48. package/dist/git/utils/blame.utils.d.ts.map +1 -1
  49. package/dist/git/utils/blame.utils.js.map +1 -1
  50. package/dist/git/utils/conflictResolution.utils.d.ts +6 -0
  51. package/dist/git/utils/conflictResolution.utils.d.ts.map +1 -0
  52. package/dist/git/utils/conflictResolution.utils.js +25 -0
  53. package/dist/git/utils/conflictResolution.utils.js.map +1 -0
  54. package/dist/git/utils/fileStatus.utils.d.ts +2 -1
  55. package/dist/git/utils/fileStatus.utils.d.ts.map +1 -1
  56. package/dist/git/utils/fileStatus.utils.js +4 -0
  57. package/dist/git/utils/fileStatus.utils.js.map +1 -1
  58. package/dist/git/utils/search.utils.d.ts.map +1 -1
  59. package/dist/git/utils/search.utils.js +3 -0
  60. package/dist/git/utils/search.utils.js.map +1 -1
  61. package/dist/git/utils/sorting.d.ts +8 -0
  62. package/dist/git/utils/sorting.d.ts.map +1 -1
  63. package/dist/git/utils/sorting.js +15 -0
  64. package/dist/git/utils/sorting.js.map +1 -1
  65. package/dist/git/utils/status.utils.d.ts.map +1 -1
  66. package/dist/git/utils/status.utils.js.map +1 -1
  67. package/dist/git/watching/classifyChange.d.ts.map +1 -1
  68. package/dist/git/watching/classifyChange.js +8 -0
  69. package/dist/git/watching/classifyChange.js.map +1 -1
  70. package/dist/git/watching/gitIgnoreFilter.js +1 -1
  71. package/dist/git/watching/gitIgnoreFilter.js.map +1 -1
  72. package/dist/git/watching/initWatcher.d.ts.map +1 -1
  73. package/dist/git/watching/initWatcher.js.map +1 -1
  74. package/dist/git/watching/watchGroup.d.ts.map +1 -1
  75. package/dist/git/watching/watchGroup.js.map +1 -1
  76. package/dist/git/watching/watchService.d.ts.map +1 -1
  77. package/dist/git/watching/watchService.js +24 -4
  78. package/dist/git/watching/watchService.js.map +1 -1
  79. package/dist/git/watching/watchSession.d.ts.map +1 -1
  80. package/dist/git/watching/watchSession.js.map +1 -1
  81. package/dist/git-cli/exec/exec.types.d.ts +1 -1
  82. package/dist/git-cli/exec/exec.types.d.ts.map +1 -1
  83. package/dist/git-cli/exec/git.d.ts +3 -3
  84. package/dist/git-cli/exec/git.d.ts.map +1 -1
  85. package/dist/git-cli/exec/git.js.map +1 -1
  86. package/dist/git-cli/exec/gitQueue.d.ts +10 -2
  87. package/dist/git-cli/exec/gitQueue.d.ts.map +1 -1
  88. package/dist/git-cli/exec/gitQueue.js +39 -12
  89. package/dist/git-cli/exec/gitQueue.js.map +1 -1
  90. package/dist/git-cli/parsers/blameParser.d.ts.map +1 -1
  91. package/dist/git-cli/parsers/blameParser.js.map +1 -1
  92. package/dist/git-cli/parsers/logParser.js.map +1 -1
  93. package/dist/git-cli/parsers/refParser.d.ts +3 -30
  94. package/dist/git-cli/parsers/refParser.d.ts.map +1 -1
  95. package/dist/git-cli/parsers/refParser.js +30 -30
  96. package/dist/git-cli/parsers/refParser.js.map +1 -1
  97. package/dist/git-cli/providers/blame.d.ts.map +1 -1
  98. package/dist/git-cli/providers/blame.js.map +1 -1
  99. package/dist/git-cli/providers/branches.d.ts +15 -5
  100. package/dist/git-cli/providers/branches.d.ts.map +1 -1
  101. package/dist/git-cli/providers/branches.js +98 -68
  102. package/dist/git-cli/providers/branches.js.map +1 -1
  103. package/dist/git-cli/providers/commits.d.ts.map +1 -1
  104. package/dist/git-cli/providers/commits.js +74 -30
  105. package/dist/git-cli/providers/commits.js.map +1 -1
  106. package/dist/git-cli/providers/config.d.ts +11 -2
  107. package/dist/git-cli/providers/config.d.ts.map +1 -1
  108. package/dist/git-cli/providers/config.js +27 -2
  109. package/dist/git-cli/providers/config.js.map +1 -1
  110. package/dist/git-cli/providers/diff.d.ts.map +1 -1
  111. package/dist/git-cli/providers/diff.js +4 -2
  112. package/dist/git-cli/providers/diff.js.map +1 -1
  113. package/dist/git-cli/providers/graph.d.ts.map +1 -1
  114. package/dist/git-cli/providers/graph.js +7 -2
  115. package/dist/git-cli/providers/graph.js.map +1 -1
  116. package/dist/git-cli/providers/operations.d.ts +13 -11
  117. package/dist/git-cli/providers/operations.d.ts.map +1 -1
  118. package/dist/git-cli/providers/operations.js +108 -38
  119. package/dist/git-cli/providers/operations.js.map +1 -1
  120. package/dist/git-cli/providers/patch.d.ts.map +1 -1
  121. package/dist/git-cli/providers/patch.js.map +1 -1
  122. package/dist/git-cli/providers/pausedOperations.d.ts +3 -1
  123. package/dist/git-cli/providers/pausedOperations.d.ts.map +1 -1
  124. package/dist/git-cli/providers/pausedOperations.js +7 -1
  125. package/dist/git-cli/providers/pausedOperations.js.map +1 -1
  126. package/dist/git-cli/providers/refs.d.ts +19 -2
  127. package/dist/git-cli/providers/refs.d.ts.map +1 -1
  128. package/dist/git-cli/providers/refs.js +286 -2
  129. package/dist/git-cli/providers/refs.js.map +1 -1
  130. package/dist/git-cli/providers/revision.js.map +1 -1
  131. package/dist/git-cli/providers/staging.d.ts +6 -0
  132. package/dist/git-cli/providers/staging.d.ts.map +1 -1
  133. package/dist/git-cli/providers/staging.js +32 -0
  134. package/dist/git-cli/providers/staging.js.map +1 -1
  135. package/dist/git-cli/providers/status.d.ts +4 -1
  136. package/dist/git-cli/providers/status.d.ts.map +1 -1
  137. package/dist/git-cli/providers/status.js +4 -3
  138. package/dist/git-cli/providers/status.js.map +1 -1
  139. package/dist/git-cli/providers/tags.d.ts.map +1 -1
  140. package/dist/git-cli/providers/tags.js +10 -6
  141. package/dist/git-cli/providers/tags.js.map +1 -1
  142. package/dist/ipc/discovery.d.ts +27 -0
  143. package/dist/ipc/discovery.d.ts.map +1 -0
  144. package/dist/ipc/discovery.js +38 -0
  145. package/dist/ipc/discovery.js.map +1 -0
  146. package/dist/ipc/ipcServer.d.ts +20 -0
  147. package/dist/ipc/ipcServer.d.ts.map +1 -0
  148. package/dist/ipc/ipcServer.js +162 -0
  149. package/dist/ipc/ipcServer.js.map +1 -0
  150. package/dist/plus/agents/providers/claudeCodeProvider.d.ts +114 -0
  151. package/dist/plus/agents/providers/claudeCodeProvider.d.ts.map +1 -0
  152. package/dist/plus/agents/providers/claudeCodeProvider.js +1480 -0
  153. package/dist/plus/agents/providers/claudeCodeProvider.js.map +1 -0
  154. package/dist/plus/agents/providers/claudeCodeTranscript.d.ts +36 -0
  155. package/dist/plus/agents/providers/claudeCodeTranscript.d.ts.map +1 -0
  156. package/dist/plus/agents/providers/claudeCodeTranscript.js +171 -0
  157. package/dist/plus/agents/providers/claudeCodeTranscript.js.map +1 -0
  158. package/dist/plus/agents/sanitizePrompt.d.ts +15 -0
  159. package/dist/plus/agents/sanitizePrompt.d.ts.map +1 -0
  160. package/dist/plus/agents/sanitizePrompt.js +103 -0
  161. package/dist/plus/agents/sanitizePrompt.js.map +1 -0
  162. package/dist/plus/agents/stateMachine.d.ts +32 -0
  163. package/dist/plus/agents/stateMachine.d.ts.map +1 -0
  164. package/dist/plus/agents/stateMachine.js +180 -0
  165. package/dist/plus/agents/stateMachine.js.map +1 -0
  166. package/dist/plus/agents/types.d.ts +203 -0
  167. package/dist/plus/agents/types.d.ts.map +1 -0
  168. package/dist/plus/agents/types.js +49 -0
  169. package/dist/plus/agents/types.js.map +1 -0
  170. package/dist/plus/ai/errors.d.ts +4 -1
  171. package/dist/plus/ai/errors.d.ts.map +1 -1
  172. package/dist/plus/ai/errors.js +38 -0
  173. package/dist/plus/ai/errors.js.map +1 -1
  174. package/dist/plus/ai/models/promptTemplates.d.ts +11 -2
  175. package/dist/plus/ai/models/promptTemplates.d.ts.map +1 -1
  176. package/dist/plus/ai/prompts.d.ts +1 -0
  177. package/dist/plus/ai/prompts.d.ts.map +1 -1
  178. package/dist/plus/ai/prompts.js +48 -8
  179. package/dist/plus/ai/prompts.js.map +1 -1
  180. package/dist/plus/ai/utils/results.utils.d.ts.map +1 -1
  181. package/dist/plus/ai/utils/results.utils.js.map +1 -1
  182. package/dist/plus/git-github/api/github.d.ts +10 -1
  183. package/dist/plus/git-github/api/github.d.ts.map +1 -1
  184. package/dist/plus/git-github/api/github.js +156 -49
  185. package/dist/plus/git-github/api/github.js.map +1 -1
  186. package/dist/plus/git-github/api/types.d.ts +8 -0
  187. package/dist/plus/git-github/api/types.d.ts.map +1 -1
  188. package/dist/plus/git-github/models.d.ts +1 -1
  189. package/dist/plus/git-github/models.d.ts.map +1 -1
  190. package/dist/plus/git-github/models.js.map +1 -1
  191. package/dist/plus/git-github/providers/github/blame.d.ts.map +1 -1
  192. package/dist/plus/git-github/providers/github/blame.js.map +1 -1
  193. package/dist/plus/git-github/providers/github/branches.d.ts +5 -1
  194. package/dist/plus/git-github/providers/github/branches.d.ts.map +1 -1
  195. package/dist/plus/git-github/providers/github/branches.js +47 -24
  196. package/dist/plus/git-github/providers/github/branches.js.map +1 -1
  197. package/dist/plus/git-github/providers/github/commits.d.ts.map +1 -1
  198. package/dist/plus/git-github/providers/github/commits.js +144 -92
  199. package/dist/plus/git-github/providers/github/commits.js.map +1 -1
  200. package/dist/plus/git-github/providers/github/diff.d.ts +1 -1
  201. package/dist/plus/git-github/providers/github/diff.d.ts.map +1 -1
  202. package/dist/plus/git-github/providers/github/diff.js +23 -9
  203. package/dist/plus/git-github/providers/github/diff.js.map +1 -1
  204. package/dist/plus/git-github/providers/github/refs.d.ts +9 -1
  205. package/dist/plus/git-github/providers/github/refs.d.ts.map +1 -1
  206. package/dist/plus/git-github/providers/github/refs.js +38 -13
  207. package/dist/plus/git-github/providers/github/refs.js.map +1 -1
  208. package/dist/plus/git-github/providers/github/revision.d.ts.map +1 -1
  209. package/dist/plus/git-github/providers/github/revision.js +1 -2
  210. package/dist/plus/git-github/providers/github/revision.js.map +1 -1
  211. package/dist/plus/git-github/providers/github/status.d.ts +4 -1
  212. package/dist/plus/git-github/providers/github/status.d.ts.map +1 -1
  213. package/dist/plus/git-github/providers/github/status.js +1 -1
  214. package/dist/plus/git-github/providers/github/status.js.map +1 -1
  215. package/dist/plus/git-github/providers/github/tags.d.ts.map +1 -1
  216. package/dist/plus/git-github/providers/github/tags.js +35 -36
  217. package/dist/plus/git-github/providers/github/tags.js.map +1 -1
  218. package/dist/plus/git-github/providers/githubGitProvider.d.ts.map +1 -1
  219. package/dist/plus/git-github/providers/githubGitProvider.js.map +1 -1
  220. package/dist/utils/color.d.ts.map +1 -1
  221. package/dist/utils/color.js.map +1 -1
  222. package/dist/utils/decorators/serializable.d.ts.map +1 -1
  223. package/dist/utils/decorators/serializable.js.map +1 -1
  224. package/dist/utils/dedupedAsyncCache.d.ts +37 -0
  225. package/dist/utils/dedupedAsyncCache.d.ts.map +1 -0
  226. package/dist/utils/dedupedAsyncCache.js +68 -0
  227. package/dist/utils/dedupedAsyncCache.js.map +1 -0
  228. package/dist/utils/diff.d.ts +2 -0
  229. package/dist/utils/diff.d.ts.map +1 -0
  230. package/dist/utils/diff.js +53 -0
  231. package/dist/utils/diff.js.map +1 -0
  232. package/dist/utils/disposable.d.ts.map +1 -1
  233. package/dist/utils/disposable.js.map +1 -1
  234. package/dist/utils/env/node/exec.d.ts.map +1 -1
  235. package/dist/utils/env/node/exec.js.map +1 -1
  236. package/dist/utils/error.d.ts.map +1 -1
  237. package/dist/utils/error.js.map +1 -1
  238. package/dist/utils/iterable.d.ts.map +1 -1
  239. package/dist/utils/iterable.js.map +1 -1
  240. package/dist/utils/logger.d.ts.map +1 -1
  241. package/dist/utils/logger.js.map +1 -1
  242. package/dist/utils/lruMap.d.ts +9 -0
  243. package/dist/utils/lruMap.d.ts.map +1 -1
  244. package/dist/utils/lruMap.js +26 -4
  245. package/dist/utils/lruMap.js.map +1 -1
  246. package/dist/utils/markdown.d.ts.map +1 -1
  247. package/dist/utils/markdown.js.map +1 -1
  248. package/dist/utils/mru.d.ts.map +1 -1
  249. package/dist/utils/mru.js.map +1 -1
  250. package/dist/utils/path.d.ts.map +1 -1
  251. package/dist/utils/path.js.map +1 -1
  252. package/dist/utils/promise.d.ts +1 -0
  253. package/dist/utils/promise.d.ts.map +1 -1
  254. package/dist/utils/promise.js +17 -0
  255. package/dist/utils/promise.js.map +1 -1
  256. package/dist/utils/promiseCache.d.ts +10 -0
  257. package/dist/utils/promiseCache.d.ts.map +1 -1
  258. package/dist/utils/promiseCache.js +22 -0
  259. package/dist/utils/promiseCache.js.map +1 -1
  260. package/dist/utils/string.d.ts.map +1 -1
  261. package/dist/utils/string.js.map +1 -1
  262. package/dist/utils/uri.d.ts.map +1 -1
  263. package/dist/utils/uri.js.map +1 -1
  264. package/dist/utils/version.d.ts.map +1 -1
  265. package/dist/utils/version.js.map +1 -1
  266. package/package.json +13 -1
  267. package/src/git/cache.ts +142 -28
  268. package/src/git/models/commit.ts +1 -0
  269. package/src/git/models/reference.ts +33 -0
  270. package/src/git/models/remote.ts +1 -0
  271. package/src/git/models/repository.ts +21 -3
  272. package/src/git/models/search.ts +2 -2
  273. package/src/git/models/worktree.ts +10 -2
  274. package/src/git/parsers/diffParser.ts +2 -0
  275. package/src/git/providers/branches.ts +22 -4
  276. package/src/git/providers/config.ts +2 -0
  277. package/src/git/providers/operations.ts +44 -0
  278. package/src/git/providers/pausedOperations.ts +1 -0
  279. package/src/git/providers/refs.ts +32 -3
  280. package/src/git/providers/staging.ts +12 -0
  281. package/src/git/providers/status.ts +6 -1
  282. package/src/git/remotes/azure-devops.ts +2 -0
  283. package/src/git/{exec.types.ts → run.types.ts} +22 -2
  284. package/src/git/service.ts +1 -0
  285. package/src/git/utils/autolink.utils.ts +1 -0
  286. package/src/git/utils/blame.utils.ts +2 -0
  287. package/src/git/utils/conflictResolution.utils.ts +31 -0
  288. package/src/git/utils/fileStatus.utils.ts +7 -1
  289. package/src/git/utils/search.utils.ts +2 -0
  290. package/src/git/utils/sorting.ts +17 -0
  291. package/src/git/utils/status.utils.ts +1 -0
  292. package/src/git/watching/classifyChange.ts +9 -0
  293. package/src/git/watching/gitIgnoreFilter.ts +2 -2
  294. package/src/git/watching/initWatcher.ts +2 -0
  295. package/src/git/watching/watchGroup.ts +3 -0
  296. package/src/git/watching/watchService.ts +38 -5
  297. package/src/git/watching/watchSession.ts +10 -0
  298. package/src/git-cli/exec/exec.types.ts +2 -2
  299. package/src/git-cli/exec/git.ts +6 -5
  300. package/src/git-cli/exec/gitQueue.ts +41 -12
  301. package/src/git-cli/parsers/blameParser.ts +1 -0
  302. package/src/git-cli/parsers/logParser.ts +1 -0
  303. package/src/git-cli/parsers/refParser.ts +33 -34
  304. package/src/git-cli/providers/blame.ts +1 -0
  305. package/src/git-cli/providers/branches.ts +164 -93
  306. package/src/git-cli/providers/commits.ts +83 -37
  307. package/src/git-cli/providers/config.ts +41 -2
  308. package/src/git-cli/providers/diff.ts +8 -2
  309. package/src/git-cli/providers/graph.ts +11 -2
  310. package/src/git-cli/providers/operations.ts +186 -49
  311. package/src/git-cli/providers/patch.ts +1 -0
  312. package/src/git-cli/providers/pausedOperations.ts +8 -0
  313. package/src/git-cli/providers/refs.ts +247 -3
  314. package/src/git-cli/providers/revision.ts +2 -2
  315. package/src/git-cli/providers/staging.ts +33 -0
  316. package/src/git-cli/providers/status.ts +15 -4
  317. package/src/git-cli/providers/tags.ts +15 -16
  318. package/src/ipc/discovery.ts +56 -0
  319. package/src/ipc/ipcServer.ts +171 -0
  320. package/src/plus/agents/providers/claudeCodeProvider.ts +1782 -0
  321. package/src/plus/agents/providers/claudeCodeTranscript.ts +214 -0
  322. package/src/plus/agents/sanitizePrompt.ts +109 -0
  323. package/src/plus/agents/stateMachine.ts +197 -0
  324. package/src/plus/agents/types.ts +277 -0
  325. package/src/plus/ai/errors.ts +40 -0
  326. package/src/plus/ai/models/promptTemplates.ts +13 -0
  327. package/src/plus/ai/prompts.ts +49 -8
  328. package/src/plus/ai/utils/results.utils.ts +2 -0
  329. package/src/plus/git-github/api/github.ts +199 -53
  330. package/src/plus/git-github/api/types.ts +15 -0
  331. package/src/plus/git-github/models.ts +1 -1
  332. package/src/plus/git-github/providers/github/blame.ts +2 -0
  333. package/src/plus/git-github/providers/github/branches.ts +61 -31
  334. package/src/plus/git-github/providers/github/commits.ts +231 -160
  335. package/src/plus/git-github/providers/github/diff.ts +27 -19
  336. package/src/plus/git-github/providers/github/refs.ts +57 -22
  337. package/src/plus/git-github/providers/github/revision.ts +1 -2
  338. package/src/plus/git-github/providers/github/status.ts +6 -1
  339. package/src/plus/git-github/providers/github/tags.ts +61 -62
  340. package/src/plus/git-github/providers/githubGitProvider.ts +1 -0
  341. package/src/utils/color.ts +2 -0
  342. package/src/utils/decorators/serializable.ts +1 -0
  343. package/src/utils/dedupedAsyncCache.ts +74 -0
  344. package/src/utils/diff.ts +61 -0
  345. package/src/utils/disposable.ts +2 -0
  346. package/src/utils/env/node/exec.ts +2 -0
  347. package/src/utils/error.ts +1 -0
  348. package/src/utils/iterable.ts +3 -0
  349. package/src/utils/logger.ts +1 -0
  350. package/src/utils/lruMap.ts +29 -4
  351. package/src/utils/markdown.ts +1 -0
  352. package/src/utils/mru.ts +2 -0
  353. package/src/utils/path.ts +1 -0
  354. package/src/utils/promise.ts +22 -0
  355. package/src/utils/promiseCache.ts +24 -0
  356. package/src/utils/string.ts +2 -0
  357. package/src/utils/uri.ts +1 -0
  358. package/src/utils/version.ts +1 -0
  359. package/dist/git/exec.types.d.ts.map +0 -1
  360. package/dist/git/exec.types.js +0 -2
  361. package/dist/git/exec.types.js.map +0 -1
@@ -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,
@@ -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()
@@ -648,6 +685,7 @@ export class ConfigGitSubProvider implements GitConfigSubProvider {
648
685
  this._migratedRepos.add(repoPath);
649
686
  return;
650
687
  }
688
+
651
689
  const gkConfigFolder = joinPaths(gkConfigPath, '..');
652
690
 
653
691
  // If .git/gk/config already exists, consider migration done — it was either
@@ -662,6 +700,7 @@ export class ConfigGitSubProvider implements GitConfigSubProvider {
662
700
 
663
701
  // If .git/gk/config doesn't exist, create an empty file to prevent multiple migration attempts in future sessions
664
702
  if (!(await this.ensureGkConfigFolder(gkConfigFolder, scope))) return;
703
+
665
704
  try {
666
705
  await fs.writeFile(gkConfigPath, new Uint8Array());
667
706
  } catch (ex) {
@@ -105,7 +105,7 @@ export class DiffGitSubProvider implements GitDiffSubProvider {
105
105
  return stat;
106
106
  } catch (ex) {
107
107
  const msg: string = ex?.toString() ?? '';
108
- if (GitErrors.noMergeBase.test(msg)) {
108
+ if (GitErrors.noMergeBase.test(msg) || GitErrors.badRevision.test(msg)) {
109
109
  return undefined;
110
110
  }
111
111
 
@@ -258,6 +258,7 @@ export class DiffGitSubProvider implements GitDiffSubProvider {
258
258
  for (const file of untracked) {
259
259
  if (seen.has(file.path)) continue;
260
260
  if (options.path && options.path !== file.path) continue;
261
+
261
262
  files.push(file);
262
263
  }
263
264
  }
@@ -328,7 +329,12 @@ export class DiffGitSubProvider implements GitDiffSubProvider {
328
329
  // Follow file history and specify the file path
329
330
  args.push('--follow', '--', relativePath);
330
331
 
331
- const result = await this.git.run({ cwd: repoPath, configs: gitConfigsLog }, ...args);
332
+ // Unbounded --follow walk over file history can be slow on long histories — queue as background
333
+ // so it doesn't compete with interactive work.
334
+ const result = await this.git.run(
335
+ { cwd: repoPath, configs: gitConfigsLog, priority: 'background' },
336
+ ...args,
337
+ );
332
338
 
333
339
  let currentSha;
334
340
  let currentPath;
@@ -234,7 +234,14 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
234
234
  // - SHA + limit = 0: Find SHA, stop immediately
235
235
  // - No SHA + limit > 0: Load exactly `limit` commits
236
236
  // - No SHA + limit = 0: Load everything remaining
237
- if ((limit && count >= limit && (!sha || found)) || (!limit && sha && found)) {
237
+ // - SHA + limit > 0 + still unfound past 10× limit: defensive cap so an unreachable
238
+ // SHA (e.g. a stale merge-base the webview hasn't yet seen invalidated) can't
239
+ // walk the entire history. `hasMore=true` lets callers retry; the graph-wrapper
240
+ // side deduplicates re-requests so the cap doesn't loop.
241
+ if (
242
+ (limit && count >= limit && (!sha || found || count >= limit * 10)) ||
243
+ (!limit && sha && found)
244
+ ) {
238
245
  hasMore = true;
239
246
  aborter.abort();
240
247
  break;
@@ -250,6 +257,7 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
250
257
  total++;
251
258
  shaOrRemapped = remappedIds.get(commit.sha);
252
259
  if (shaOrRemapped && ids.has(shaOrRemapped)) continue;
260
+
253
261
  shaOrRemapped ??= commit.sha;
254
262
 
255
263
  ids.add(shaOrRemapped);
@@ -464,7 +472,7 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
464
472
  args.push(`--${ordering}-order`, '--all');
465
473
 
466
474
  const statsResult = await this.git.run(
467
- { cwd: repoPath, configs: gitConfigsLog, stdin: stdin },
475
+ { cwd: repoPath, configs: gitConfigsLog, stdin: stdin, priority: 'background' },
468
476
  'log',
469
477
  stdin ? '--stdin' : undefined,
470
478
  ...args,
@@ -725,6 +733,7 @@ export class GraphGitSubProvider implements GitGraphSubProvider {
725
733
  runningTotal: results.size,
726
734
  hasMore: true,
727
735
  };
736
+
728
737
  batch.length = 0;
729
738
  lastProgressTime = Date.now();
730
739
  }
@@ -16,7 +16,11 @@ import {
16
16
  import type { GitBranchReference, GitReference } from '../../git/models/reference.js';
17
17
  import type { SigningFormat } from '../../git/models/signature.js';
18
18
  import type { GitConflictFile } from '../../git/models/staging.js';
19
- import type { GitOperationResult, GitOperationsSubProvider } from '../../git/providers/operations.js';
19
+ import type {
20
+ GitOperationResult,
21
+ GitOperationRunOptions,
22
+ GitOperationsSubProvider,
23
+ } from '../../git/providers/operations.js';
20
24
  import { getBranchNameAndRemote, getBranchTrackingWithoutRemote } from '../../git/utils/branch.utils.js';
21
25
  import { isBranchReference } from '../../git/utils/reference.utils.js';
22
26
  import { debug } from '../../utils/decorators/log.js';
@@ -32,6 +36,7 @@ import {
32
36
  gitConfigsPull,
33
37
  GitErrors,
34
38
  inferSigningFormatFromError,
39
+ maxGitCliLength,
35
40
  } from '../exec/git.js';
36
41
 
37
42
  export class OperationsGitSubProvider implements GitOperationsSubProvider {
@@ -47,11 +52,12 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
47
52
  repoPath: string,
48
53
  ref: string,
49
54
  options?: { createBranch?: string } | { path?: string },
55
+ runOptions?: GitOperationRunOptions,
50
56
  ): Promise<void> {
51
57
  const scope = getScopedLogger();
52
58
 
53
59
  try {
54
- await this.checkoutCore(repoPath, ref, options);
60
+ await this.checkoutCore(repoPath, ref, options, runOptions);
55
61
  this.context.hooks?.cache?.onReset?.(repoPath, 'branches', 'status');
56
62
  this.context.hooks?.repository?.onChanged?.(repoPath, ['head', 'heads', 'index']);
57
63
  } catch (ex) {
@@ -64,6 +70,7 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
64
70
  repoPath: string,
65
71
  ref: string,
66
72
  { createBranch, path }: { createBranch?: string; path?: string } = {},
73
+ runOptions?: GitOperationRunOptions,
67
74
  ): Promise<void> {
68
75
  const params = ['checkout'];
69
76
  if (createBranch) {
@@ -78,7 +85,7 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
78
85
  }
79
86
 
80
87
  try {
81
- await this.git.run({ cwd: repoPath }, ...params);
88
+ await this.git.run({ cwd: repoPath, ...runOptions }, ...params);
82
89
  } catch (ex) {
83
90
  throw getGitCommandError(
84
91
  'checkout',
@@ -92,12 +99,101 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
92
99
  }
93
100
  }
94
101
 
102
+ @debug()
103
+ async checkoutConflictedPath(
104
+ repoPath: string,
105
+ path: string,
106
+ side: 'ours' | 'theirs',
107
+ runOptions?: GitOperationRunOptions,
108
+ ): Promise<void> {
109
+ const scope = getScopedLogger();
110
+
111
+ [path, repoPath] = splitPath(path, repoPath, true);
112
+ const params = ['checkout', `--${side}`, '--', path];
113
+
114
+ try {
115
+ await this.git.run({ cwd: repoPath, ...runOptions }, ...params);
116
+ this.context.hooks?.cache?.onReset?.(repoPath, 'status');
117
+ this.context.hooks?.repository?.onChanged?.(repoPath, ['index']);
118
+ } catch (ex) {
119
+ scope?.error(ex);
120
+ throw getGitCommandError(
121
+ 'checkout',
122
+ ex,
123
+ reason =>
124
+ new CheckoutError(
125
+ { reason: reason ?? 'other', ref: side, gitCommand: { repoPath: repoPath, args: params } },
126
+ ex,
127
+ ),
128
+ );
129
+ }
130
+ }
131
+
132
+ @debug()
133
+ async checkoutConflictedPaths(
134
+ repoPath: string,
135
+ paths: string[],
136
+ side: 'ours' | 'theirs',
137
+ runOptions?: GitOperationRunOptions,
138
+ ): Promise<void> {
139
+ if (!paths.length) return;
140
+
141
+ const scope = getScopedLogger();
142
+ const normalized = paths.map(p => splitPath(p, repoPath, true)[0]);
143
+
144
+ // Build batches by accumulating the length of each path + a small per-arg overhead (separator +
145
+ // possible quoting), so a single long path — or just the overhead itself — can't push a batch
146
+ // past the CLI length limit. Always keep at least one path per batch, even if a lone path is
147
+ // longer than the limit (the OS may still accept it on some platforms).
148
+ const perArgOverhead = 3;
149
+ const batches: string[][] = [];
150
+ let current: string[] = [];
151
+ let currentLen = 0;
152
+ for (const p of normalized) {
153
+ const cost = p.length + perArgOverhead;
154
+ if (current.length > 0 && currentLen + cost > maxGitCliLength) {
155
+ batches.push(current);
156
+ current = [];
157
+ currentLen = 0;
158
+ }
159
+ current.push(p);
160
+ currentLen += cost;
161
+ }
162
+ if (current.length) {
163
+ batches.push(current);
164
+ }
165
+
166
+ try {
167
+ for (const batch of batches) {
168
+ await this.git.run({ cwd: repoPath, ...runOptions }, 'checkout', `--${side}`, '--', ...batch);
169
+ }
170
+ this.context.hooks?.cache?.onReset?.(repoPath, 'status');
171
+ this.context.hooks?.repository?.onChanged?.(repoPath, ['index']);
172
+ } catch (ex) {
173
+ scope?.error(ex);
174
+ throw getGitCommandError(
175
+ 'checkout',
176
+ ex,
177
+ reason =>
178
+ new CheckoutError(
179
+ {
180
+ reason: reason ?? 'other',
181
+ ref: side,
182
+ gitCommand: { repoPath: repoPath, args: ['checkout', `--${side}`, '--', ...normalized] },
183
+ },
184
+ ex,
185
+ ),
186
+ );
187
+ }
188
+ }
189
+
95
190
  @sequentialize({ getQueueKey: rp => rp })
96
191
  @debug()
97
192
  async cherryPick(
98
193
  repoPath: string,
99
194
  revs: string[],
100
195
  options?: { edit?: boolean; noCommit?: boolean; source?: unknown },
196
+ runOptions?: GitOperationRunOptions,
101
197
  ): Promise<GitOperationResult> {
102
198
  const scope = getScopedLogger();
103
199
 
@@ -110,7 +206,8 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
110
206
  }
111
207
 
112
208
  if (revs.length > 1) {
113
- // Sort commits in topological order (oldest ancestor first) so cherry-pick applies them correctly
209
+ // Sort commits in topological order (oldest ancestor first) so cherry-pick applies them correctly.
210
+ // This is a local read-only pre-flight, not the user-initiated op — don't subject it to caller env/timeout.
114
211
  const revsSet = new Set(revs);
115
212
  const result = await this.git.run({ cwd: repoPath }, 'rev-list', '--topo-order', '--reverse', ...revs);
116
213
 
@@ -131,7 +228,7 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
131
228
  args.push(...revs);
132
229
 
133
230
  try {
134
- await this.git.run({ cwd: repoPath, errors: 'throw' }, ...args);
231
+ await this.git.run({ cwd: repoPath, errors: 'throw', ...runOptions }, ...args);
135
232
  this.context.hooks?.cache?.onReset?.(repoPath, 'branches', 'status');
136
233
  this.context.hooks?.repository?.onChanged?.(repoPath, ['head', 'heads', 'index']);
137
234
  return { conflicted: false };
@@ -168,6 +265,7 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
168
265
  signoff?: boolean;
169
266
  source?: unknown;
170
267
  },
268
+ runOptions?: GitOperationRunOptions,
171
269
  ): Promise<void> {
172
270
  const scope = getScopedLogger();
173
271
 
@@ -194,7 +292,10 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
194
292
  params.push('-F', '-');
195
293
 
196
294
  try {
197
- await this.git.run({ cwd: repoPath, stdin: message, stdinEncoding: 'utf8', errors: 'throw' }, ...params);
295
+ await this.git.run(
296
+ { cwd: repoPath, stdin: message, stdinEncoding: 'utf8', errors: 'throw', ...runOptions },
297
+ ...params,
298
+ );
198
299
  this.context.hooks?.cache?.onReset?.(repoPath, 'branches', 'status');
199
300
  this.context.hooks?.repository?.onChanged?.(repoPath, ['head', 'heads', 'index']);
200
301
  } catch (ex) {
@@ -217,6 +318,7 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
217
318
  async fetch(
218
319
  repoPath: string,
219
320
  options?: { all?: boolean; branch?: GitBranchReference; prune?: boolean; pull?: boolean; remote?: string },
321
+ runOptions?: GitOperationRunOptions,
220
322
  ): Promise<void> {
221
323
  const scope = getScopedLogger();
222
324
 
@@ -226,14 +328,18 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
226
328
  const [branchName, remoteName] = getBranchNameAndRemote(branch);
227
329
  if (remoteName == null) return;
228
330
 
229
- await this.fetchCore(repoPath, {
230
- branch: branchName,
231
- remote: remoteName,
232
- upstream: getBranchTrackingWithoutRemote(branch)!,
233
- pull: options?.pull,
234
- });
331
+ await this.fetchCore(
332
+ repoPath,
333
+ {
334
+ branch: branchName,
335
+ remote: remoteName,
336
+ upstream: getBranchTrackingWithoutRemote(branch)!,
337
+ pull: options?.pull,
338
+ },
339
+ runOptions,
340
+ );
235
341
  } else {
236
- await this.fetchCore(repoPath, opts);
342
+ await this.fetchCore(repoPath, opts, runOptions);
237
343
  }
238
344
 
239
345
  this.context.hooks?.cache?.onReset?.(repoPath, 'branches', 'tags');
@@ -256,6 +362,7 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
256
362
  remote: string;
257
363
  upstream: string;
258
364
  },
365
+ runOptions?: GitOperationRunOptions,
259
366
  ): Promise<void> {
260
367
  const params = ['fetch'];
261
368
 
@@ -276,7 +383,7 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
276
383
  }
277
384
 
278
385
  try {
279
- await this.git.run({ cwd: repoPath }, ...params);
386
+ await this.git.run({ cwd: repoPath, ...runOptions }, ...params);
280
387
  } catch (ex) {
281
388
  throw getGitCommandError(
282
389
  'fetch',
@@ -301,6 +408,7 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
301
408
  repoPath: string,
302
409
  ref: string,
303
410
  options?: { fastForward?: boolean | 'only'; noCommit?: boolean; squash?: boolean; source?: unknown },
411
+ runOptions?: GitOperationRunOptions,
304
412
  ): Promise<GitOperationResult> {
305
413
  const scope = getScopedLogger();
306
414
 
@@ -325,7 +433,7 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
325
433
  try {
326
434
  await this.git.run(
327
435
  // Avoid a timeout since merges can take a long time (set to 0 to disable)
328
- { cwd: repoPath, errors: 'throw', timeout: 0 },
436
+ { cwd: repoPath, errors: 'throw', timeout: 0, ...runOptions },
329
437
  ...args,
330
438
  );
331
439
 
@@ -356,6 +464,7 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
356
464
  async pull(
357
465
  repoPath: string,
358
466
  options?: { branch?: GitBranchReference; rebase?: boolean; tags?: boolean; source?: unknown },
467
+ runOptions?: GitOperationRunOptions,
359
468
  ): Promise<void> {
360
469
  const scope = getScopedLogger();
361
470
 
@@ -369,25 +478,33 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
369
478
  );
370
479
  if (worktree != null) {
371
480
  // Branch is checked out in a worktree — run git pull in that worktree's directory
372
- await this.pullCore(normalizePath(worktree.uri.fsPath), {
373
- rebase: options?.rebase,
374
- tags: options?.tags,
375
- source: options?.source,
376
- });
481
+ await this.pullCore(
482
+ normalizePath(worktree.uri.fsPath),
483
+ {
484
+ rebase: options?.rebase,
485
+ tags: options?.tags,
486
+ source: options?.source,
487
+ },
488
+ runOptions,
489
+ );
377
490
  this.context.hooks?.cache?.onReset?.(repoPath, 'branches', 'status', 'tags');
378
491
  this.context.hooks?.repository?.onChanged?.(repoPath, ['head', 'heads', 'remotes', 'index']);
379
492
  } else {
380
493
  // Branch is not checked out anywhere — can only fetch (no working tree to merge into)
381
- await this.fetch(repoPath, { branch: branch });
494
+ await this.fetch(repoPath, { branch: branch }, runOptions);
382
495
  }
383
496
  return;
384
497
  }
385
498
 
386
- await this.pullCore(repoPath, {
387
- rebase: options?.rebase,
388
- tags: options?.tags,
389
- source: options?.source,
390
- });
499
+ await this.pullCore(
500
+ repoPath,
501
+ {
502
+ rebase: options?.rebase,
503
+ tags: options?.tags,
504
+ source: options?.source,
505
+ },
506
+ runOptions,
507
+ );
391
508
 
392
509
  this.context.hooks?.cache?.onReset?.(repoPath, 'branches', 'status', 'tags');
393
510
  this.context.hooks?.repository?.onChanged?.(repoPath, ['head', 'heads', 'remotes', 'index']);
@@ -400,6 +517,7 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
400
517
  private async pullCore(
401
518
  repoPath: string,
402
519
  options: { rebase?: boolean; tags?: boolean; source?: unknown },
520
+ runOptions?: GitOperationRunOptions,
403
521
  ): Promise<void> {
404
522
  const params = ['pull'];
405
523
 
@@ -412,7 +530,7 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
412
530
  }
413
531
 
414
532
  try {
415
- await this.git.run({ cwd: repoPath, configs: gitConfigsPull }, ...params);
533
+ await this.git.run({ cwd: repoPath, configs: gitConfigsPull, ...runOptions }, ...params);
416
534
  } catch (ex) {
417
535
  await this.throwIfSigningError(repoPath, params, ex, options.source);
418
536
  throw getGitCommandError(
@@ -429,6 +547,7 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
429
547
  async push(
430
548
  repoPath: string,
431
549
  options?: { reference?: GitReference; force?: boolean; publish?: { remote: string } },
550
+ runOptions?: GitOperationRunOptions,
432
551
  ): Promise<void> {
433
552
  const scope = getScopedLogger();
434
553
 
@@ -487,27 +606,40 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
487
606
  }
488
607
 
489
608
  try {
490
- await this.pushCore(repoPath, {
491
- branch: branchName,
492
- remote: remoteName,
493
- upstream: upstreamName,
494
- force: forceOpts,
495
- publish: options?.publish != null,
496
- });
497
-
498
- // Since Git can't setup upstream tracking when publishing a new branch to a specific commit, do it now
609
+ await this.pushCore(
610
+ repoPath,
611
+ {
612
+ branch: branchName,
613
+ remote: remoteName,
614
+ upstream: upstreamName,
615
+ force: forceOpts,
616
+ publish: options?.publish != null,
617
+ },
618
+ runOptions,
619
+ );
620
+
621
+ // Since Git can't setup upstream tracking when publishing a new branch to a specific commit, do it now.
622
+ // Soft-fail: the push already succeeded, so don't let a local upstream-tracking failure (cancellation
623
+ // arriving mid-spawn, transient git error) suppress the post-push hook fan-out below.
499
624
  if (setUpstream != null) {
500
- await this.git.run(
501
- { cwd: repoPath },
502
- 'branch',
503
- '--set-upstream-to',
504
- `${setUpstream.remote}/${setUpstream.remoteBranch}`,
505
- setUpstream.branch,
506
- );
625
+ try {
626
+ await this.git.run(
627
+ { cwd: repoPath, ...runOptions },
628
+ 'branch',
629
+ '--set-upstream-to',
630
+ `${setUpstream.remote}/${setUpstream.remoteBranch}`,
631
+ setUpstream.branch,
632
+ );
633
+ } catch (ex) {
634
+ scope?.error(ex, 'Unable to set upstream tracking after publish');
635
+ }
507
636
  }
508
637
 
509
638
  this.context.hooks?.cache?.onReset?.(repoPath, 'branches');
510
- this.context.hooks?.repository?.onChanged?.(repoPath, ['remotes']);
639
+ this.context.hooks?.repository?.onChanged?.(
640
+ repoPath,
641
+ options?.publish != null ? ['config', 'heads', 'remotes'] : ['remotes'],
642
+ );
511
643
  } catch (ex) {
512
644
  scope?.error(ex);
513
645
  throw ex;
@@ -524,6 +656,7 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
524
656
  upstream?: string;
525
657
  delete?: { remote: string; branch: string };
526
658
  },
659
+ runOptions?: GitOperationRunOptions,
527
660
  ): Promise<void> {
528
661
  const params = ['push'];
529
662
 
@@ -555,7 +688,7 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
555
688
  }
556
689
 
557
690
  try {
558
- await this.git.run({ cwd: repoPath }, ...params);
691
+ await this.git.run({ cwd: repoPath, ...runOptions }, ...params);
559
692
  } catch (ex) {
560
693
  const error = getGitCommandError(
561
694
  'push',
@@ -629,6 +762,7 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
629
762
  updateRefs?: boolean;
630
763
  source?: unknown;
631
764
  },
765
+ runOptions?: GitOperationRunOptions,
632
766
  ): Promise<GitOperationResult> {
633
767
  const scope = getScopedLogger();
634
768
 
@@ -664,7 +798,7 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
664
798
  try {
665
799
  await this.git.run(
666
800
  // Avoid a timeout since rebases can take a long time (set to 0 to disable)
667
- { cwd: repoPath, errors: 'throw', configs: configs, timeout: 0 },
801
+ { cwd: repoPath, errors: 'throw', configs: configs, timeout: 0, ...runOptions },
668
802
  ...args,
669
803
  );
670
804
  this.context.hooks?.cache?.onReset?.(repoPath, 'branches', 'status');
@@ -698,11 +832,12 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
698
832
  repoPath: string,
699
833
  rev: string,
700
834
  options?: { mode?: 'hard' | 'keep' | 'merge' | 'mixed' | 'soft' },
835
+ runOptions?: GitOperationRunOptions,
701
836
  ): Promise<void> {
702
837
  const scope = getScopedLogger();
703
838
 
704
839
  try {
705
- await this.resetCore(repoPath, rev, options?.mode);
840
+ await this.resetCore(repoPath, rev, options?.mode, runOptions);
706
841
  this.context.hooks?.cache?.onReset?.(repoPath, 'branches', 'status');
707
842
  this.context.hooks?.repository?.onChanged?.(repoPath, ['head', 'heads', 'index']);
708
843
  } catch (ex) {
@@ -715,6 +850,7 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
715
850
  repoPath: string,
716
851
  rev: string,
717
852
  mode?: 'hard' | 'keep' | 'merge' | 'mixed' | 'soft',
853
+ runOptions?: GitOperationRunOptions,
718
854
  ): Promise<void> {
719
855
  const params = ['reset', '-q'];
720
856
  if (mode) {
@@ -723,7 +859,7 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
723
859
  params.push(rev, '--');
724
860
 
725
861
  try {
726
- await this.git.run({ cwd: repoPath }, ...params);
862
+ await this.git.run({ cwd: repoPath, ...runOptions }, ...params);
727
863
  } catch (ex) {
728
864
  throw getGitCommandError(
729
865
  'reset',
@@ -740,6 +876,7 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
740
876
  repoPath: string,
741
877
  refs: string[],
742
878
  options?: { editMessage?: boolean; source?: unknown },
879
+ runOptions?: GitOperationRunOptions,
743
880
  ): Promise<GitOperationResult> {
744
881
  const scope = getScopedLogger();
745
882
 
@@ -756,7 +893,7 @@ export class OperationsGitSubProvider implements GitOperationsSubProvider {
756
893
  try {
757
894
  await this.git.run(
758
895
  // Avoid a timeout since reverts can take a long time (set to 0 to disable)
759
- { cwd: repoPath, errors: 'throw', timeout: 0 },
896
+ { cwd: repoPath, errors: 'throw', timeout: 0, ...runOptions },
760
897
  ...args,
761
898
  );
762
899
 
@@ -113,6 +113,7 @@ export class PatchGitSubProvider implements GitPatchSubProvider {
113
113
  }
114
114
  } catch (ex) {
115
115
  if (ex instanceof ApplyPatchCommitError) throw ex;
116
+
116
117
  scope?.error(ex);
117
118
 
118
119
  if (CherryPickError.is(ex, 'wouldOverwriteChanges')) {
@@ -43,10 +43,18 @@ export class PausedOperationsGitSubProvider implements GitPausedOperationsSubPro
43
43
  @debug()
44
44
  getPausedOperationStatus(
45
45
  repoPath: string,
46
+ options?: { force?: boolean },
46
47
  cancellation?: AbortSignal,
47
48
  ): Promise<GitPausedOperationStatus | undefined> {
48
49
  const scope = getScopedLogger();
49
50
 
51
+ // Self-heal callers (WIP refresh) bypass the cache so a missed `'pausedOp'` FS-watcher
52
+ // event (CLI-driven completion, torn-down secondary-worktree watcher) can't leave the
53
+ // status stuck — the readdir-based detection is cheap enough to repeat on every tick.
54
+ if (options?.force) {
55
+ this.cache.pausedOperationStatus.delete(repoPath);
56
+ }
57
+
50
58
  const status = this.cache.pausedOperationStatus.getOrCreate(repoPath, async _cancellable => {
51
59
  const gitDir = await this.provider.config.getGitDir(repoPath);
52
60