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
gitpane/history/graph.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""Lay out real parent edges in persistent lanes without inferring ancestry."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, replace
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from collections.abc import Set as AbstractSet
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class GraphSegment:
|
|
14
|
+
"""Describe a portion of a direct child-parent edge in logical row coordinates."""
|
|
15
|
+
|
|
16
|
+
child_oid: str
|
|
17
|
+
parent_oid: str
|
|
18
|
+
start_lane: int
|
|
19
|
+
end_lane: int
|
|
20
|
+
start_position: float
|
|
21
|
+
end_position: float
|
|
22
|
+
color_index: int
|
|
23
|
+
kind: str = "parent"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True)
|
|
27
|
+
class GraphRow:
|
|
28
|
+
"""Place exactly one commit node beside its incoming and outgoing edge portions."""
|
|
29
|
+
|
|
30
|
+
oid: str
|
|
31
|
+
lane: int
|
|
32
|
+
segments: list[GraphSegment]
|
|
33
|
+
lane_count: int
|
|
34
|
+
is_tip: bool
|
|
35
|
+
color_index: int
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
ROW_TOP, ROW_CENTER, ROW_BOTTOM = 0.0, 0.5, 1.0
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class GraphLayout:
|
|
42
|
+
"""Carry open parent edges across pages while keeping existing lanes stable."""
|
|
43
|
+
|
|
44
|
+
def __init__(self) -> None:
|
|
45
|
+
"""Start with no pending ancestry or allocated lanes."""
|
|
46
|
+
self.lane_by_oid: dict[str, int] = {}
|
|
47
|
+
self.color_by_oid: dict[str, int] = {}
|
|
48
|
+
self.next_color_index = 0
|
|
49
|
+
self.open_edges: list[GraphSegment] = []
|
|
50
|
+
self.row_by_oid: dict[str, GraphRow] = {}
|
|
51
|
+
|
|
52
|
+
def append(self, oid: str, parent_oids: list[str], known_oid_set: AbstractSet[str], index_complete: bool) -> GraphRow:
|
|
53
|
+
"""Extend only direct parent edges, marking missing or unread targets explicitly."""
|
|
54
|
+
existing = self.row_by_oid.get(oid)
|
|
55
|
+
if existing is not None:
|
|
56
|
+
return existing
|
|
57
|
+
lane = self.lane_by_oid.pop(oid, None)
|
|
58
|
+
is_tip = lane is None
|
|
59
|
+
if is_tip:
|
|
60
|
+
lane = self._right_lane()
|
|
61
|
+
color_index = self.color_by_oid.pop(oid, None)
|
|
62
|
+
if color_index is None:
|
|
63
|
+
color_index = self._new_color()
|
|
64
|
+
segments = []
|
|
65
|
+
# Built field by field rather than with dataclasses.replace, which costs several times more and runs for every
|
|
66
|
+
# open edge of every row of a long history.
|
|
67
|
+
for edge in self.open_edges:
|
|
68
|
+
ends_here = edge.parent_oid == oid
|
|
69
|
+
segments.append(GraphSegment(edge.child_oid, edge.parent_oid, edge.end_lane, lane if ends_here else edge.end_lane, ROW_TOP, ROW_CENTER if ends_here else ROW_BOTTOM,
|
|
70
|
+
edge.color_index, edge.kind))
|
|
71
|
+
first_parent_index = 0
|
|
72
|
+
for parent_index, parent_oid in enumerate(dict.fromkeys(parent_oids)):
|
|
73
|
+
target_lane = self.lane_by_oid.get(parent_oid)
|
|
74
|
+
if target_lane is None:
|
|
75
|
+
target_lane = lane if lane not in self.lane_by_oid.values() else self._right_lane()
|
|
76
|
+
parent_color = self.color_by_oid.get(parent_oid)
|
|
77
|
+
if parent_color is None:
|
|
78
|
+
parent_color = color_index if parent_index == first_parent_index else self._new_color()
|
|
79
|
+
unavailable = index_complete and parent_oid not in known_oid_set
|
|
80
|
+
kind = "unavailable" if unavailable else "pending" if parent_oid not in self.row_by_oid else "parent"
|
|
81
|
+
segment = GraphSegment(oid, parent_oid, lane, target_lane, ROW_CENTER, ROW_BOTTOM, parent_color, kind)
|
|
82
|
+
segments.append(segment)
|
|
83
|
+
if not unavailable:
|
|
84
|
+
self.lane_by_oid[parent_oid] = target_lane
|
|
85
|
+
self.color_by_oid[parent_oid] = parent_color
|
|
86
|
+
segments = self._compact_lane(segments)
|
|
87
|
+
lane_count = lane + 1
|
|
88
|
+
for segment in segments:
|
|
89
|
+
lane_count = max(lane_count, segment.start_lane + 1, segment.end_lane + 1)
|
|
90
|
+
row = GraphRow(oid, lane, segments, lane_count, is_tip, color_index)
|
|
91
|
+
self.row_by_oid[oid] = row
|
|
92
|
+
return row
|
|
93
|
+
|
|
94
|
+
def _right_lane(self) -> int:
|
|
95
|
+
"""Start new branches beyond active ancestry, leaving gaps on its left unused."""
|
|
96
|
+
empty_lane = -1
|
|
97
|
+
return max(self.lane_by_oid.values(), default=empty_lane) + 1
|
|
98
|
+
|
|
99
|
+
def _new_color(self) -> int:
|
|
100
|
+
"""Assign a branch identity independent of the lane it occupies after a join."""
|
|
101
|
+
color_index = self.next_color_index
|
|
102
|
+
self.next_color_index += 1
|
|
103
|
+
return color_index
|
|
104
|
+
|
|
105
|
+
def _compact_lane(self, segments: list[GraphSegment]) -> list[GraphSegment]:
|
|
106
|
+
"""Close empty lanes below the nodes without moving previously published rows."""
|
|
107
|
+
previous_lanes = sorted(set(self.lane_by_oid.values()))
|
|
108
|
+
if not previous_lanes or previous_lanes[-1] == len(previous_lanes) - 1:
|
|
109
|
+
# No lane is empty, which is the common case: every segment and every lane stays where it is.
|
|
110
|
+
self.open_edges = []
|
|
111
|
+
for segment in segments:
|
|
112
|
+
if segment.end_position == ROW_BOTTOM and segment.kind != "unavailable":
|
|
113
|
+
self.open_edges.append(segment)
|
|
114
|
+
return segments
|
|
115
|
+
lane_by_previous = {}
|
|
116
|
+
for lane, previous in enumerate(previous_lanes):
|
|
117
|
+
lane_by_previous[previous] = lane
|
|
118
|
+
compacted_segments = []
|
|
119
|
+
for segment in segments:
|
|
120
|
+
lane = lane_by_previous.get(segment.end_lane, segment.end_lane)
|
|
121
|
+
if segment.end_position != ROW_BOTTOM or segment.kind == "unavailable" or lane == segment.end_lane:
|
|
122
|
+
compacted_segments.append(segment)
|
|
123
|
+
continue
|
|
124
|
+
if segment.start_position == ROW_TOP:
|
|
125
|
+
compacted_segments.append(replace(segment, end_lane=segment.start_lane, end_position=ROW_CENTER))
|
|
126
|
+
segment = replace(segment, start_position=ROW_CENTER)
|
|
127
|
+
compacted_segments.append(replace(segment, end_lane=lane))
|
|
128
|
+
compacted_lane_by_oid = {}
|
|
129
|
+
for oid, lane in self.lane_by_oid.items():
|
|
130
|
+
compacted_lane_by_oid[oid] = lane_by_previous[lane]
|
|
131
|
+
self.lane_by_oid = compacted_lane_by_oid
|
|
132
|
+
self.open_edges = []
|
|
133
|
+
for segment in compacted_segments:
|
|
134
|
+
if segment.end_position == ROW_BOTTOM and segment.kind != "unavailable":
|
|
135
|
+
self.open_edges.append(segment)
|
|
136
|
+
return compacted_segments
|
|
137
|
+
|
|
138
|
+
def finish_index(self, known_oid_set: set[str]) -> None:
|
|
139
|
+
"""Resolve unread boundaries once EOF proves which parent objects are unavailable."""
|
|
140
|
+
for oid, row in tuple(self.row_by_oid.items()):
|
|
141
|
+
segments = []
|
|
142
|
+
for segment in row.segments:
|
|
143
|
+
kind = "parent" if segment.parent_oid in known_oid_set else "unavailable"
|
|
144
|
+
segments.append(replace(segment, kind=kind))
|
|
145
|
+
self.row_by_oid[oid] = replace(row, segments=segments)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Name what the filter of a repository window searches in each commit."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from typing import Self
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class HistoryFilterMode(Enum):
|
|
10
|
+
"""What the filter field of a repository window compares the typed text with: the commit messages or the names of the files each commit changes.
|
|
11
|
+
|
|
12
|
+
The mode is never saved: it lives on the window only. The value is only written in diagnostics; the other
|
|
13
|
+
attributes are the texts the window shows for the mode.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
MESSAGES = ("messages", "Filter messages", "Search full commit messages (* wildcard)", "Filter commit messages", "No message matches")
|
|
17
|
+
FILES = ("files", "Filter files", "Search changed file names (* wildcard)", "Filter changed file names", "No changed file matches")
|
|
18
|
+
|
|
19
|
+
def __new__(cls, value: str, label: str, placeholder: str, field_name: str, no_match_text: str) -> Self:
|
|
20
|
+
"""Keep the diagnostic word as the value and the texts the window shows for the mode beside it."""
|
|
21
|
+
member = object.__new__(cls)
|
|
22
|
+
member._value_ = value
|
|
23
|
+
member.label = label
|
|
24
|
+
member.placeholder = placeholder
|
|
25
|
+
member.field_name = field_name
|
|
26
|
+
member.no_match_text = no_match_text
|
|
27
|
+
return member
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# Every opening of a window, and every repository picked in its switch list, starts on this mode.
|
|
31
|
+
HISTORY_FILTER_INITIAL_MODE = HistoryFilterMode.MESSAGES
|