gitpane 1.0.0__py3-none-any.whl

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 (353) hide show
  1. gitpane/__init__.py +0 -0
  2. gitpane/account/__init__.py +0 -0
  3. gitpane/account/account_choice.py +297 -0
  4. gitpane/account/address_owner.py +67 -0
  5. gitpane/account/authentication.py +1811 -0
  6. gitpane/account/authentication_worker.py +836 -0
  7. gitpane/account/credential_manager.py +480 -0
  8. gitpane/account/credential_manager_install.py +171 -0
  9. gitpane/account/keyring_reader.py +135 -0
  10. gitpane/account/keyring_state.py +158 -0
  11. gitpane/account/ssh.py +312 -0
  12. gitpane/account/ssh_askpass.py +216 -0
  13. gitpane/account/ssh_prompt.py +222 -0
  14. gitpane/application.py +684 -0
  15. gitpane/commit/__init__.py +0 -0
  16. gitpane/commit/commit_metadata.py +88 -0
  17. gitpane/commit/commit_push.py +276 -0
  18. gitpane/commit/commit_write.py +830 -0
  19. gitpane/config.py +1066 -0
  20. gitpane/controller/__init__.py +0 -0
  21. gitpane/controller/account_controller.py +677 -0
  22. gitpane/controller/clone_controller.py +776 -0
  23. gitpane/controller/close_readiness.py +212 -0
  24. gitpane/controller/commit_timing_controller.py +566 -0
  25. gitpane/controller/conflict_controller.py +885 -0
  26. gitpane/controller/console_registry.py +38 -0
  27. gitpane/controller/emoji_window_controller.py +95 -0
  28. gitpane/controller/file_action_controller.py +479 -0
  29. gitpane/controller/history_read_controller.py +671 -0
  30. gitpane/controller/history_rewrite_controller.py +739 -0
  31. gitpane/controller/navigation_controller.py +658 -0
  32. gitpane/controller/opening_controller.py +375 -0
  33. gitpane/controller/operation_policy.py +76 -0
  34. gitpane/controller/rebase_controller.py +797 -0
  35. gitpane/controller/rebase_editor_controller.py +309 -0
  36. gitpane/controller/reference_form_controller.py +1677 -0
  37. gitpane/controller/reference_read_controller.py +242 -0
  38. gitpane/controller/refresh_controller.py +477 -0
  39. gitpane/controller/replay_controller.py +805 -0
  40. gitpane/controller/session_registry.py +337 -0
  41. gitpane/controller/setting_controller.py +592 -0
  42. gitpane/controller/shutdown_controller.py +345 -0
  43. gitpane/controller/staging_controller.py +199 -0
  44. gitpane/controller/staging_session_registry.py +47 -0
  45. gitpane/controller/stash_controller.py +367 -0
  46. gitpane/controller/transport_controller.py +813 -0
  47. gitpane/controller/welcome_controller.py +102 -0
  48. gitpane/controller/window_activation.py +116 -0
  49. gitpane/controller/worktree_state_controller.py +169 -0
  50. gitpane/emoji/__init__.py +0 -0
  51. gitpane/emoji/emoji_catalogue.py +156 -0
  52. gitpane/git_config/__init__.py +0 -0
  53. gitpane/git_config/git_setting.py +441 -0
  54. gitpane/history/__init__.py +0 -0
  55. gitpane/history/avatar.py +241 -0
  56. gitpane/history/file_history.py +359 -0
  57. gitpane/history/graph.py +145 -0
  58. gitpane/history/history_filter.py +31 -0
  59. gitpane/history/history_parse.py +605 -0
  60. gitpane/history/repository.py +1600 -0
  61. gitpane/history/repository_value.py +341 -0
  62. gitpane/history/revision.py +180 -0
  63. gitpane/history/revision_file.py +182 -0
  64. gitpane/history_rewrite/__init__.py +0 -0
  65. gitpane/history_rewrite/graph.py +210 -0
  66. gitpane/history_rewrite/guard.py +288 -0
  67. gitpane/history_rewrite/index.py +125 -0
  68. gitpane/history_rewrite/prepare.py +378 -0
  69. gitpane/history_rewrite/protocol.py +320 -0
  70. gitpane/history_rewrite/publish.py +444 -0
  71. gitpane/history_rewrite/recovery.py +481 -0
  72. gitpane/history_rewrite/value.py +229 -0
  73. gitpane/operation/__init__.py +0 -0
  74. gitpane/operation/console_line.py +313 -0
  75. gitpane/operation/console_tone.py +76 -0
  76. gitpane/operation/diagnostic.py +954 -0
  77. gitpane/operation/launch_signature.py +254 -0
  78. gitpane/operation/operation_status.py +87 -0
  79. gitpane/operation/operation_trace.py +766 -0
  80. gitpane/operation/repository_operation.py +781 -0
  81. gitpane/preference/__init__.py +0 -0
  82. gitpane/preference/preference_location.py +40 -0
  83. gitpane/preference/preference_store.py +590 -0
  84. gitpane/process/__init__.py +0 -0
  85. gitpane/process/exact_arguments.py +77 -0
  86. gitpane/process/git_output_text.py +20 -0
  87. gitpane/process/process_group.py +851 -0
  88. gitpane/process/process_log.py +114 -0
  89. gitpane/process/process_runner.py +1033 -0
  90. gitpane/process/released_child_watch.py +138 -0
  91. gitpane/recent/__init__.py +0 -0
  92. gitpane/recent/mount_table.py +422 -0
  93. gitpane/recent/recent_availability.py +414 -0
  94. gitpane/recent/recent_name.py +11 -0
  95. gitpane/recent/recent_search.py +114 -0
  96. gitpane/reference/__init__.py +0 -0
  97. gitpane/reference/reference_edit.py +2030 -0
  98. gitpane/reference/reference_snapshot.py +196 -0
  99. gitpane/reference/stash.py +730 -0
  100. gitpane/reference/stash_list.py +164 -0
  101. gitpane/sequencer/__init__.py +0 -0
  102. gitpane/sequencer/commit_message_rule.py +154 -0
  103. gitpane/sequencer/conflict.py +506 -0
  104. gitpane/sequencer/merge_recovery.py +163 -0
  105. gitpane/sequencer/merge_state.py +166 -0
  106. gitpane/sequencer/mergetool.py +856 -0
  107. gitpane/sequencer/rebase.py +750 -0
  108. gitpane/sequencer/rebase_edit_file.py +224 -0
  109. gitpane/sequencer/rebase_state.py +523 -0
  110. gitpane/sequencer/replay.py +1435 -0
  111. gitpane/sequencer/replay_legacy.py +253 -0
  112. gitpane/sequencer/replay_state.py +747 -0
  113. gitpane/submodule/__init__.py +0 -0
  114. gitpane/submodule/checkout_refusal.py +102 -0
  115. gitpane/submodule/checkout_refusal_notice.py +111 -0
  116. gitpane/submodule/cloned_without_checkout.py +65 -0
  117. gitpane/submodule/submodule_discovery.py +1180 -0
  118. gitpane/submodule/submodule_loss.py +218 -0
  119. gitpane/system/__init__.py +0 -0
  120. gitpane/system/appearance_portal.py +125 -0
  121. gitpane/system/application_identity.py +48 -0
  122. gitpane/system/command_line.py +36 -0
  123. gitpane/system/desktop_icon_fact.py +128 -0
  124. gitpane/system/desktop_icon_support.py +270 -0
  125. gitpane/system/desktop_shortcut.py +536 -0
  126. gitpane/system/icns_image.py +35 -0
  127. gitpane/system/installed_launcher.py +136 -0
  128. gitpane/system/linux_distribution.py +151 -0
  129. gitpane/system/network_folder.py +46 -0
  130. gitpane/system/path_identity.py +95 -0
  131. gitpane/system/private_tree_removal.py +47 -0
  132. gitpane/system/process_program.py +63 -0
  133. gitpane/system/qt_folder.py +38 -0
  134. gitpane/system/ready_channel_watch.py +67 -0
  135. gitpane/system/startup_activation.py +65 -0
  136. gitpane/system/terminal_detach.py +384 -0
  137. gitpane/system/xdg_session.py +85 -0
  138. gitpane/text/__init__.py +0 -0
  139. gitpane/text/text_query.py +97 -0
  140. gitpane/text/typed_path.py +75 -0
  141. gitpane/text/typed_text.py +17 -0
  142. gitpane/thread/__init__.py +0 -0
  143. gitpane/thread/weak_signal.py +93 -0
  144. gitpane/tool/__init__.py +0 -0
  145. gitpane/tool/app_bundle.py +164 -0
  146. gitpane/tool/desktop_text_editor.py +156 -0
  147. gitpane/tool/difftool.py +429 -0
  148. gitpane/tool/editor.py +445 -0
  149. gitpane/tool/editor_bridge.py +193 -0
  150. gitpane/tool/editor_recipe.py +481 -0
  151. gitpane/tool/file_reveal.py +94 -0
  152. gitpane/tool/git_executable.py +264 -0
  153. gitpane/tool/git_version.py +62 -0
  154. gitpane/tool/kdiff3.py +41 -0
  155. gitpane/tool/kdiff3_install.py +162 -0
  156. gitpane/tool/launch_services_text_editor.py +104 -0
  157. gitpane/tool/mergetool_recipe.py +84 -0
  158. gitpane/tool/system_editor_default.py +80 -0
  159. gitpane/tool/system_text_editor.py +44 -0
  160. gitpane/tool/system_tool.py +816 -0
  161. gitpane/tool/terminal_recipe.py +390 -0
  162. gitpane/transport/__init__.py +0 -0
  163. gitpane/transport/branch_exchange.py +1690 -0
  164. gitpane/transport/clone.py +1273 -0
  165. gitpane/transport/credential_read_retry.py +35 -0
  166. gitpane/transport/git_refusal.py +207 -0
  167. gitpane/transport/push_tracking.py +121 -0
  168. gitpane/transport/refusal_notice.py +225 -0
  169. gitpane/transport/remote.py +747 -0
  170. gitpane/transport/remote_config.py +544 -0
  171. gitpane/ui/__init__.py +0 -0
  172. gitpane/ui/console/__init__.py +0 -0
  173. gitpane/ui/console/error_dialog.py +332 -0
  174. gitpane/ui/console/operation_dialog.py +408 -0
  175. gitpane/ui/console/operation_output.py +91 -0
  176. gitpane/ui/console/operation_output_view.py +373 -0
  177. gitpane/ui/emoji/__init__.py +0 -0
  178. gitpane/ui/emoji/emoji_font.py +257 -0
  179. gitpane/ui/emoji/emoji_grid.py +125 -0
  180. gitpane/ui/emoji/emoji_window.py +154 -0
  181. gitpane/ui/emoji/file_action_icon.py +131 -0
  182. gitpane/ui/emoji/opentype_family_name.py +182 -0
  183. gitpane/ui/history_rewrite/__init__.py +0 -0
  184. gitpane/ui/history_rewrite/details.py +78 -0
  185. gitpane/ui/history_rewrite/preview.py +565 -0
  186. gitpane/ui/history_rewrite/preview_model.py +291 -0
  187. gitpane/ui/history_rewrite/question.py +87 -0
  188. gitpane/ui/history_rewrite/recovery_dialog.py +262 -0
  189. gitpane/ui/look/__init__.py +0 -0
  190. gitpane/ui/look/action_icon.py +504 -0
  191. gitpane/ui/look/application_icon.py +78 -0
  192. gitpane/ui/look/application_style.py +317 -0
  193. gitpane/ui/look/avatar_label.py +53 -0
  194. gitpane/ui/look/color_contrast.py +43 -0
  195. gitpane/ui/look/desktop_appearance_follower.py +118 -0
  196. gitpane/ui/look/fixed_font.py +21 -0
  197. gitpane/ui/look/gtk_settings.py +116 -0
  198. gitpane/ui/look/icon_density.py +16 -0
  199. gitpane/ui/look/reference_font.py +19 -0
  200. gitpane/ui/look/theme_color.py +63 -0
  201. gitpane/ui/look/theme_look.py +95 -0
  202. gitpane/ui/look/theme_palette.py +173 -0
  203. gitpane/ui/look/warning_icon.py +86 -0
  204. gitpane/ui/look/windows_control_look.py +250 -0
  205. gitpane/ui/reader/__init__.py +0 -0
  206. gitpane/ui/reader/commit_message.py +129 -0
  207. gitpane/ui/reader/commit_panel.py +378 -0
  208. gitpane/ui/reader/diff_panel.py +626 -0
  209. gitpane/ui/reader/file_action.py +138 -0
  210. gitpane/ui/reader/file_tree_panel.py +235 -0
  211. gitpane/ui/reader/reader_toolbar.py +56 -0
  212. gitpane/ui/reader/revision_tree.py +80 -0
  213. gitpane/ui/reader/search_result.py +165 -0
  214. gitpane/ui/reader/source_text.py +134 -0
  215. gitpane/ui/reader/source_view.py +151 -0
  216. gitpane/ui/reader/syntax_span.py +67 -0
  217. gitpane/ui/reference/__init__.py +0 -0
  218. gitpane/ui/reference/branch_selector.py +106 -0
  219. gitpane/ui/reference/checkout_dialog.py +419 -0
  220. gitpane/ui/reference/checkout_revision_dialog.py +182 -0
  221. gitpane/ui/reference/commit_summary.py +339 -0
  222. gitpane/ui/reference/create_branch_dialog.py +346 -0
  223. gitpane/ui/reference/create_tag_dialog.py +283 -0
  224. gitpane/ui/reference/delete_local_branch_dialog.py +323 -0
  225. gitpane/ui/reference/delete_tag_dialog.py +394 -0
  226. gitpane/ui/reference/form_opening.py +53 -0
  227. gitpane/ui/reference/reference_dialog.py +417 -0
  228. gitpane/ui/reference/reference_feedback.py +62 -0
  229. gitpane/ui/reference/reference_form.py +139 -0
  230. gitpane/ui/reference/rename_branch_dialog.py +179 -0
  231. gitpane/ui/reference/reset_branch_dialog.py +430 -0
  232. gitpane/ui/reference/reset_dialog.py +263 -0
  233. gitpane/ui/reference/revision_dialog.py +121 -0
  234. gitpane/ui/reference/revision_picker.py +227 -0
  235. gitpane/ui/reference/select_branches_dialog.py +199 -0
  236. gitpane/ui/reference/stash_dialog.py +171 -0
  237. gitpane/ui/repository/__init__.py +0 -0
  238. gitpane/ui/repository/graph_delegate.py +298 -0
  239. gitpane/ui/repository/history_avatar.py +130 -0
  240. gitpane/ui/repository/history_model.py +179 -0
  241. gitpane/ui/repository/repository_list_popup.py +319 -0
  242. gitpane/ui/repository/repository_window.py +2580 -0
  243. gitpane/ui/resource/ArrowDown.png +0 -0
  244. gitpane/ui/resource/ArrowUp.png +0 -0
  245. gitpane/ui/resource/BranchCheckout.png +0 -0
  246. gitpane/ui/resource/BranchCreate.png +0 -0
  247. gitpane/ui/resource/BranchDelete.png +0 -0
  248. gitpane/ui/resource/CopyToClipboard.png +0 -0
  249. gitpane/ui/resource/GitPane-16.png +0 -0
  250. gitpane/ui/resource/GitPane-24.png +0 -0
  251. gitpane/ui/resource/GitPane-256.png +0 -0
  252. gitpane/ui/resource/GitPane-32.png +0 -0
  253. gitpane/ui/resource/GitPane-48.png +0 -0
  254. gitpane/ui/resource/GitPane-64.png +0 -0
  255. gitpane/ui/resource/GitPane.ico +0 -0
  256. gitpane/ui/resource/NotoColorEmoji-OFL.txt +92 -0
  257. gitpane/ui/resource/NotoColorEmoji_WindowsCompatible.ttf +0 -0
  258. gitpane/ui/resource/Renamed.png +0 -0
  259. gitpane/ui/resource/ResetAnotherBranchToHere.png +0 -0
  260. gitpane/ui/resource/ResetCurrentBranchToHere.png +0 -0
  261. gitpane/ui/resource/Stash.png +0 -0
  262. gitpane/ui/resource/Tag.png +0 -0
  263. gitpane/ui/resource/TagCreate.png +0 -0
  264. gitpane/ui/resource/TagDelete.png +0 -0
  265. gitpane/ui/resource/Unmerged.png +0 -0
  266. gitpane/ui/resource/checkout.png +0 -0
  267. gitpane/ui/sequencer/__init__.py +0 -0
  268. gitpane/ui/sequencer/conflict_dialog.py +532 -0
  269. gitpane/ui/sequencer/merge_recovery_dialog.py +67 -0
  270. gitpane/ui/sequencer/rebase_dialog.py +461 -0
  271. gitpane/ui/sequencer/rebase_editor_window.py +220 -0
  272. gitpane/ui/sequencer/replay_conflict_prompt.py +105 -0
  273. gitpane/ui/sequencer/replay_dialog.py +501 -0
  274. gitpane/ui/setting/__init__.py +0 -0
  275. gitpane/ui/setting/account_page.py +724 -0
  276. gitpane/ui/setting/add_account_dialog.py +1005 -0
  277. gitpane/ui/setting/credential_manager_dialog.py +151 -0
  278. gitpane/ui/setting/git_setting_widget.py +388 -0
  279. gitpane/ui/setting/setting_block.py +19 -0
  280. gitpane/ui/setting/setting_dialog.py +750 -0
  281. gitpane/ui/setting/ssh_widget.py +232 -0
  282. gitpane/ui/setting/utility_page.py +240 -0
  283. gitpane/ui/staging/__init__.py +0 -0
  284. gitpane/ui/staging/staging_action.py +356 -0
  285. gitpane/ui/staging/staging_window.py +745 -0
  286. gitpane/ui/transport/__init__.py +0 -0
  287. gitpane/ui/transport/clone_dialog.py +879 -0
  288. gitpane/ui/transport/push_dialog.py +580 -0
  289. gitpane/ui/transport/push_rejection_prompt.py +100 -0
  290. gitpane/ui/transport/push_tracking_question.py +88 -0
  291. gitpane/ui/transport/remote_dialog.py +943 -0
  292. gitpane/ui/welcome/__init__.py +0 -0
  293. gitpane/ui/welcome/invalid_repository_prompt.py +114 -0
  294. gitpane/ui/welcome/welcome_window.py +1137 -0
  295. gitpane/ui/widget/__init__.py +0 -0
  296. gitpane/ui/widget/busy_indicator.py +106 -0
  297. gitpane/ui/widget/commit_date_text.py +55 -0
  298. gitpane/ui/widget/content_growth.py +262 -0
  299. gitpane/ui/widget/content_measure.py +99 -0
  300. gitpane/ui/widget/dialog_layout.py +668 -0
  301. gitpane/ui/widget/drop_down_wheel_filter.py +32 -0
  302. gitpane/ui/widget/flow_layout.py +110 -0
  303. gitpane/ui/widget/long_text.py +542 -0
  304. gitpane/ui/widget/mnemonic.py +13 -0
  305. gitpane/ui/widget/numbered_text_edit.py +127 -0
  306. gitpane/ui/widget/plain_label.py +15 -0
  307. gitpane/ui/widget/plain_tooltip.py +19 -0
  308. gitpane/ui/widget/refresh_advice.py +25 -0
  309. gitpane/ui/widget/result_label.py +227 -0
  310. gitpane/ui/widget/result_status_bar.py +53 -0
  311. gitpane/ui/widget/scrolled_text.py +69 -0
  312. gitpane/ui/widget/shortcut_text.py +31 -0
  313. gitpane/ui/widget/standard_icon_label.py +55 -0
  314. gitpane/ui/widget/system_shortcut.py +75 -0
  315. gitpane/ui/widget/unavailable_reason.py +29 -0
  316. gitpane/ui/widget/wheel_safe_drop_down.py +24 -0
  317. gitpane/ui/window_control/__init__.py +0 -0
  318. gitpane/ui/window_control/button_focus.py +42 -0
  319. gitpane/ui/window_control/compositor_answer.py +152 -0
  320. gitpane/ui/window_control/compositor_growth_cap.py +73 -0
  321. gitpane/ui/window_control/desktop_activation.py +203 -0
  322. gitpane/ui/window_control/dialog_modality.py +122 -0
  323. gitpane/ui/window_control/item_activation.py +42 -0
  324. gitpane/ui/window_control/modal_focus_relay.py +124 -0
  325. gitpane/ui/window_control/wayland_activation.py +695 -0
  326. gitpane/ui/window_control/window_placement.py +305 -0
  327. gitpane/vendor/DISTLIB-LICENSE.txt +284 -0
  328. gitpane/vendor/w32.exe +0 -0
  329. gitpane/vendor/w64-arm.exe +0 -0
  330. gitpane/vendor/w64.exe +0 -0
  331. gitpane/worktree/__init__.py +0 -0
  332. gitpane/worktree/command_sequence.py +110 -0
  333. gitpane/worktree/file_action_target.py +61 -0
  334. gitpane/worktree/file_menu_action.py +52 -0
  335. gitpane/worktree/historical_restore.py +361 -0
  336. gitpane/worktree/ignore_rule.py +266 -0
  337. gitpane/worktree/mutation.py +1539 -0
  338. gitpane/worktree/partial_patch.py +435 -0
  339. gitpane/worktree/staged_reset.py +447 -0
  340. gitpane/worktree/staging.py +1418 -0
  341. gitpane/worktree/trash.py +302 -0
  342. gitpane/worktree/worktree_file.py +406 -0
  343. gitpane/worktree/worktree_service.py +584 -0
  344. gitpane/worktree/worktree_snapshot.py +630 -0
  345. gitpane-1.0.0.dist-info/METADATA +141 -0
  346. gitpane-1.0.0.dist-info/RECORD +353 -0
  347. gitpane-1.0.0.dist-info/WHEEL +5 -0
  348. gitpane-1.0.0.dist-info/entry_points.txt +2 -0
  349. gitpane-1.0.0.dist-info/licenses/LICENSE +21 -0
  350. gitpane-1.0.0.dist-info/licenses/THIRD_PARTY_NOTICES.md +109 -0
  351. gitpane-1.0.0.dist-info/licenses/src/gitpane/ui/resource/NotoColorEmoji-OFL.txt +92 -0
  352. gitpane-1.0.0.dist-info/licenses/src/gitpane/vendor/DISTLIB-LICENSE.txt +284 -0
  353. gitpane-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,212 @@
