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.
- gitpane/__init__.py +0 -0
- gitpane/account/__init__.py +0 -0
- gitpane/account/account_choice.py +297 -0
- gitpane/account/address_owner.py +67 -0
- gitpane/account/authentication.py +1811 -0
- gitpane/account/authentication_worker.py +836 -0
- gitpane/account/credential_manager.py +480 -0
- gitpane/account/credential_manager_install.py +171 -0
- gitpane/account/keyring_reader.py +135 -0
- gitpane/account/keyring_state.py +158 -0
- gitpane/account/ssh.py +312 -0
- gitpane/account/ssh_askpass.py +216 -0
- gitpane/account/ssh_prompt.py +222 -0
- gitpane/application.py +684 -0
- gitpane/commit/__init__.py +0 -0
- gitpane/commit/commit_metadata.py +88 -0
- gitpane/commit/commit_push.py +276 -0
- gitpane/commit/commit_write.py +830 -0
- gitpane/config.py +1066 -0
- gitpane/controller/__init__.py +0 -0
- gitpane/controller/account_controller.py +677 -0
- gitpane/controller/clone_controller.py +776 -0
- gitpane/controller/close_readiness.py +212 -0
- gitpane/controller/commit_timing_controller.py +566 -0
- gitpane/controller/conflict_controller.py +885 -0
- gitpane/controller/console_registry.py +38 -0
- gitpane/controller/emoji_window_controller.py +95 -0
- gitpane/controller/file_action_controller.py +479 -0
- gitpane/controller/history_read_controller.py +671 -0
- gitpane/controller/history_rewrite_controller.py +739 -0
- gitpane/controller/navigation_controller.py +658 -0
- gitpane/controller/opening_controller.py +375 -0
- gitpane/controller/operation_policy.py +76 -0
- gitpane/controller/rebase_controller.py +797 -0
- gitpane/controller/rebase_editor_controller.py +309 -0
- gitpane/controller/reference_form_controller.py +1677 -0
- gitpane/controller/reference_read_controller.py +242 -0
- gitpane/controller/refresh_controller.py +477 -0
- gitpane/controller/replay_controller.py +805 -0
- gitpane/controller/session_registry.py +337 -0
- gitpane/controller/setting_controller.py +592 -0
- gitpane/controller/shutdown_controller.py +345 -0
- gitpane/controller/staging_controller.py +199 -0
- gitpane/controller/staging_session_registry.py +47 -0
- gitpane/controller/stash_controller.py +367 -0
- gitpane/controller/transport_controller.py +813 -0
- gitpane/controller/welcome_controller.py +102 -0
- gitpane/controller/window_activation.py +116 -0
- gitpane/controller/worktree_state_controller.py +169 -0
- gitpane/emoji/__init__.py +0 -0
- gitpane/emoji/emoji_catalogue.py +156 -0
- gitpane/git_config/__init__.py +0 -0
- gitpane/git_config/git_setting.py +441 -0
- gitpane/history/__init__.py +0 -0
- gitpane/history/avatar.py +241 -0
- gitpane/history/file_history.py +359 -0
- gitpane/history/graph.py +145 -0
- gitpane/history/history_filter.py +31 -0
- gitpane/history/history_parse.py +605 -0
- gitpane/history/repository.py +1600 -0
- gitpane/history/repository_value.py +341 -0
- gitpane/history/revision.py +180 -0
- gitpane/history/revision_file.py +182 -0
- gitpane/history_rewrite/__init__.py +0 -0
- gitpane/history_rewrite/graph.py +210 -0
- gitpane/history_rewrite/guard.py +288 -0
- gitpane/history_rewrite/index.py +125 -0
- gitpane/history_rewrite/prepare.py +378 -0
- gitpane/history_rewrite/protocol.py +320 -0
- gitpane/history_rewrite/publish.py +444 -0
- gitpane/history_rewrite/recovery.py +481 -0
- gitpane/history_rewrite/value.py +229 -0
- gitpane/operation/__init__.py +0 -0
- gitpane/operation/console_line.py +313 -0
- gitpane/operation/console_tone.py +76 -0
- gitpane/operation/diagnostic.py +954 -0
- gitpane/operation/launch_signature.py +254 -0
- gitpane/operation/operation_status.py +87 -0
- gitpane/operation/operation_trace.py +766 -0
- gitpane/operation/repository_operation.py +781 -0
- gitpane/preference/__init__.py +0 -0
- gitpane/preference/preference_location.py +40 -0
- gitpane/preference/preference_store.py +590 -0
- gitpane/process/__init__.py +0 -0
- gitpane/process/exact_arguments.py +77 -0
- gitpane/process/git_output_text.py +20 -0
- gitpane/process/process_group.py +851 -0
- gitpane/process/process_log.py +114 -0
- gitpane/process/process_runner.py +1033 -0
- gitpane/process/released_child_watch.py +138 -0
- gitpane/recent/__init__.py +0 -0
- gitpane/recent/mount_table.py +422 -0
- gitpane/recent/recent_availability.py +414 -0
- gitpane/recent/recent_name.py +11 -0
- gitpane/recent/recent_search.py +114 -0
- gitpane/reference/__init__.py +0 -0
- gitpane/reference/reference_edit.py +2030 -0
- gitpane/reference/reference_snapshot.py +196 -0
- gitpane/reference/stash.py +730 -0
- gitpane/reference/stash_list.py +164 -0
- gitpane/sequencer/__init__.py +0 -0
- gitpane/sequencer/commit_message_rule.py +154 -0
- gitpane/sequencer/conflict.py +506 -0
- gitpane/sequencer/merge_recovery.py +163 -0
- gitpane/sequencer/merge_state.py +166 -0
- gitpane/sequencer/mergetool.py +856 -0
- gitpane/sequencer/rebase.py +750 -0
- gitpane/sequencer/rebase_edit_file.py +224 -0
- gitpane/sequencer/rebase_state.py +523 -0
- gitpane/sequencer/replay.py +1435 -0
- gitpane/sequencer/replay_legacy.py +253 -0
- gitpane/sequencer/replay_state.py +747 -0
- gitpane/submodule/__init__.py +0 -0
- gitpane/submodule/checkout_refusal.py +102 -0
- gitpane/submodule/checkout_refusal_notice.py +111 -0
- gitpane/submodule/cloned_without_checkout.py +65 -0
- gitpane/submodule/submodule_discovery.py +1180 -0
- gitpane/submodule/submodule_loss.py +218 -0
- gitpane/system/__init__.py +0 -0
- gitpane/system/appearance_portal.py +125 -0
- gitpane/system/application_identity.py +48 -0
- gitpane/system/command_line.py +36 -0
- gitpane/system/desktop_icon_fact.py +128 -0
- gitpane/system/desktop_icon_support.py +270 -0
- gitpane/system/desktop_shortcut.py +536 -0
- gitpane/system/icns_image.py +35 -0
- gitpane/system/installed_launcher.py +136 -0
- gitpane/system/linux_distribution.py +151 -0
- gitpane/system/network_folder.py +46 -0
- gitpane/system/path_identity.py +95 -0
- gitpane/system/private_tree_removal.py +47 -0
- gitpane/system/process_program.py +63 -0
- gitpane/system/qt_folder.py +38 -0
- gitpane/system/ready_channel_watch.py +67 -0
- gitpane/system/startup_activation.py +65 -0
- gitpane/system/terminal_detach.py +384 -0
- gitpane/system/xdg_session.py +85 -0
- gitpane/text/__init__.py +0 -0
- gitpane/text/text_query.py +97 -0
- gitpane/text/typed_path.py +75 -0
- gitpane/text/typed_text.py +17 -0
- gitpane/thread/__init__.py +0 -0
- gitpane/thread/weak_signal.py +93 -0
- gitpane/tool/__init__.py +0 -0
- gitpane/tool/app_bundle.py +164 -0
- gitpane/tool/desktop_text_editor.py +156 -0
- gitpane/tool/difftool.py +429 -0
- gitpane/tool/editor.py +445 -0
- gitpane/tool/editor_bridge.py +193 -0
- gitpane/tool/editor_recipe.py +481 -0
- gitpane/tool/file_reveal.py +94 -0
- gitpane/tool/git_executable.py +264 -0
- gitpane/tool/git_version.py +62 -0
- gitpane/tool/kdiff3.py +41 -0
- gitpane/tool/kdiff3_install.py +162 -0
- gitpane/tool/launch_services_text_editor.py +104 -0
- gitpane/tool/mergetool_recipe.py +84 -0
- gitpane/tool/system_editor_default.py +80 -0
- gitpane/tool/system_text_editor.py +44 -0
- gitpane/tool/system_tool.py +816 -0
- gitpane/tool/terminal_recipe.py +390 -0
- gitpane/transport/__init__.py +0 -0
- gitpane/transport/branch_exchange.py +1690 -0
- gitpane/transport/clone.py +1273 -0
- gitpane/transport/credential_read_retry.py +35 -0
- gitpane/transport/git_refusal.py +207 -0
- gitpane/transport/push_tracking.py +121 -0
- gitpane/transport/refusal_notice.py +225 -0
- gitpane/transport/remote.py +747 -0
- gitpane/transport/remote_config.py +544 -0
- gitpane/ui/__init__.py +0 -0
- gitpane/ui/console/__init__.py +0 -0
- gitpane/ui/console/error_dialog.py +332 -0
- gitpane/ui/console/operation_dialog.py +408 -0
- gitpane/ui/console/operation_output.py +91 -0
- gitpane/ui/console/operation_output_view.py +373 -0
- gitpane/ui/emoji/__init__.py +0 -0
- gitpane/ui/emoji/emoji_font.py +257 -0
- gitpane/ui/emoji/emoji_grid.py +125 -0
- gitpane/ui/emoji/emoji_window.py +154 -0
- gitpane/ui/emoji/file_action_icon.py +131 -0
- gitpane/ui/emoji/opentype_family_name.py +182 -0
- gitpane/ui/history_rewrite/__init__.py +0 -0
- gitpane/ui/history_rewrite/details.py +78 -0
- gitpane/ui/history_rewrite/preview.py +565 -0
- gitpane/ui/history_rewrite/preview_model.py +291 -0
- gitpane/ui/history_rewrite/question.py +87 -0
- gitpane/ui/history_rewrite/recovery_dialog.py +262 -0
- gitpane/ui/look/__init__.py +0 -0
- gitpane/ui/look/action_icon.py +504 -0
- gitpane/ui/look/application_icon.py +78 -0
- gitpane/ui/look/application_style.py +317 -0
- gitpane/ui/look/avatar_label.py +53 -0
- gitpane/ui/look/color_contrast.py +43 -0
- gitpane/ui/look/desktop_appearance_follower.py +118 -0
- gitpane/ui/look/fixed_font.py +21 -0
- gitpane/ui/look/gtk_settings.py +116 -0
- gitpane/ui/look/icon_density.py +16 -0
- gitpane/ui/look/reference_font.py +19 -0
- gitpane/ui/look/theme_color.py +63 -0
- gitpane/ui/look/theme_look.py +95 -0
- gitpane/ui/look/theme_palette.py +173 -0
- gitpane/ui/look/warning_icon.py +86 -0
- gitpane/ui/look/windows_control_look.py +250 -0
- gitpane/ui/reader/__init__.py +0 -0
- gitpane/ui/reader/commit_message.py +129 -0
- gitpane/ui/reader/commit_panel.py +378 -0
- gitpane/ui/reader/diff_panel.py +626 -0
- gitpane/ui/reader/file_action.py +138 -0
- gitpane/ui/reader/file_tree_panel.py +235 -0
- gitpane/ui/reader/reader_toolbar.py +56 -0
- gitpane/ui/reader/revision_tree.py +80 -0
- gitpane/ui/reader/search_result.py +165 -0
- gitpane/ui/reader/source_text.py +134 -0
- gitpane/ui/reader/source_view.py +151 -0
- gitpane/ui/reader/syntax_span.py +67 -0
- gitpane/ui/reference/__init__.py +0 -0
- gitpane/ui/reference/branch_selector.py +106 -0
- gitpane/ui/reference/checkout_dialog.py +419 -0
- gitpane/ui/reference/checkout_revision_dialog.py +182 -0
- gitpane/ui/reference/commit_summary.py +339 -0
- gitpane/ui/reference/create_branch_dialog.py +346 -0
- gitpane/ui/reference/create_tag_dialog.py +283 -0
- gitpane/ui/reference/delete_local_branch_dialog.py +323 -0
- gitpane/ui/reference/delete_tag_dialog.py +394 -0
- gitpane/ui/reference/form_opening.py +53 -0
- gitpane/ui/reference/reference_dialog.py +417 -0
- gitpane/ui/reference/reference_feedback.py +62 -0
- gitpane/ui/reference/reference_form.py +139 -0
- gitpane/ui/reference/rename_branch_dialog.py +179 -0
- gitpane/ui/reference/reset_branch_dialog.py +430 -0
- gitpane/ui/reference/reset_dialog.py +263 -0
- gitpane/ui/reference/revision_dialog.py +121 -0
- gitpane/ui/reference/revision_picker.py +227 -0
- gitpane/ui/reference/select_branches_dialog.py +199 -0
- gitpane/ui/reference/stash_dialog.py +171 -0
- gitpane/ui/repository/__init__.py +0 -0
- gitpane/ui/repository/graph_delegate.py +298 -0
- gitpane/ui/repository/history_avatar.py +130 -0
- gitpane/ui/repository/history_model.py +179 -0
- gitpane/ui/repository/repository_list_popup.py +319 -0
- gitpane/ui/repository/repository_window.py +2580 -0
- gitpane/ui/resource/ArrowDown.png +0 -0
- gitpane/ui/resource/ArrowUp.png +0 -0
- gitpane/ui/resource/BranchCheckout.png +0 -0
- gitpane/ui/resource/BranchCreate.png +0 -0
- gitpane/ui/resource/BranchDelete.png +0 -0
- gitpane/ui/resource/CopyToClipboard.png +0 -0
- gitpane/ui/resource/GitPane-16.png +0 -0
- gitpane/ui/resource/GitPane-24.png +0 -0
- gitpane/ui/resource/GitPane-256.png +0 -0
- gitpane/ui/resource/GitPane-32.png +0 -0
- gitpane/ui/resource/GitPane-48.png +0 -0
- gitpane/ui/resource/GitPane-64.png +0 -0
- gitpane/ui/resource/GitPane.ico +0 -0
- gitpane/ui/resource/NotoColorEmoji-OFL.txt +92 -0
- gitpane/ui/resource/NotoColorEmoji_WindowsCompatible.ttf +0 -0
- gitpane/ui/resource/Renamed.png +0 -0
- gitpane/ui/resource/ResetAnotherBranchToHere.png +0 -0
- gitpane/ui/resource/ResetCurrentBranchToHere.png +0 -0
- gitpane/ui/resource/Stash.png +0 -0
- gitpane/ui/resource/Tag.png +0 -0
- gitpane/ui/resource/TagCreate.png +0 -0
- gitpane/ui/resource/TagDelete.png +0 -0
- gitpane/ui/resource/Unmerged.png +0 -0
- gitpane/ui/resource/checkout.png +0 -0
- gitpane/ui/sequencer/__init__.py +0 -0
- gitpane/ui/sequencer/conflict_dialog.py +532 -0
- gitpane/ui/sequencer/merge_recovery_dialog.py +67 -0
- gitpane/ui/sequencer/rebase_dialog.py +461 -0
- gitpane/ui/sequencer/rebase_editor_window.py +220 -0
- gitpane/ui/sequencer/replay_conflict_prompt.py +105 -0
- gitpane/ui/sequencer/replay_dialog.py +501 -0
- gitpane/ui/setting/__init__.py +0 -0
- gitpane/ui/setting/account_page.py +724 -0
- gitpane/ui/setting/add_account_dialog.py +1005 -0
- gitpane/ui/setting/credential_manager_dialog.py +151 -0
- gitpane/ui/setting/git_setting_widget.py +388 -0
- gitpane/ui/setting/setting_block.py +19 -0
- gitpane/ui/setting/setting_dialog.py +750 -0
- gitpane/ui/setting/ssh_widget.py +232 -0
- gitpane/ui/setting/utility_page.py +240 -0
- gitpane/ui/staging/__init__.py +0 -0
- gitpane/ui/staging/staging_action.py +356 -0
- gitpane/ui/staging/staging_window.py +745 -0
- gitpane/ui/transport/__init__.py +0 -0
- gitpane/ui/transport/clone_dialog.py +879 -0
- gitpane/ui/transport/push_dialog.py +580 -0
- gitpane/ui/transport/push_rejection_prompt.py +100 -0
- gitpane/ui/transport/push_tracking_question.py +88 -0
- gitpane/ui/transport/remote_dialog.py +943 -0
- gitpane/ui/welcome/__init__.py +0 -0
- gitpane/ui/welcome/invalid_repository_prompt.py +114 -0
- gitpane/ui/welcome/welcome_window.py +1137 -0
- gitpane/ui/widget/__init__.py +0 -0
- gitpane/ui/widget/busy_indicator.py +106 -0
- gitpane/ui/widget/commit_date_text.py +55 -0
- gitpane/ui/widget/content_growth.py +262 -0
- gitpane/ui/widget/content_measure.py +99 -0
- gitpane/ui/widget/dialog_layout.py +668 -0
- gitpane/ui/widget/drop_down_wheel_filter.py +32 -0
- gitpane/ui/widget/flow_layout.py +110 -0
- gitpane/ui/widget/long_text.py +542 -0
- gitpane/ui/widget/mnemonic.py +13 -0
- gitpane/ui/widget/numbered_text_edit.py +127 -0
- gitpane/ui/widget/plain_label.py +15 -0
- gitpane/ui/widget/plain_tooltip.py +19 -0
- gitpane/ui/widget/refresh_advice.py +25 -0
- gitpane/ui/widget/result_label.py +227 -0
- gitpane/ui/widget/result_status_bar.py +53 -0
- gitpane/ui/widget/scrolled_text.py +69 -0
- gitpane/ui/widget/shortcut_text.py +31 -0
- gitpane/ui/widget/standard_icon_label.py +55 -0
- gitpane/ui/widget/system_shortcut.py +75 -0
- gitpane/ui/widget/unavailable_reason.py +29 -0
- gitpane/ui/widget/wheel_safe_drop_down.py +24 -0
- gitpane/ui/window_control/__init__.py +0 -0
- gitpane/ui/window_control/button_focus.py +42 -0
- gitpane/ui/window_control/compositor_answer.py +152 -0
- gitpane/ui/window_control/compositor_growth_cap.py +73 -0
- gitpane/ui/window_control/desktop_activation.py +203 -0
- gitpane/ui/window_control/dialog_modality.py +122 -0
- gitpane/ui/window_control/item_activation.py +42 -0
- gitpane/ui/window_control/modal_focus_relay.py +124 -0
- gitpane/ui/window_control/wayland_activation.py +695 -0
- gitpane/ui/window_control/window_placement.py +305 -0
- gitpane/vendor/DISTLIB-LICENSE.txt +284 -0
- gitpane/vendor/w32.exe +0 -0
- gitpane/vendor/w64-arm.exe +0 -0
- gitpane/vendor/w64.exe +0 -0
- gitpane/worktree/__init__.py +0 -0
- gitpane/worktree/command_sequence.py +110 -0
- gitpane/worktree/file_action_target.py +61 -0
- gitpane/worktree/file_menu_action.py +52 -0
- gitpane/worktree/historical_restore.py +361 -0
- gitpane/worktree/ignore_rule.py +266 -0
- gitpane/worktree/mutation.py +1539 -0
- gitpane/worktree/partial_patch.py +435 -0
- gitpane/worktree/staged_reset.py +447 -0
- gitpane/worktree/staging.py +1418 -0
- gitpane/worktree/trash.py +302 -0
- gitpane/worktree/worktree_file.py +406 -0
- gitpane/worktree/worktree_service.py +584 -0
- gitpane/worktree/worktree_snapshot.py +630 -0
- gitpane-1.0.0.dist-info/METADATA +141 -0
- gitpane-1.0.0.dist-info/RECORD +353 -0
- gitpane-1.0.0.dist-info/WHEEL +5 -0
- gitpane-1.0.0.dist-info/entry_points.txt +2 -0
- gitpane-1.0.0.dist-info/licenses/LICENSE +21 -0
- gitpane-1.0.0.dist-info/licenses/THIRD_PARTY_NOTICES.md +109 -0
- gitpane-1.0.0.dist-info/licenses/src/gitpane/ui/resource/NotoColorEmoji-OFL.txt +92 -0
- gitpane-1.0.0.dist-info/licenses/src/gitpane/vendor/DISTLIB-LICENSE.txt +284 -0
- gitpane-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
"""Manage the stashes of a repository window."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import uuid
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
from PySide6.QtCore import QObject, Qt, Signal
|
|
10
|
+
from PySide6.QtWidgets import QMessageBox
|
|
11
|
+
from shiboken6 import isValid
|
|
12
|
+
|
|
13
|
+
from gitpane.config import ConsoleOperation
|
|
14
|
+
from gitpane.controller.refresh_controller import RefreshScope
|
|
15
|
+
from gitpane.history.file_history import WindowCapability
|
|
16
|
+
from gitpane.operation.console_line import END_TEXT_TEMPLATE, REPOSITORY_HEADER_LABEL, STASH_POP_LABEL, ConsoleHeader, operation_end_text
|
|
17
|
+
from gitpane.operation.diagnostic import Diagnostic
|
|
18
|
+
from gitpane.operation.operation_status import OperationStatus
|
|
19
|
+
from gitpane.operation.operation_trace import OperationTrace, publish_after_trace
|
|
20
|
+
from gitpane.operation.repository_operation import OperationAction, OperationIntent
|
|
21
|
+
from gitpane.reference.stash import DROP_OPERATION_LABEL, StashDropIntent, StashObservation, pop_stopped_on_conflict
|
|
22
|
+
from gitpane.reference.stash_list import stash_selector
|
|
23
|
+
from gitpane.sequencer.conflict import snapshot_has_conflicts
|
|
24
|
+
from gitpane.system.path_identity import canonical_path
|
|
25
|
+
from gitpane.ui.console.operation_dialog import OperationDialog
|
|
26
|
+
from gitpane.ui.reference.stash_dialog import StashDialog
|
|
27
|
+
from gitpane.ui.window_control.dialog_modality import open_window_modal, prepare_message_box
|
|
28
|
+
from gitpane.worktree.worktree_snapshot import WorktreeKey
|
|
29
|
+
|
|
30
|
+
if TYPE_CHECKING:
|
|
31
|
+
from gitpane.controller.operation_policy import OperationPolicy
|
|
32
|
+
from gitpane.controller.refresh_controller import FullRefreshRead, RefreshController
|
|
33
|
+
from gitpane.controller.replay_controller import ReplayController
|
|
34
|
+
from gitpane.controller.session_registry import SessionRegistry
|
|
35
|
+
from gitpane.controller.staging_session_registry import StagingSessionRegistry
|
|
36
|
+
from gitpane.controller.window_activation import WindowActivationFilter
|
|
37
|
+
from gitpane.history.repository import GitRepository
|
|
38
|
+
from gitpane.operation.diagnostic import DiagnosticLog
|
|
39
|
+
from gitpane.operation.repository_operation import OperationOutcome
|
|
40
|
+
from gitpane.preference.preference_store import PreferenceStore
|
|
41
|
+
from gitpane.reference.stash import StashIntent, StashService
|
|
42
|
+
from gitpane.reference.stash_list import StashEntry
|
|
43
|
+
from gitpane.worktree.worktree_service import WorktreeService
|
|
44
|
+
|
|
45
|
+
# The question Drop stash asks, filled with the stash selector and the message the left panel shows.
|
|
46
|
+
STASH_DROP_QUESTION = "Drop {selector}: {message}?\n\nThe stash is deleted. Working files, the index and submodules are not changed."
|
|
47
|
+
STASH_DROP_CANCEL_LABEL = "Cancel"
|
|
48
|
+
STASH_DROPPED_STATUS = "Dropped {selector}."
|
|
49
|
+
STASH_DROP_FAILED_PREFIX = "Drop stash refused: "
|
|
50
|
+
STASH_DROP_VIEW_CHANGED_CAUSE = "The repository view changed; refresh it and try again"
|
|
51
|
+
# Where a stash drop came from, as the --log journal names it.
|
|
52
|
+
STASH_DROP_MANUAL_ORIGIN = "Drop stash from the left panel"
|
|
53
|
+
STASH_DROP_AUTOMATIC_ORIGIN = "Stash kept by a Stash pop, dropped after its conflicts were resolved in GitPane"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(frozen=True)
|
|
57
|
+
class StashDropAfterResolution:
|
|
58
|
+
"""Remember that a conflict of a Stash pop was just resolved in GitPane, so the stash that pop kept may be dropped.
|
|
59
|
+
|
|
60
|
+
Git keeps a stash whose pop stopped on conflicts. Once the last of those conflicts is resolved in GitPane's conflict
|
|
61
|
+
list or merge tool, the stash is dropped with nothing shown, the way users expect a pop to end. ``known_generation``
|
|
62
|
+
is the worktree read generation when the resolution finished: only a later read shows its result, and that read
|
|
63
|
+
decides. A read still listing a conflict ends the request; the next resolution in GitPane asks again.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
session_id: str
|
|
67
|
+
retained_oid: str
|
|
68
|
+
known_generation: int
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class StashController(QObject):
|
|
72
|
+
"""The stashes of every repository window: the Stash form, Stash pop with its console, Drop stash with its question, and the drop of a stash a Stash pop kept, once GitPane resolved its conflicts."""
|
|
73
|
+
|
|
74
|
+
# A Stash pop that ended with its stash kept tells the conflict, which watches that stash while the pop's conflicts
|
|
75
|
+
# remain: the worktree key and the observation of the kept stash. Connected directly, so the watch starts before
|
|
76
|
+
# the rest of the pop's result is handled, as when the stash called the conflict itself.
|
|
77
|
+
conflict_retention_requested = Signal(object, object)
|
|
78
|
+
|
|
79
|
+
def __init__(self, *, session_registry_controller: SessionRegistry, operation_policy_controller: OperationPolicy, staging_session_registry_controller: StagingSessionRegistry, window_activation_controller: WindowActivationFilter,
|
|
80
|
+
refresh_controller: RefreshController, replay_controller: ReplayController, preference: PreferenceStore, repository: GitRepository, worktree_service: WorktreeService,
|
|
81
|
+
stash: StashService, diagnostic_log: DiagnosticLog | None, parent: QObject | None = None) -> None:
|
|
82
|
+
"""Receive the services and the lower controllers a stash works with; the wiring builds it after the rebase controller, and the conflict hears from it through a signal the wiring connects."""
|
|
83
|
+
super().__init__(parent)
|
|
84
|
+
self.session_registry_controller = session_registry_controller
|
|
85
|
+
self.operation_policy_controller = operation_policy_controller
|
|
86
|
+
self.staging_session_registry_controller = staging_session_registry_controller
|
|
87
|
+
self.window_activation_controller = window_activation_controller
|
|
88
|
+
self.refresh_controller = refresh_controller
|
|
89
|
+
self.preference = preference
|
|
90
|
+
self.repository = repository
|
|
91
|
+
self.worktree_service = worktree_service
|
|
92
|
+
self.stash = stash
|
|
93
|
+
self.diagnostic_log = diagnostic_log
|
|
94
|
+
self.replay_controller = replay_controller
|
|
95
|
+
self.stash_dialog_by_session: dict[str, StashDialog] = {}
|
|
96
|
+
self.stash_intent_by_id: dict[str, StashIntent] = {}
|
|
97
|
+
self.stash_pop_intent_by_id: dict[str, OperationIntent] = {}
|
|
98
|
+
self.stash_pop_dialog_by_id: dict[str, OperationDialog] = {}
|
|
99
|
+
self.stash_pop_trace_by_id: dict[str, OperationTrace] = {}
|
|
100
|
+
self.stash_drop_by_id: dict[str, StashDropIntent] = {}
|
|
101
|
+
# The drops GitPane makes on its own, after the conflicts of a Stash pop were resolved: they show nothing.
|
|
102
|
+
self.stash_drop_automatic_id_set: set[str] = set()
|
|
103
|
+
self.stash_drop_confirmation_by_session: dict[str, QMessageBox] = {}
|
|
104
|
+
self.stash_drop_after_resolution_by_key: dict[WorktreeKey, StashDropAfterResolution] = {}
|
|
105
|
+
|
|
106
|
+
def open_stash(self, session_id: str) -> StashDialog | None:
|
|
107
|
+
"""Open or explicitly reload a retained stash draft under current management authority."""
|
|
108
|
+
if not self.operation_policy_controller.allows(session_id, WindowCapability.MANAGE):
|
|
109
|
+
return None
|
|
110
|
+
window = self.session_registry_controller.window_by_session[session_id]
|
|
111
|
+
if window.context.is_bare:
|
|
112
|
+
return None
|
|
113
|
+
existing = self.stash_dialog_by_session.get(session_id)
|
|
114
|
+
if existing is not None and isValid(existing) and not existing.closed:
|
|
115
|
+
existing.raise_()
|
|
116
|
+
if existing.running or existing.capture_pending:
|
|
117
|
+
return existing
|
|
118
|
+
dialog = existing
|
|
119
|
+
else:
|
|
120
|
+
dialog = StashDialog(session_id, self.preference, window)
|
|
121
|
+
dialog.installEventFilter(self.window_activation_controller)
|
|
122
|
+
self.stash_dialog_by_session[session_id] = dialog
|
|
123
|
+
dialog.submit_requested.connect(lambda intent: self.submit_stash(session_id, intent))
|
|
124
|
+
session = self.session_registry_controller.session_by_id[session_id]
|
|
125
|
+
generation = session.generation
|
|
126
|
+
dialog.begin_capture()
|
|
127
|
+
dialog.show()
|
|
128
|
+
|
|
129
|
+
def complete(snapshot: object, error: Exception | None) -> None:
|
|
130
|
+
"""Publish only to the still-authorized form that requested this read."""
|
|
131
|
+
if dialog.closed or not isValid(dialog):
|
|
132
|
+
return
|
|
133
|
+
if session.is_closed or session.generation != generation or not self.operation_policy_controller.allows(session_id, WindowCapability.MANAGE):
|
|
134
|
+
dialog.show_error("The repository changed. Click Stash in the toolbar to reload this draft.")
|
|
135
|
+
elif error is not None:
|
|
136
|
+
dialog.show_error(str(error))
|
|
137
|
+
else:
|
|
138
|
+
dialog.populate(snapshot)
|
|
139
|
+
|
|
140
|
+
self.worktree_service.read(window.context, self.repository.git_filename, generation, complete)
|
|
141
|
+
return dialog
|
|
142
|
+
|
|
143
|
+
def submit_stash(self, session_id: str, intent: StashIntent) -> str | None:
|
|
144
|
+
"""Submit only the original visible form's immutable intention."""
|
|
145
|
+
dialog = self.stash_dialog_by_session.get(session_id)
|
|
146
|
+
if (not self.operation_policy_controller.allows(session_id, WindowCapability.MANAGE) or dialog is None or dialog.closed
|
|
147
|
+
or dialog.intent != intent or intent.operation.session_id != session_id):
|
|
148
|
+
return None
|
|
149
|
+
|
|
150
|
+
def is_current() -> bool:
|
|
151
|
+
"""Recheck authority after asynchronous reads and any native queue wait."""
|
|
152
|
+
return (not dialog.closed and self.operation_policy_controller.operation_allowed(intent.operation)
|
|
153
|
+
and self.repository.git_filename == intent.operation.git_filename)
|
|
154
|
+
|
|
155
|
+
self.stash_intent_by_id[intent.operation.operation_id] = intent
|
|
156
|
+
return self.stash.create(intent, is_current=is_current)
|
|
157
|
+
|
|
158
|
+
def stash_finished(self, outcome: OperationOutcome) -> None:
|
|
159
|
+
"""Keep uncertain drafts while refreshing every view of the original worktree."""
|
|
160
|
+
intent = self.stash_intent_by_id.pop(outcome.operation_id, None)
|
|
161
|
+
if intent is None:
|
|
162
|
+
return
|
|
163
|
+
self.stash.outcome_by_id[outcome.operation_id] = outcome
|
|
164
|
+
dialog = self.stash_dialog_by_session.get(intent.operation.session_id)
|
|
165
|
+
if dialog is not None and isValid(dialog) and not dialog.closed and dialog.intent == intent:
|
|
166
|
+
dialog.show_result(outcome)
|
|
167
|
+
if outcome.diagnostic is not None and self.diagnostic_log is not None:
|
|
168
|
+
self.diagnostic_log.write(outcome.diagnostic)
|
|
169
|
+
shared_by_key: dict[WorktreeKey, FullRefreshRead] = {}
|
|
170
|
+
for session_id, session in tuple(self.session_registry_controller.session_by_id.items()):
|
|
171
|
+
if not session.is_closed and WorktreeKey.from_context(session.repository_context) == intent.operation.key:
|
|
172
|
+
self.refresh_controller.refresh(session_id, full_refresh_read=self.refresh_controller.full_refresh_read_for(session, shared_by_key))
|
|
173
|
+
refreshed_key_set = self.refresh_controller.observe_full_refresh_pauses(shared_by_key)
|
|
174
|
+
staging = self.staging_session_registry_controller.staging_by_key.get(intent.operation.key)
|
|
175
|
+
# A preparation session without any open repository window still shows the result.
|
|
176
|
+
if staging is not None and intent.operation.key not in refreshed_key_set:
|
|
177
|
+
staging.refresh()
|
|
178
|
+
|
|
179
|
+
def stash_pop(self, session_id: str) -> OperationDialog | None:
|
|
180
|
+
"""Apply the latest stash directly with the shared output preference and write owner."""
|
|
181
|
+
if not self.operation_policy_controller.allows(session_id, WindowCapability.MANAGE):
|
|
182
|
+
return None
|
|
183
|
+
session = self.session_registry_controller.session_by_id[session_id]
|
|
184
|
+
if session.repository_context.is_bare:
|
|
185
|
+
return None
|
|
186
|
+
operation = OperationIntent.capture(uuid.uuid4().hex, session.repository_context, self.repository.git_filename,
|
|
187
|
+
session_id=session_id, generation=self.session_registry_controller.generation_by_session[session_id], operation=STASH_POP_LABEL, action=OperationAction.STASH)
|
|
188
|
+
trace = OperationTrace(operation.operation_id, self.repository.runner, self.diagnostic_log, header=ConsoleHeader(REPOSITORY_HEADER_LABEL, operation.context.repository_dir))
|
|
189
|
+
dialog = OperationDialog(trace, STASH_POP_LABEL, self.session_registry_controller.window_by_session[session_id], preference=self.preference, console_operation=ConsoleOperation.STASH_POP)
|
|
190
|
+
self.stash_pop_intent_by_id[operation.operation_id] = operation
|
|
191
|
+
self.stash_pop_trace_by_id[operation.operation_id] = trace
|
|
192
|
+
self.stash_pop_dialog_by_id[operation.operation_id] = dialog
|
|
193
|
+
dialog.finished.connect(trace.close)
|
|
194
|
+
dialog.set_running()
|
|
195
|
+
open_window_modal(dialog, keeps_window_buttons=True)
|
|
196
|
+
self.stash.pop(operation, is_current=lambda: self.operation_policy_controller.operation_allowed(operation) and self.repository.git_filename == operation.git_filename)
|
|
197
|
+
return dialog
|
|
198
|
+
|
|
199
|
+
def stash_pop_finished(self, outcome: OperationOutcome) -> None:
|
|
200
|
+
"""Retain output independently and refresh only the original current repository session."""
|
|
201
|
+
operation = self.stash_pop_intent_by_id.pop(outcome.operation_id, None)
|
|
202
|
+
if operation is None:
|
|
203
|
+
return
|
|
204
|
+
self.stash.outcome_by_id[outcome.operation_id] = outcome
|
|
205
|
+
trace = self.stash_pop_trace_by_id[outcome.operation_id]
|
|
206
|
+
allowed = self.operation_policy_controller.operation_allowed(operation)
|
|
207
|
+
|
|
208
|
+
def publish_result() -> None:
|
|
209
|
+
"""Write the result in the console once the trace has ended the last process with its own line."""
|
|
210
|
+
if outcome.diagnostic is not None:
|
|
211
|
+
# The log keeps the cause alone here, as it always did; the console shows the diagnostic's own short line.
|
|
212
|
+
trace.append_record(outcome.operation_id, "status", outcome.diagnostic.cause,
|
|
213
|
+
trace.diagnostic_console_line(outcome.diagnostic, outcome.status, paused=pop_stopped_on_conflict(outcome)))
|
|
214
|
+
trace.append_end_line(outcome.operation_id, "status", END_TEXT_TEMPLATE.format(label=STASH_POP_LABEL, word=outcome.status.value),
|
|
215
|
+
operation_end_text(STASH_POP_LABEL, outcome.status), outcome.status, expected_stop=pop_stopped_on_conflict(outcome))
|
|
216
|
+
dialog = self.stash_pop_dialog_by_id.get(outcome.operation_id)
|
|
217
|
+
if dialog is not None and isValid(dialog):
|
|
218
|
+
# Conflicts are a pause: the conflict question and the repository window wait for the user, not the console.
|
|
219
|
+
dialog.show_result(outcome.status, expected_stop=pop_stopped_on_conflict(outcome))
|
|
220
|
+
if not allowed:
|
|
221
|
+
trace.close()
|
|
222
|
+
|
|
223
|
+
publish_after_trace(publish_result, self)
|
|
224
|
+
if not allowed:
|
|
225
|
+
return
|
|
226
|
+
for step in outcome.steps:
|
|
227
|
+
if isinstance(step.after, StashObservation) and step.after.retained_oid is not None:
|
|
228
|
+
self.conflict_retention_requested.emit(operation.key, step.after)
|
|
229
|
+
if step.after.unmerged_paths:
|
|
230
|
+
self.session_registry_controller.conflict_pending_session_set.add(operation.session_id)
|
|
231
|
+
shared_by_key: dict[WorktreeKey, FullRefreshRead] = {}
|
|
232
|
+
for session_id, session in tuple(self.session_registry_controller.session_by_id.items()):
|
|
233
|
+
if not session.is_closed and WorktreeKey.from_context(session.repository_context) == operation.key:
|
|
234
|
+
self.refresh_controller.checkout_refresh_session_set.add(session_id)
|
|
235
|
+
self.refresh_controller.refresh(session_id, full_refresh_read=self.refresh_controller.full_refresh_read_for(session, shared_by_key, explicit=True))
|
|
236
|
+
refreshed_key_set = self.refresh_controller.observe_full_refresh_pauses(shared_by_key)
|
|
237
|
+
staging = self.staging_session_registry_controller.staging_by_key.get(operation.key)
|
|
238
|
+
# A preparation session without any open repository window still shows the result.
|
|
239
|
+
if staging is not None and operation.key not in refreshed_key_set:
|
|
240
|
+
staging.refresh(explicit=True)
|
|
241
|
+
|
|
242
|
+
def drop_stash(self, session_id: str, entry: StashEntry) -> QMessageBox | None:
|
|
243
|
+
"""Ask before dropping the stash chosen in the left panel, and drop exactly that stash once the user agrees.
|
|
244
|
+
|
|
245
|
+
The question names the stash by its selector and message, with Cancel as the default and Escape answer. The
|
|
246
|
+
stash is checked again by its commit just before Git drops it, so a list changed meanwhile drops nothing.
|
|
247
|
+
"""
|
|
248
|
+
if not self.operation_policy_controller.allows(session_id, WindowCapability.MANAGE):
|
|
249
|
+
return None
|
|
250
|
+
window = self.session_registry_controller.window_by_session.get(session_id)
|
|
251
|
+
session = self.session_registry_controller.session_by_id[session_id]
|
|
252
|
+
if window is None or session.repository_context.is_bare:
|
|
253
|
+
return None
|
|
254
|
+
previous = self.stash_drop_confirmation_by_session.pop(session_id, None)
|
|
255
|
+
if previous is not None and isValid(previous):
|
|
256
|
+
previous.close()
|
|
257
|
+
previous.deleteLater()
|
|
258
|
+
operation = OperationIntent.capture(uuid.uuid4().hex, session.repository_context, self.repository.git_filename, session_id=session_id,
|
|
259
|
+
generation=self.session_registry_controller.generation_by_session[session_id], operation=DROP_OPERATION_LABEL, action=OperationAction.STASH)
|
|
260
|
+
intent = StashDropIntent.of_entry(operation, entry)
|
|
261
|
+
text = STASH_DROP_QUESTION.format(selector=stash_selector(entry.index), message=entry.message)
|
|
262
|
+
confirmation = QMessageBox(QMessageBox.Icon.Question, DROP_OPERATION_LABEL, text, QMessageBox.StandardButton.NoButton, window)
|
|
263
|
+
prepare_message_box(confirmation, DROP_OPERATION_LABEL)
|
|
264
|
+
confirmation.setTextFormat(Qt.TextFormat.PlainText)
|
|
265
|
+
drop_button = confirmation.addButton(DROP_OPERATION_LABEL, QMessageBox.ButtonRole.DestructiveRole)
|
|
266
|
+
cancel_button = confirmation.addButton(STASH_DROP_CANCEL_LABEL, QMessageBox.ButtonRole.RejectRole)
|
|
267
|
+
confirmation.setDefaultButton(cancel_button)
|
|
268
|
+
confirmation.setEscapeButton(cancel_button)
|
|
269
|
+
self.stash_drop_confirmation_by_session[session_id] = confirmation
|
|
270
|
+
|
|
271
|
+
def answer() -> None:
|
|
272
|
+
"""Drop only on an explicit Drop stash; any other ending leaves every stash in place."""
|
|
273
|
+
if self.stash_drop_confirmation_by_session.get(session_id) is confirmation:
|
|
274
|
+
del self.stash_drop_confirmation_by_session[session_id]
|
|
275
|
+
accepted = confirmation.clickedButton() is drop_button
|
|
276
|
+
confirmation.deleteLater()
|
|
277
|
+
if accepted:
|
|
278
|
+
self._submit_stash_drop(intent, automatic=False)
|
|
279
|
+
|
|
280
|
+
confirmation.finished.connect(answer)
|
|
281
|
+
open_window_modal(confirmation)
|
|
282
|
+
return confirmation
|
|
283
|
+
|
|
284
|
+
def _submit_stash_drop(self, intent: StashDropIntent, *, automatic: bool) -> None:
|
|
285
|
+
"""Hand one drop to the stash service while its window may still write, under the Git it was asked with."""
|
|
286
|
+
operation = intent.operation
|
|
287
|
+
if not self.operation_policy_controller.operation_allowed(operation) or self.repository.git_filename != operation.git_filename:
|
|
288
|
+
self._report_stash_drop(intent, automatic, OperationStatus.REFUSED, STASH_DROP_VIEW_CHANGED_CAUSE)
|
|
289
|
+
return
|
|
290
|
+
self.stash_drop_by_id[operation.operation_id] = intent
|
|
291
|
+
if automatic:
|
|
292
|
+
self.stash_drop_automatic_id_set.add(operation.operation_id)
|
|
293
|
+
self.stash.drop(intent, is_current=lambda: self.operation_policy_controller.operation_allowed(operation) and self.repository.git_filename == operation.git_filename)
|
|
294
|
+
|
|
295
|
+
def stash_drop_finished(self, outcome: OperationOutcome) -> None:
|
|
296
|
+
"""Report a finished drop and read the stash list again in every window of that repository, whatever the result."""
|
|
297
|
+
intent = self.stash_drop_by_id.pop(outcome.operation_id, None)
|
|
298
|
+
if intent is None:
|
|
299
|
+
return
|
|
300
|
+
automatic = outcome.operation_id in self.stash_drop_automatic_id_set
|
|
301
|
+
self.stash_drop_automatic_id_set.discard(outcome.operation_id)
|
|
302
|
+
cause = outcome.diagnostic.cause if outcome.diagnostic is not None else ""
|
|
303
|
+
self._report_stash_drop(intent, automatic, outcome.status, cause)
|
|
304
|
+
# The stash list lives in the shared Git directory: every window of the repository, linked worktrees included, shows it.
|
|
305
|
+
for session_id, session in tuple(self.session_registry_controller.session_by_id.items()):
|
|
306
|
+
if (not session.is_closed and WindowCapability.MANAGE in session.capabilities
|
|
307
|
+
and canonical_path(session.repository_context.common_git_dir) == intent.operation.common_git_dir):
|
|
308
|
+
self.refresh_controller.refresh(session_id, scope=RefreshScope.REFERENCES)
|
|
309
|
+
|
|
310
|
+
def _report_stash_drop(self, intent: StashDropIntent, automatic: bool, status: OperationStatus, cause: str) -> None:
|
|
311
|
+
"""Write every drop to the --log journal with the stash commit, and tell the user about the drops they asked for.
|
|
312
|
+
|
|
313
|
+
The dropped commit stays in the object database until Git prunes it, so with --log the journal line is what lets
|
|
314
|
+
it be recovered. A drop GitPane makes on its own after a Stash pop shows nothing at all, by design: without
|
|
315
|
+
--log no trace of that commit is kept, and only ``git fsck --unreachable`` finds it before the next prune. A
|
|
316
|
+
failed drop that carries no cause shows the word of its status instead.
|
|
317
|
+
"""
|
|
318
|
+
operation = intent.operation
|
|
319
|
+
if self.diagnostic_log is not None:
|
|
320
|
+
origin = STASH_DROP_AUTOMATIC_ORIGIN if automatic else STASH_DROP_MANUAL_ORIGIN
|
|
321
|
+
message = f"{origin}: {status.value}. Stash commit: {intent.oid}." + (" " + cause if cause else "")
|
|
322
|
+
self.diagnostic_log.write(Diagnostic(DROP_OPERATION_LABEL, operation.context.repository_dir, operation.git_filename, [], message).prepare())
|
|
323
|
+
if automatic or operation.session_id is None:
|
|
324
|
+
return
|
|
325
|
+
window = self.session_registry_controller.window_by_session.get(operation.session_id)
|
|
326
|
+
if window is None or not isValid(window):
|
|
327
|
+
return
|
|
328
|
+
if status == OperationStatus.SUCCESS:
|
|
329
|
+
window.statusBar().showMessage(STASH_DROPPED_STATUS.format(selector=stash_selector(intent.index)))
|
|
330
|
+
return
|
|
331
|
+
window.statusBar().showMessage(STASH_DROP_FAILED_PREFIX + (cause or status.value))
|
|
332
|
+
|
|
333
|
+
def continue_stash_drops_after_resolution(self) -> None:
|
|
334
|
+
"""Look again at every waiting drop, for instance once the merge tool or another write has released the repository."""
|
|
335
|
+
for key in tuple(self.stash_drop_after_resolution_by_key):
|
|
336
|
+
self.continue_stash_drop_after_resolution(key)
|
|
337
|
+
|
|
338
|
+
def continue_stash_drop_after_resolution(self, key: WorktreeKey) -> None:
|
|
339
|
+
"""Drop the kept stash once a worktree read newer than the resolution shows no conflict and nothing holds the repository.
|
|
340
|
+
|
|
341
|
+
A read still listing a conflict ends the request without dropping anything. A closed or departed window ends it
|
|
342
|
+
too. The drop itself checks again, under the repository reservation, that no conflict remains and that the
|
|
343
|
+
stash commit is still in the list, and shows nothing to the user whatever happens; only the --log journal keeps
|
|
344
|
+
the dropped commit.
|
|
345
|
+
"""
|
|
346
|
+
request = self.stash_drop_after_resolution_by_key.get(key)
|
|
347
|
+
if request is None:
|
|
348
|
+
return
|
|
349
|
+
session = self.session_registry_controller.session_by_id.get(request.session_id)
|
|
350
|
+
if (session is None or not self.operation_policy_controller.allows(request.session_id, WindowCapability.MANAGE) or WorktreeKey.from_context(session.repository_context) != key
|
|
351
|
+
or self.replay_controller.replay_session_leaving(request.session_id)):
|
|
352
|
+
del self.stash_drop_after_resolution_by_key[key]
|
|
353
|
+
return
|
|
354
|
+
staging = self.staging_session_registry_controller.staging_by_key.get(key)
|
|
355
|
+
snapshot = staging.snapshot if staging is not None else None
|
|
356
|
+
if (snapshot is None or not snapshot.fresh or staging.refresh_active or staging.refresh_queued or staging.explicit_refresh
|
|
357
|
+
or snapshot.generation <= request.known_generation):
|
|
358
|
+
return
|
|
359
|
+
if snapshot_has_conflicts(snapshot):
|
|
360
|
+
del self.stash_drop_after_resolution_by_key[key]
|
|
361
|
+
return
|
|
362
|
+
if self.replay_controller.replay_writer_busy(session.repository_context):
|
|
363
|
+
return
|
|
364
|
+
del self.stash_drop_after_resolution_by_key[key]
|
|
365
|
+
operation = OperationIntent.capture(uuid.uuid4().hex, session.repository_context, self.repository.git_filename, session_id=request.session_id,
|
|
366
|
+
generation=self.session_registry_controller.generation_by_session[request.session_id], operation=DROP_OPERATION_LABEL, action=OperationAction.STASH)
|
|
367
|
+
self._submit_stash_drop(StashDropIntent.of_retained(operation, request.retained_oid), automatic=True)
|