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,196 @@
|
|
|
1
|
+
"""Parse a for-each-ref listing into one frozen reference snapshot, without starting Git.
|
|
2
|
+
|
|
3
|
+
The listing itself is read by ``ReferenceService`` of ``gitpane.reference.reference_edit``, with the format and the
|
|
4
|
+
parser defined here.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass, replace
|
|
10
|
+
from dataclasses import field as dataclass_field
|
|
11
|
+
from typing import TYPE_CHECKING
|
|
12
|
+
|
|
13
|
+
from gitpane.config import LOCAL_PREFIX, REMOTE_PREFIX
|
|
14
|
+
from gitpane.history.repository_value import Reference, ReferenceKind, validate_oid
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from gitpane.history.repository_value import RepositoryContext
|
|
18
|
+
from gitpane.reference.stash_list import StashList
|
|
19
|
+
|
|
20
|
+
# One for-each-ref line per reference: its full name, its object, the peeled object and both types, its symbolic
|
|
21
|
+
# target, the marker Git prints when HEAD is that very reference, then the upstream Git resolves for a local branch
|
|
22
|
+
# and the remote that upstream belongs to. Git leaves both upstream fields empty for anything but a local branch with
|
|
23
|
+
# a usable upstream, and names the remote "." when the upstream is another local branch. Every reader of references
|
|
24
|
+
# uses this one format and the indices below, so no second copy of the field order can drift from the parser, and the
|
|
25
|
+
# upstream comes with the same single listing instead of another Git process.
|
|
26
|
+
REFERENCE_FORMAT = ("%(refname)%00%(objectname)%00%(*objectname)%00%(objecttype)%00%(*objecttype)%00%(symref)%00%(HEAD)"
|
|
27
|
+
"%00%(upstream)%00%(upstream:remotename)")
|
|
28
|
+
REFERENCE_NAME_FIELD_INDEX = 0
|
|
29
|
+
REFERENCE_OBJECT_OID_FIELD_INDEX = 1
|
|
30
|
+
REFERENCE_PEELED_OID_FIELD_INDEX = 2
|
|
31
|
+
REFERENCE_OBJECT_TYPE_FIELD_INDEX = 3
|
|
32
|
+
REFERENCE_PEELED_TYPE_FIELD_INDEX = 4
|
|
33
|
+
REFERENCE_SYMBOLIC_TARGET_FIELD_INDEX = 5
|
|
34
|
+
REFERENCE_HEAD_MARKER_FIELD_INDEX = 6
|
|
35
|
+
REFERENCE_UPSTREAM_FIELD_INDEX = 7
|
|
36
|
+
REFERENCE_UPSTREAM_REMOTE_FIELD_INDEX = 8
|
|
37
|
+
REFERENCE_FIELD_COUNT = 9
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# The remote name Git gives an upstream that is a branch of this very repository: no remote to push to.
|
|
41
|
+
LOCAL_UPSTREAM_REMOTE_NAME = "."
|
|
42
|
+
REFERENCE_FIELD_SEPARATOR = "\0"
|
|
43
|
+
REFERENCE_LINE_TERMINATOR = b"\n"
|
|
44
|
+
CURRENT_HEAD_MARKER = "*"
|
|
45
|
+
OTHER_HEAD_MARKER = " "
|
|
46
|
+
COMMIT_OBJECT_TYPE = "commit"
|
|
47
|
+
TAG_OBJECT_TYPE = "tag"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def reference_kind(full_name: str) -> ReferenceKind:
|
|
51
|
+
"""Group a full reference name the way the window lists it: local, remote, and everything else with tags."""
|
|
52
|
+
if full_name.startswith(LOCAL_PREFIX):
|
|
53
|
+
return ReferenceKind.HEADS
|
|
54
|
+
if full_name.startswith(REMOTE_PREFIX):
|
|
55
|
+
return ReferenceKind.REMOTES
|
|
56
|
+
return ReferenceKind.TAGS
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def short_reference_name(full_name: str) -> str:
|
|
60
|
+
"""Write a full reference name the way the window shows it: without its refs/heads/, refs/remotes/ or refs/tags/ prefix."""
|
|
61
|
+
return full_name.removeprefix(reference_kind(full_name).prefix)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass(frozen=True)
|
|
65
|
+
class ReferenceRead:
|
|
66
|
+
"""Everything one for-each-ref listing written with REFERENCE_FORMAT proves, for the reference lists and the history graph.
|
|
67
|
+
|
|
68
|
+
The first four fields mean exactly what parse_reference_output answers. The commit set names every reference whose
|
|
69
|
+
own object, or the object its tag peels to, is a commit. The unresolved tags are those Git could only peel to
|
|
70
|
+
another tag: each full name is kept with its own tag object, which a separate lookup still has to follow. The two
|
|
71
|
+
upstream maps hold, for each local branch that has one, the full name of its upstream and the remote that upstream
|
|
72
|
+
belongs to; a branch without an upstream is absent from both, and one following another local branch is absent
|
|
73
|
+
from the remote map.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
references: tuple[Reference, ...]
|
|
77
|
+
object_oid_by_name: dict[str, str]
|
|
78
|
+
head_oid: str | None
|
|
79
|
+
head_ref: str | None
|
|
80
|
+
commit_reference_name_set: frozenset[str]
|
|
81
|
+
unresolved_tag_oid_by_name: dict[str, str]
|
|
82
|
+
upstream_by_name: dict[str, str] = dataclass_field(default_factory=dict)
|
|
83
|
+
upstream_remote_by_name: dict[str, str] = dataclass_field(default_factory=dict)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def parse_reference_output(output: bytes) -> tuple[tuple[Reference, ...], dict[str, str], str | None, str | None]:
|
|
87
|
+
"""Read a for-each-ref listing written with REFERENCE_FORMAT: references, the object of each name, HEAD's commit and branch.
|
|
88
|
+
|
|
89
|
+
This is parse_reference_read reduced to the four values every reader of references started with.
|
|
90
|
+
"""
|
|
91
|
+
read = parse_reference_read(output)
|
|
92
|
+
return read.references, read.object_oid_by_name, read.head_oid, read.head_ref
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def parse_reference_read(output: bytes) -> ReferenceRead:
|
|
96
|
+
"""Read a for-each-ref listing written with REFERENCE_FORMAT, whatever the order of its lines.
|
|
97
|
+
|
|
98
|
+
Answers the references, the object each name holds, and HEAD's commit and branch when the listing itself
|
|
99
|
+
proves them: only a local branch holding a commit and carrying Git's current marker names HEAD. A detached or
|
|
100
|
+
unborn HEAD marks nothing, and a marker on anything else is not trusted either; both answer None for HEAD, so
|
|
101
|
+
the caller asks Git directly instead of guessing. A cut-off, malformed, duplicated or doubly marked listing is
|
|
102
|
+
refused, because a partial list of references would look exactly like a complete one to the window.
|
|
103
|
+
"""
|
|
104
|
+
if output and not output.endswith(REFERENCE_LINE_TERMINATOR):
|
|
105
|
+
raise ValueError("Incomplete reference data returned by Git")
|
|
106
|
+
records = output.split(REFERENCE_LINE_TERMINATOR)
|
|
107
|
+
records.pop()
|
|
108
|
+
references: list[Reference] = []
|
|
109
|
+
object_oid_by_name: dict[str, str] = {}
|
|
110
|
+
commit_reference_name_set: set[str] = set()
|
|
111
|
+
unresolved_tag_oid_by_name: dict[str, str] = {}
|
|
112
|
+
upstream_by_name: dict[str, str] = {}
|
|
113
|
+
upstream_remote_by_name: dict[str, str] = {}
|
|
114
|
+
marked_name = None
|
|
115
|
+
for record in records:
|
|
116
|
+
# The HEAD marker is a single space and the upstream fields are empty for most references: the line is split as
|
|
117
|
+
# it is, never trimmed, so a blank field is never mistaken for a missing one.
|
|
118
|
+
fields = record.decode("utf-8", "surrogateescape").split(REFERENCE_FIELD_SEPARATOR)
|
|
119
|
+
if len(fields) != REFERENCE_FIELD_COUNT:
|
|
120
|
+
raise ValueError("Incomplete reference data returned by Git")
|
|
121
|
+
full_name = fields[REFERENCE_NAME_FIELD_INDEX]
|
|
122
|
+
object_oid = fields[REFERENCE_OBJECT_OID_FIELD_INDEX]
|
|
123
|
+
peeled_oid = fields[REFERENCE_PEELED_OID_FIELD_INDEX]
|
|
124
|
+
object_type = fields[REFERENCE_OBJECT_TYPE_FIELD_INDEX]
|
|
125
|
+
peeled_type = fields[REFERENCE_PEELED_TYPE_FIELD_INDEX]
|
|
126
|
+
symbolic_target = fields[REFERENCE_SYMBOLIC_TARGET_FIELD_INDEX]
|
|
127
|
+
head_marker = fields[REFERENCE_HEAD_MARKER_FIELD_INDEX]
|
|
128
|
+
if not full_name or not object_type or head_marker not in (CURRENT_HEAD_MARKER, OTHER_HEAD_MARKER):
|
|
129
|
+
raise ValueError("Incomplete reference data returned by Git")
|
|
130
|
+
validate_oid(object_oid)
|
|
131
|
+
if peeled_oid:
|
|
132
|
+
validate_oid(peeled_oid)
|
|
133
|
+
if full_name in object_oid_by_name:
|
|
134
|
+
raise ValueError("Git listed the same reference twice")
|
|
135
|
+
if head_marker == CURRENT_HEAD_MARKER:
|
|
136
|
+
if marked_name is not None:
|
|
137
|
+
raise ValueError("Git marked more than one reference as HEAD")
|
|
138
|
+
marked_name = full_name
|
|
139
|
+
target_oid = object_oid if object_type == COMMIT_OBJECT_TYPE else peeled_oid if peeled_type == COMMIT_OBJECT_TYPE and peeled_oid else None
|
|
140
|
+
object_oid_by_name[full_name] = object_oid
|
|
141
|
+
references.append(Reference(full_name, reference_kind(full_name), target_oid, symbolic_target or None, False))
|
|
142
|
+
if target_oid is not None:
|
|
143
|
+
commit_reference_name_set.add(full_name)
|
|
144
|
+
elif object_type == TAG_OBJECT_TYPE and peeled_type == TAG_OBJECT_TYPE:
|
|
145
|
+
unresolved_tag_oid_by_name[full_name] = object_oid
|
|
146
|
+
if full_name.startswith(LOCAL_PREFIX):
|
|
147
|
+
upstream = fields[REFERENCE_UPSTREAM_FIELD_INDEX]
|
|
148
|
+
upstream_remote = fields[REFERENCE_UPSTREAM_REMOTE_FIELD_INDEX]
|
|
149
|
+
if upstream:
|
|
150
|
+
upstream_by_name[full_name] = upstream
|
|
151
|
+
if upstream_remote and upstream_remote != LOCAL_UPSTREAM_REMOTE_NAME:
|
|
152
|
+
upstream_remote_by_name[full_name] = upstream_remote
|
|
153
|
+
head_oid = None
|
|
154
|
+
head_ref = None
|
|
155
|
+
if marked_name is not None:
|
|
156
|
+
marked = next(reference for reference in references if reference.full_name == marked_name)
|
|
157
|
+
if marked.kind == ReferenceKind.HEADS and marked.symbolic_target is None and marked.target_oid == object_oid_by_name[marked_name]:
|
|
158
|
+
head_oid = marked.target_oid
|
|
159
|
+
head_ref = marked_name
|
|
160
|
+
return ReferenceRead(mark_head(references, head_ref), object_oid_by_name, head_oid, head_ref, frozenset(commit_reference_name_set), unresolved_tag_oid_by_name,
|
|
161
|
+
upstream_by_name, upstream_remote_by_name)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def mark_head(references: list[Reference] | tuple[Reference, ...], head_ref: str | None) -> tuple[Reference, ...]:
|
|
165
|
+
"""Flag the one reference HEAD is on, and only that one."""
|
|
166
|
+
marked_references: list[Reference] = []
|
|
167
|
+
for reference in references:
|
|
168
|
+
is_head = head_ref is not None and reference.full_name == head_ref
|
|
169
|
+
marked_references.append(reference if reference.is_head == is_head else replace(reference, is_head=is_head))
|
|
170
|
+
return tuple(marked_references)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@dataclass(frozen=True)
|
|
174
|
+
class ReferenceSnapshot:
|
|
175
|
+
"""Freeze refs, HEAD and worktree membership from one consistent native read."""
|
|
176
|
+
|
|
177
|
+
context: RepositoryContext
|
|
178
|
+
git_filename: str
|
|
179
|
+
generation: int
|
|
180
|
+
references: tuple[Reference, ...]
|
|
181
|
+
object_oid_by_name: dict[str, str]
|
|
182
|
+
head_oid: str | None
|
|
183
|
+
head_ref: str | None
|
|
184
|
+
worktrees: bytes
|
|
185
|
+
signature: tuple
|
|
186
|
+
remote_names: tuple[str, ...] = ()
|
|
187
|
+
# The listing this capture was built from, so the history graph can use the same observation instead of listing
|
|
188
|
+
# the references again. It is derived from the fields above and never compared: two captures of the same state
|
|
189
|
+
# are equal whether or not they carry it.
|
|
190
|
+
reference_read: ReferenceRead | None = dataclass_field(default=None, compare=False)
|
|
191
|
+
# Upstream of each local branch that has one, and the remote of that upstream, from the same listing.
|
|
192
|
+
upstream_by_name: dict[str, str] = dataclass_field(default_factory=dict)
|
|
193
|
+
upstream_remote_by_name: dict[str, str] = dataclass_field(default_factory=dict)
|
|
194
|
+
# The stashes, read in the same pass for the left panel of a window. Only reads made to show a window carry it,
|
|
195
|
+
# and it never takes part in comparing two captures: the stash reflog is not one of the files the signature covers.
|
|
196
|
+
stash_list: StashList | None = dataclass_field(default=None, compare=False)
|