1
+ """Tell whether a repository window may close or change repository now."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ from PySide6.QtCore import QObject, QTimer
8
+ from shiboken6 import isValid
9
+
10
+ from gitpane.config import REBASE_CLOSE_REREAD_ATTEMPTS, REBASE_REFRESH_INTERVAL_MS, SharedText
11
+ from gitpane.sequencer.replay_state import replay_may_be_present
12
+ from gitpane.worktree.worktree_snapshot import WorktreeKey
13
+
14
+ if TYPE_CHECKING:
15
+ from gitpane.controller.console_registry import ConsoleRegistry
16
+ from gitpane.controller.rebase_controller import RebaseController
17
+ from gitpane.controller.rebase_editor_controller import RebaseEditorController
18
+ from gitpane.controller.reference_form_controller import ReferenceFormController
19
+ from gitpane.controller.replay_controller import ReplayController
20
+ from gitpane.controller.session_registry import SessionRegistry
21
+ from gitpane.operation.repository_operation import RepositoryOperationService
22
+ from gitpane.sequencer.mergetool import MergetoolService
23
+ from gitpane.sequencer.rebase import RebaseService
24
+ from gitpane.sequencer.replay import ReplayService
25
+ from gitpane.tool.editor import EditorService
26
+ from gitpane.worktree.mutation import MutationService
27
+
28
+
29
+ class CloseReadiness(QObject):
30
+ """The wait of a repository window that closes or changes repository while Git still works in it: the decision, the explanation shown in its banner, the bounded rereads of the native state, the timer that tries again."""
31
+
32
+ def __init__(self, *, session_registry_controller: SessionRegistry, console_registry_controller: ConsoleRegistry, reference_form_controller: ReferenceFormController, replay_controller: ReplayController, rebase_controller: RebaseController,
33
+ rebase_editor_controller: RebaseEditorController, editor: EditorService, repository_operation: RepositoryOperationService, rebase: RebaseService, replay: ReplayService, mutation: MutationService, mergetool: MergetoolService,
34
+ parent: QObject | None = None) -> None:
35
+ """Receive the services and the lower controllers the decision reads, then set the interval of the timer; the wiring builds it after the conflict controller and connects the timer to the resumption of the pending closes.
36
+
37
+ The rebase editor controller brings its windows forward when a close waits for an edit Git asked for.
38
+ """
39
+ super().__init__(parent)
40
+ self.session_registry_controller = session_registry_controller
41
+ self.console_registry_controller = console_registry_controller
42
+ self.reference_form_controller = reference_form_controller
43
+ self.replay_controller = replay_controller
44
+ self.rebase_controller = rebase_controller
45
+ self.rebase_editor_controller = rebase_editor_controller
46
+ self.editor = editor
47
+ self.repository_operation = repository_operation
48
+ self.rebase = rebase
49
+ self.replay = replay
50
+ self.mutation = mutation
51
+ self.mergetool = mergetool
52
+ self.close_timer = QTimer(self)
53
+ self.close_timer.setInterval(REBASE_REFRESH_INTERVAL_MS)
54
+
55
+ def _close_busy(self, session_id: str | None = None) -> bool:
56
+ """Retain authority until reference forms, output, editing and reconciliation finish."""
57
+ session = self.session_registry_controller.session_by_id.get(session_id)
58
+ if session_id is not None and session is None:
59
+ return False
60
+ key = WorktreeKey.from_context(session.repository_context) if session is not None else None
61
+ if key is None:
62
+ return bool(self.editor.active_by_id or self.rebase_controller.rebase_intent_by_id or any(self._close_busy(identity) for identity in self.session_registry_controller.session_by_id))
63
+ for dialog_by_session in (self.replay_controller.replay_dialog_by_session, self.reference_form_controller.checkout_dialog_by_session,
64
+ self.reference_form_controller.checkout_revision_dialog_by_session, self.reference_form_controller.branch_dialog_by_session,
65
+ self.reference_form_controller.tag_dialog_by_session):
66
+ dialog = dialog_by_session.get(session_id)
67
+ if dialog is not None and isValid(dialog) and dialog.busy:
68
+ return True
69
+ window = self.session_registry_controller.window_by_session.get(session_id)
70
+ for output in self.console_registry_controller.reference_output_by_id.values():
71
+ if isValid(output) and output.running and output.parent() is window:
72
+ return True
73
+ for intent in self.rebase_controller.rebase_intent_by_id.values():
74
+ if intent.operation.key == key:
75
+ return True
76
+ for context in self.editor.context_by_id.values():
77
+ if context.worktree_key == key and not context.closed:
78
+ return True
79
+ if any(intent.operation.key == key for intent in self.replay_controller.replay_intent_by_id.values()):
80
+ return True
81
+ if replay_may_be_present(self.replay.state_by_key.get(key)):
82
+ if any(operation.key == key for operation in self.repository_operation.active_by_id.values()):
83
+ return True
84
+ if self.mergetool.owns_repository(session.repository_context.common_git_dir):
85
+ return True
86
+ state = self.rebase.state_by_key.get(key)
87
+ if state is None or state.identity is None:
88
+ return False
89
+ for operation in self.repository_operation.active_by_id.values():
90
+ if operation.key == key:
91
+ return True
92
+ return self.mergetool.owns_repository(session.repository_context.common_git_dir)
93
+
94
+ def close_ready(self, session_id: str | None = None) -> bool:
95
+ """Require a fresh reread only after a command left its native result uncertain, and give up after a bounded number of failed rereads.
96
+
97
+ A state that stays unreadable (todo saved in another encoding, metadata removed externally) would otherwise keep the window,
98
+ Quit and navigation blocked forever while nothing runs any more; Git keeps its native files on disk, so closing cannot lose work.
99
+ """
100
+ if self._close_busy(session_id):
101
+ return False
102
+ ready = True
103
+ for identity, session in tuple(self.session_registry_controller.session_by_id.items()):
104
+ if session_id is not None and identity != session_id:
105
+ continue
106
+ key = WorktreeKey.from_context(session.repository_context)
107
+ state = self.rebase.state_by_key.get(key)
108
+ attempts = self.session_registry_controller.close_reread_count_by_key.get(key)
109
+ if state is None or state.fresh or attempts is None:
110
+ self.session_registry_controller.close_reread_count_by_key.pop(key, None)
111
+ continue
112
+ if key in self.rebase.observers_by_key:
113
+ # A reread is still running; its published result decides whether another attempt is needed.
114
+ ready = False
115
+ continue
116
+ if attempts >= REBASE_CLOSE_REREAD_ATTEMPTS:
117
+ self.session_registry_controller.close_reread_count_by_key.pop(key, None)
118
+ continue
119
+ self.session_registry_controller.close_reread_count_by_key[key] = attempts + 1
120
+ self.rebase_controller.observe_rebase(identity)
121
+ ready = False
122
+ return ready
123
+
124
+ def navigation_ready(self, session_id: str) -> bool:
125
+ """Tell whether the window may leave its repository now.
126
+
127
+ Besides what closing the window waits for (the native repository state), a change of repository also waits for
128
+ any reserved operation holding the working tree, Fetch and Push included, and for any staging write in it:
129
+ leaving the repository would otherwise stop them halfway. The change waits for them to finish instead.
130
+ """
131
+ session = self.session_registry_controller.session_by_id.get(session_id)
132
+ if session is None:
133
+ return False
134
+ if not self.close_ready(session_id):
135
+ return False
136
+ key = WorktreeKey.from_context(session.repository_context)
137
+ return not self.repository_operation.owns_worktree(key) and key not in self.mutation.active_key_set
138
+
139
+ def show_close_wait(self, session_id: str | None = None, *, navigation: bool = False) -> None:
140
+ """Keep a visible explanation while the original close request waits for native work.
141
+
142
+ While Git waits for an edit, the explanation sends the user to the GitPane editor window, which comes forward;
143
+ nothing answers Git in the user's place.
144
+ """
145
+ subject = "The repository will change" if navigation else "GitPane will close"
146
+ editor_waiting = bool(self.editor.active_by_id)
147
+ if editor_waiting:
148
+ message = f"{SharedText.REBASE_EDITOR_CLOSE_WAIT.value} {subject} after Git reaches a stable state."
149
+ else:
150
+ message = f"Waiting for Git and external tools to finish. {subject} after the repository state has been read."
151
+ editor_wait_started = False
152
+ for target_id, window in tuple(self.session_registry_controller.window_by_session.items()):
153
+ if session_id is not None and target_id != session_id:
154
+ continue
155
+ if not isValid(window) or window.is_closed:
156
+ continue
157
+ text = self._close_reread_message(target_id, message, subject)
158
+ if editor_waiting and window.rebase_label.text() != text:
159
+ editor_wait_started = True
160
+ window.rebase_label.setText(text)
161
+ window.rebase_banner.show()
162
+ window.show()
163
+ # The editor windows come forward when a banner starts asking for them, not at each retry of the close timer,
164
+ # so a long wait never keeps taking the keyboard from another window.
165
+ if editor_wait_started:
166
+ self.rebase_editor_controller.raise_windows()
167
+
168
+ def clear_navigation_wait(self, session_id: str) -> None:
169
+ """Put back the real rebase state in a window whose change of repository stopped without happening.
170
+
171
+ While a change of repository waits for Git, the rebase banner says the repository will change, with its Open
172
+ rebase button; only a published change hides it. A change that stops instead (folder gone, window opened
173
+ meanwhile, failed read) would otherwise keep announcing a change that will not come. A pending close keeps its
174
+ own message, and a running rebase command writes its progress there and publishes its state when it ends.
175
+ """
176
+ window = self.session_registry_controller.window_by_session.get(session_id)
177
+ session = self.session_registry_controller.session_by_id.get(session_id)
178
+ if window is None or not isValid(window) or window.is_closed or session is None or session.is_closed:
179
+ return
180
+ if self.session_registry_controller.quit_pending or session_id in self.session_registry_controller.close_session_set:
181
+ return
182
+ key = WorktreeKey.from_context(session.repository_context)
183
+ for intent in self.rebase_controller.rebase_intent_by_id.values():
184
+ if intent.operation.key == key:
185
+ return
186
+ state = self.rebase.state_by_key.get(key)
187
+ if state is None:
188
+ window.rebase_banner.hide()
189
+ return
190
+ window.show_rebase_state(state)
191
+
192
+ def _close_reread_message(self, session_id: str, message: str, subject: str) -> str:
193
+ """Name the reread cause and attempt count instead of a generic wait once closing depends on rereading the native state."""
194
+ session = self.session_registry_controller.session_by_id.get(session_id)
195
+ if session is None or self.editor.active_by_id:
196
+ return message
197
+ key = WorktreeKey.from_context(session.repository_context)
198
+ attempts = self.session_registry_controller.close_reread_count_by_key.get(key, 0)
199
+ state = self.rebase.state_by_key.get(key)
200
+ if not attempts or state is None or state.fresh:
201
+ return message
202
+ return (f"Rereading the native rebase state, attempt {attempts} of {REBASE_CLOSE_REREAD_ATTEMPTS}. {subject} once it has been read, or after the last attempt."
203
+ + (" " + state.diagnostic if state.diagnostic else ""))
204
+
205
+ def defer_repository_close(self, session_id: str) -> bool:
206
+ """Record a close before any window or session callback can be invalidated."""
207
+ if self.close_ready(session_id):
208
+ return False
209
+ self.session_registry_controller.close_session_set.add(session_id)
210
+ self.show_close_wait(session_id)
211
+ self.close_timer.start()
212
+ return True