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,166 @@
|
|
|
1
|
+
"""Read the merge Git leaves waiting for its commit, from Git's own files alone, without starting a Git process.
|
|
2
|
+
|
|
3
|
+
GitPane never starts a merge, but the user often concludes one begun on the command line or by a pull: the Commit
|
|
4
|
+
that does it must expect the merged commits as extra parents, and can offer the message Git prepared.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
|
|
12
|
+
from gitpane.config import MERGE_HEAD_BASENAME, MERGE_MODE_BASENAME, MERGE_MODE_NO_FAST_FORWARD, MERGE_STATE_FILE_LIMIT_BYTES, READ_SIZE_SENTINEL_BYTES
|
|
13
|
+
from gitpane.history.repository_value import validate_oid
|
|
14
|
+
from gitpane.sequencer.commit_message_rule import DEFAULT_COMMENT_PREFIX
|
|
15
|
+
from gitpane.sequencer.rebase_state import REBASE_STATE_DIR_BASENAMES
|
|
16
|
+
from gitpane.sequencer.replay_state import (
|
|
17
|
+
BUSY_ACTION_REASON,
|
|
18
|
+
CHERRY_PICK_HEAD_BASENAME,
|
|
19
|
+
MERGE_MESSAGE_BASENAME,
|
|
20
|
+
RESOLVE_CONFLICTS_FIRST_REASON,
|
|
21
|
+
REVERT_HEAD_BASENAME,
|
|
22
|
+
SEQUENCER_BASENAME,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
MERGE_STATE_UNREADABLE_CAUSE = "The merge in progress could not be read. Refresh before committing."
|
|
26
|
+
MERGE_STATE_TOO_LARGE_CAUSE = "The merge in progress is described by an unexpectedly large file. Refresh before committing."
|
|
27
|
+
MERGE_HEAD_INVALID_CAUSE = "The merge in progress names something other than commits. Refresh before committing."
|
|
28
|
+
# Why Abort is greyed for a merge that could not be read, and why a click finds no merge read to abort.
|
|
29
|
+
MERGE_UNREADABLE_ABORT_REASON = "The merge in progress could not be read. Refresh before aborting it."
|
|
30
|
+
MERGE_ABORT_REFRESH_REASON = "Refresh before aborting the merge."
|
|
31
|
+
# The first line of the banner of a merge waiting for its commit, worded as Git Extensions words it.
|
|
32
|
+
MERGE_IN_PROGRESS_TEXT = "Merge is currently in progress"
|
|
33
|
+
# A rebase or a cherry-pick/revert can stop with a MERGE_HEAD of its own; that pause keeps its own banner and buttons.
|
|
34
|
+
OTHER_PAUSE_MARKER_BASENAMES = (*REBASE_STATE_DIR_BASENAMES, CHERRY_PICK_HEAD_BASENAME, REVERT_HEAD_BASENAME, SEQUENCER_BASENAME)
|
|
35
|
+
# The buttons of the merge banner, as merge_action_reason names them.
|
|
36
|
+
CONTINUE_ACTION = "continue"
|
|
37
|
+
ABORT_ACTION = "abort"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(frozen=True)
|
|
41
|
+
class MergeInProgress:
|
|
42
|
+
"""The commits a waiting merge adds after HEAD, in the order Git records them, and the raw message Git prepared.
|
|
43
|
+
|
|
44
|
+
``keeps_every_parent`` is true for a merge started with --no-ff. Otherwise the commit that concludes the merge drops
|
|
45
|
+
any parent that another of its parents already contains, as an octopus merge of a branch and its own ancestor does.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
parent_oids: tuple[str, ...]
|
|
49
|
+
message: str
|
|
50
|
+
keeps_every_parent: bool = False
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _read_merge_file(filename: str) -> bytes | None:
|
|
54
|
+
"""Read one small merge file whole, answering None when it does not exist.
|
|
55
|
+
|
|
56
|
+
Anything else that prevents a complete read, a folder in its place or a size no real merge reaches included,
|
|
57
|
+
is an error: a merge that cannot be read must never pass for no merge at all.
|
|
58
|
+
"""
|
|
59
|
+
try:
|
|
60
|
+
with open(filename, "rb") as stream:
|
|
61
|
+
content = stream.read(MERGE_STATE_FILE_LIMIT_BYTES + READ_SIZE_SENTINEL_BYTES)
|
|
62
|
+
except FileNotFoundError:
|
|
63
|
+
return None
|
|
64
|
+
except OSError as error:
|
|
65
|
+
raise ValueError(MERGE_STATE_UNREADABLE_CAUSE) from error
|
|
66
|
+
if len(content) > MERGE_STATE_FILE_LIMIT_BYTES:
|
|
67
|
+
raise ValueError(MERGE_STATE_TOO_LARGE_CAUSE)
|
|
68
|
+
return content
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def read_merge_in_progress(git_dir: str) -> MergeInProgress | None:
|
|
72
|
+
"""Answer the merge waiting for its commit in this worktree's Git directory, or None when there is none.
|
|
73
|
+
|
|
74
|
+
The commit IDs are validated one by one and kept in their order, whatever the line ends; the message is decoded
|
|
75
|
+
as UTF-8, any undecodable byte replaced, and is empty when Git left none. The merge mode is compared exactly as Git
|
|
76
|
+
compares it when it commits.
|
|
77
|
+
"""
|
|
78
|
+
merge_head = _read_merge_file(os.path.join(git_dir, MERGE_HEAD_BASENAME))
|
|
79
|
+
if merge_head is None:
|
|
80
|
+
return None
|
|
81
|
+
parent_oids = []
|
|
82
|
+
try:
|
|
83
|
+
for line in merge_head.decode("ascii").splitlines():
|
|
84
|
+
if line.strip():
|
|
85
|
+
parent_oids.append(validate_oid(line.strip()))
|
|
86
|
+
except (UnicodeDecodeError, ValueError) as error:
|
|
87
|
+
raise ValueError(MERGE_HEAD_INVALID_CAUSE) from error
|
|
88
|
+
if not parent_oids:
|
|
89
|
+
raise ValueError(MERGE_HEAD_INVALID_CAUSE)
|
|
90
|
+
message = _read_merge_file(os.path.join(git_dir, MERGE_MESSAGE_BASENAME))
|
|
91
|
+
mode = _read_merge_file(os.path.join(git_dir, MERGE_MODE_BASENAME))
|
|
92
|
+
return MergeInProgress(tuple(parent_oids), (message or b"").decode("utf-8", "replace"), mode == MERGE_MODE_NO_FAST_FORWARD)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def merge_message_text(raw: str) -> str:
|
|
96
|
+
"""Turn the message Git prepared into the text offered for the commit: comment lines, blank lines around it and trailing blanks removed.
|
|
97
|
+
|
|
98
|
+
Git writes the conflicted paths as comment lines; it removes them itself only when it opens an editor, and a
|
|
99
|
+
commit given its message directly would record them.
|
|
100
|
+
"""
|
|
101
|
+
# Git's default comment character. core.commentChar is not read: that would cost a Git process at every read, and a
|
|
102
|
+
# line kept by mistake stays visible and editable in the message before Commit.
|
|
103
|
+
kept_lines = []
|
|
104
|
+
for line in raw.splitlines():
|
|
105
|
+
if not line.startswith(DEFAULT_COMMENT_PREFIX):
|
|
106
|
+
kept_lines.append(line)
|
|
107
|
+
return "\n".join(kept_lines).lstrip("\n").rstrip()
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@dataclass(frozen=True)
|
|
111
|
+
class MergeBannerState:
|
|
112
|
+
"""What the repository window needs to show a merge waiting for its commit.
|
|
113
|
+
|
|
114
|
+
``merge`` is the merge read from Git's files, ``other_pause`` tells that a rebase or a cherry-pick/revert is paused
|
|
115
|
+
beside it, and ``cause`` explains a MERGE_HEAD that exists but could not be read.
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
merge: MergeInProgress | None = None
|
|
119
|
+
other_pause: bool = False
|
|
120
|
+
cause: str = ""
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def in_progress(self) -> bool:
|
|
124
|
+
"""Tell whether the window shows the merge banner: a MERGE_HEAD exists, readable or not, and no other operation is paused."""
|
|
125
|
+
return (self.merge is not None or bool(self.cause)) and not self.other_pause
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def read_merge_banner_state(git_dir: str) -> MergeBannerState:
|
|
129
|
+
"""Read the merge state the banner shows from Git's files in this worktree's Git directory, without a Git process.
|
|
130
|
+
|
|
131
|
+
A MERGE_HEAD that cannot be read is answered with its cause, never as an exception, so the banner can say why.
|
|
132
|
+
The markers of another pause are only looked for when a MERGE_HEAD exists.
|
|
133
|
+
"""
|
|
134
|
+
try:
|
|
135
|
+
merge = read_merge_in_progress(git_dir)
|
|
136
|
+
except ValueError as error:
|
|
137
|
+
merge, cause = None, str(error)
|
|
138
|
+
else:
|
|
139
|
+
cause = ""
|
|
140
|
+
if merge is None and not cause:
|
|
141
|
+
return MergeBannerState()
|
|
142
|
+
other_pause = False
|
|
143
|
+
for basename in OTHER_PAUSE_MARKER_BASENAMES:
|
|
144
|
+
if os.path.lexists(os.path.join(git_dir, basename)):
|
|
145
|
+
other_pause = True
|
|
146
|
+
break
|
|
147
|
+
return MergeBannerState(merge, other_pause, cause)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def merge_action_reason(state: MergeBannerState, action: str, *, conflict_count: int, busy: bool) -> str:
|
|
151
|
+
"""Tell why a button of the merge banner is greyed, or answer "" when it may act.
|
|
152
|
+
|
|
153
|
+
A merge tool or an operation at work comes first, then a merge that could not be read: Continue gives the cause the
|
|
154
|
+
Commit would give, and Abort its own reason, since an Abort only applies to the merged commits the banner showed and
|
|
155
|
+
an unreadable merge shows none. Continue also waits until no conflict is left, since the Commit it leads to cannot
|
|
156
|
+
conclude a merge with unresolved files.
|
|
157
|
+
"""
|
|
158
|
+
if busy:
|
|
159
|
+
return BUSY_ACTION_REASON
|
|
160
|
+
if state.cause:
|
|
161
|
+
if action == ABORT_ACTION:
|
|
162
|
+
return MERGE_UNREADABLE_ABORT_REASON
|
|
163
|
+
return state.cause
|
|
164
|
+
if action == CONTINUE_ACTION and conflict_count:
|
|
165
|
+
return RESOLVE_CONFLICTS_FIRST_REASON
|
|
166
|
+
return ""
|