py-wlcli 0.8.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.
- py_wlcli-0.8.0.dist-info/METADATA +139 -0
- py_wlcli-0.8.0.dist-info/RECORD +320 -0
- py_wlcli-0.8.0.dist-info/WHEEL +4 -0
- py_wlcli-0.8.0.dist-info/entry_points.txt +2 -0
- py_wlcli-0.8.0.dist-info/licenses/LICENSE +21 -0
- wl_cli/__init__.py +27 -0
- wl_cli/__main__.py +130 -0
- wl_cli/capabilities.py +79 -0
- wl_cli/cc/__init__.py +5 -0
- wl_cli/cc/checks.py +1019 -0
- wl_cli/cc/cli.py +370 -0
- wl_cli/cc/mutate.py +1350 -0
- wl_cli/cli_click.py +156 -0
- wl_cli/commands/__init__.py +84 -0
- wl_cli/commands/base.py +80 -0
- wl_cli/commands/buildcommands/__init__.py +1 -0
- wl_cli/commands/buildcommands/build.py +71 -0
- wl_cli/commands/buildcommands/build_utils.py +47 -0
- wl_cli/commands/buildcommands/builders/__init__.py +18 -0
- wl_cli/commands/buildcommands/builders/cleanup.py +35 -0
- wl_cli/commands/buildcommands/builders/coordinator.py +122 -0
- wl_cli/commands/buildcommands/builders/environment.py +81 -0
- wl_cli/commands/buildcommands/builders/rust_builder.py +39 -0
- wl_cli/commands/buildcommands/builders/stubs.py +58 -0
- wl_cli/commands/buildcommands/dist.py +326 -0
- wl_cli/commands/buildcommands/test.py +89 -0
- wl_cli/commands/clean/__init__.py +1 -0
- wl_cli/commands/clean/build.py +151 -0
- wl_cli/commands/clean/clean.py +294 -0
- wl_cli/commands/clean/clean_utils.py +77 -0
- wl_cli/commands/clean/core.py +236 -0
- wl_cli/commands/clean/dead_code/__init__.py +0 -0
- wl_cli/commands/clean/dead_code/dead_code_command.py +70 -0
- wl_cli/commands/clean/dead_code/detector.py +161 -0
- wl_cli/commands/clean/dead_code/fixer.py +176 -0
- wl_cli/commands/clean/dead_code/reporter.py +295 -0
- wl_cli/commands/clean/dead_code/utils.py +0 -0
- wl_cli/commands/clean/lfs.py +215 -0
- wl_cli/commands/clean/python.py +81 -0
- wl_cli/commands/clean/rust.py +91 -0
- wl_cli/commands/clean/utils/__init__.py +34 -0
- wl_cli/commands/clean/venv.py +91 -0
- wl_cli/commands/cleanup.py +73 -0
- wl_cli/commands/click_base.py +79 -0
- wl_cli/commands/config/__init__.py +1 -0
- wl_cli/commands/config/config.py +291 -0
- wl_cli/commands/debug/__init__.py +6 -0
- wl_cli/commands/debug/debug.py +309 -0
- wl_cli/commands/debug/debug_cache.py +86 -0
- wl_cli/commands/debug/debug_subagent.py +156 -0
- wl_cli/commands/debug/output_formatter.py +165 -0
- wl_cli/commands/debug/source_hasher.py +92 -0
- wl_cli/commands/format/__init__.py +13 -0
- wl_cli/commands/format/format_cache_adapter.py +125 -0
- wl_cli/commands/format/format_command.py +197 -0
- wl_cli/commands/format/format_coordinator.py +191 -0
- wl_cli/commands/format/format_executor.py +470 -0
- wl_cli/commands/format/format_outcome.py +44 -0
- wl_cli/commands/format/format_path_handler.py +1016 -0
- wl_cli/commands/format/format_progress.py +62 -0
- wl_cli/commands/format/format_reporter.py +31 -0
- wl_cli/commands/format/python_formatter.py +506 -0
- wl_cli/commands/format/rust_formatter.py +106 -0
- wl_cli/commands/gate/__init__.py +1 -0
- wl_cli/commands/gate/gate_command.py +862 -0
- wl_cli/commands/gate/preflight.py +445 -0
- wl_cli/commands/git/__init__.py +1 -0
- wl_cli/commands/git/config.py +169 -0
- wl_cli/commands/git/git_facts.py +60 -0
- wl_cli/commands/git/git_flow.py +86 -0
- wl_cli/commands/git/hook.py +112 -0
- wl_cli/commands/git/policy.py +216 -0
- wl_cli/commands/git/push_update.py +82 -0
- wl_cli/commands/initenv/__init__.py +1 -0
- wl_cli/commands/initenv/common/__init__.py +23 -0
- wl_cli/commands/initenv/common/check_git_bash.py +78 -0
- wl_cli/commands/initenv/common/error_handler.py +68 -0
- wl_cli/commands/initenv/common/exceptions.py +52 -0
- wl_cli/commands/initenv/components/__init__.py +4 -0
- wl_cli/commands/initenv/components/cargo_sync.py +240 -0
- wl_cli/commands/initenv/components/config_manager.py +106 -0
- wl_cli/commands/initenv/components/directory_utils.py +71 -0
- wl_cli/commands/initenv/components/git/__init__.py +9 -0
- wl_cli/commands/initenv/components/git/branch_creator.py +143 -0
- wl_cli/commands/initenv/components/git/git_flow_interface.py +24 -0
- wl_cli/commands/initenv/components/git/git_flow_manager.py +159 -0
- wl_cli/commands/initenv/components/git/git_lfs_manager.py +272 -0
- wl_cli/commands/initenv/components/git/git_utility.py +254 -0
- wl_cli/commands/initenv/components/git/gitignore_manager.py +53 -0
- wl_cli/commands/initenv/components/git_initializer.py +212 -0
- wl_cli/commands/initenv/components/i18n_manager.py +104 -0
- wl_cli/commands/initenv/components/log_manager.py +241 -0
- wl_cli/commands/initenv/components/project_structure/__init__.py +23 -0
- wl_cli/commands/initenv/components/project_structure/config_generator.py +360 -0
- wl_cli/commands/initenv/components/project_structure/directory_creator.py +184 -0
- wl_cli/commands/initenv/components/project_structure/hook_source.py +110 -0
- wl_cli/commands/initenv/components/project_structure/hooks_manager.py +995 -0
- wl_cli/commands/initenv/components/project_structure/readme_handler.py +56 -0
- wl_cli/commands/initenv/components/project_structure/rust_files_handler.py +30 -0
- wl_cli/commands/initenv/components/project_structure/setup_handler.py +103 -0
- wl_cli/commands/initenv/components/project_structure/sop_generator.py +73 -0
- wl_cli/commands/initenv/components/project_structure/template_copier.py +104 -0
- wl_cli/commands/initenv/components/project_structure/template_manager.py +195 -0
- wl_cli/commands/initenv/components/pyproject_generator.py +194 -0
- wl_cli/commands/initenv/components/rust_initializer.py +145 -0
- wl_cli/commands/initenv/components/symlink_cleaner.py +99 -0
- wl_cli/commands/initenv/components/symlink_manager.py +52 -0
- wl_cli/commands/initenv/components/venv_manager.py +118 -0
- wl_cli/commands/initenv/initenv.py +169 -0
- wl_cli/commands/initenv/services/__init__.py +11 -0
- wl_cli/commands/initenv/services/environment_initializer.py +288 -0
- wl_cli/commands/initenv/services/git_lfs_initializer.py +26 -0
- wl_cli/commands/initenv/services/project_structure_initializer.py +52 -0
- wl_cli/commands/lfs/README.md +221 -0
- wl_cli/commands/lfs/__init__.py +5 -0
- wl_cli/commands/lfs/cli.py +106 -0
- wl_cli/commands/lfs/config.py +164 -0
- wl_cli/commands/lfs/hook.py +61 -0
- wl_cli/commands/lfs/initializer.py +531 -0
- wl_cli/commands/lfs/manager.py +95 -0
- wl_cli/commands/lfs/policy.py +165 -0
- wl_cli/commands/lfs/watcher.py +294 -0
- wl_cli/commands/lint/__init__.py +1 -0
- wl_cli/commands/lint/lint_command.py +297 -0
- wl_cli/commands/lint/lint_executor.py +409 -0
- wl_cli/commands/lint/lint_reporter.py +579 -0
- wl_cli/commands/lint/lint_run.py +258 -0
- wl_cli/commands/lint/mypy_config.py +90 -0
- wl_cli/commands/registry.py +103 -0
- wl_cli/commands/self/__init__.py +7 -0
- wl_cli/commands/self/common/__init__.py +8 -0
- wl_cli/commands/self/common/capabilities.py +30 -0
- wl_cli/commands/self/common/config.py +27 -0
- wl_cli/commands/self/common/version_cache.py +85 -0
- wl_cli/commands/self/handlers/__init__.py +19 -0
- wl_cli/commands/self/handlers/base_handler.py +28 -0
- wl_cli/commands/self/handlers/install_handler.py +85 -0
- wl_cli/commands/self/handlers/status_handler.py +66 -0
- wl_cli/commands/self/handlers/uninstall_handler.py +58 -0
- wl_cli/commands/self/handlers/update_handler.py +102 -0
- wl_cli/commands/self/handlers/version_handler.py +16 -0
- wl_cli/commands/self/self_command.py +237 -0
- wl_cli/commands/self/services/__init__.py +15 -0
- wl_cli/commands/self/services/installation_manager.py +187 -0
- wl_cli/commands/self/services/local_installer.py +165 -0
- wl_cli/commands/self/services/pypi_updater.py +278 -0
- wl_cli/commands/self/services/windows_update.py +174 -0
- wl_cli/commands/testcommands/__init__.py +8 -0
- wl_cli/commands/testcommands/base_dependency_analyzer.py +198 -0
- wl_cli/commands/testcommands/batch_processor.py +278 -0
- wl_cli/commands/testcommands/cache_manager.py +1156 -0
- wl_cli/commands/testcommands/coverage_analyzer.py +955 -0
- wl_cli/commands/testcommands/coverage_cache_manager.py +183 -0
- wl_cli/commands/testcommands/progress_manager.py +79 -0
- wl_cli/commands/testcommands/python_env_detector.py +207 -0
- wl_cli/commands/testcommands/report_generator.py +101 -0
- wl_cli/commands/testcommands/single_test_executor.py +250 -0
- wl_cli/commands/testcommands/speed_checker.py +118 -0
- wl_cli/commands/testcommands/test_command_builder.py +165 -0
- wl_cli/commands/testcommands/test_commands.py +786 -0
- wl_cli/commands/testcommands/test_reporter.py +181 -0
- wl_cli/commands/testcommands/test_result_handler.py +96 -0
- wl_cli/commands/testcommands/test_runner.py +1343 -0
- wl_cli/commands/testcommands/test_utils.py +197 -0
- wl_cli/commands/testcommands/utils/__init__.py +17 -0
- wl_cli/commands/testcommands/utils/coverage_parser.py +98 -0
- wl_cli/commands/testcommands/utils/env_info.py +36 -0
- wl_cli/commands/testcommands/utils/junit_parser.py +75 -0
- wl_cli/commands/testcommands/utils/markdown_utils.py +20 -0
- wl_cli/commands/testcommands/utils/project_root.py +61 -0
- wl_cli/commands/update/__init__.py +9 -0
- wl_cli/commands/update/services/__init__.py +21 -0
- wl_cli/commands/update/services/backup_manager.py +207 -0
- wl_cli/commands/update/services/component_manager.py +83 -0
- wl_cli/commands/update/services/conflict_resolver.py +189 -0
- wl_cli/commands/update/services/vendors_updater.py +85 -0
- wl_cli/commands/update/services/version_tracker.py +127 -0
- wl_cli/commands/update/update_command.py +270 -0
- wl_cli/commands/update/utils/__init__.py +3 -0
- wl_cli/commands/version/__init__.py +9 -0
- wl_cli/commands/version/version_command.py +155 -0
- wl_cli/commands/workflow.py +477 -0
- wl_cli/env/__init__.py +5 -0
- wl_cli/env/cli.py +167 -0
- wl_cli/env_status.py +157 -0
- wl_cli/envelope.py +91 -0
- wl_cli/exceptions.py +85 -0
- wl_cli/lib/__init__.py +3 -0
- wl_cli/lib/rust_utils.py +35 -0
- wl_cli/mcp/__init__.py +6 -0
- wl_cli/mcp/capabilities.py +19 -0
- wl_cli/mcp/cli.py +22 -0
- wl_cli/mcp/gitlab.py +553 -0
- wl_cli/mcp/k8s.py +344 -0
- wl_cli/mcp/protocol.py +159 -0
- wl_cli/platform.py +139 -0
- wl_cli/quality/__init__.py +18 -0
- wl_cli/utils/__init__.py +7 -0
- wl_cli/utils/cache.py +491 -0
- wl_cli/utils/cache_ledger.py +355 -0
- wl_cli/utils/cache_store.py +546 -0
- wl_cli/utils/config/__init__.py +27 -0
- wl_cli/utils/config/config_loader.py +163 -0
- wl_cli/utils/config/core.py +862 -0
- wl_cli/utils/config/defaults.py +142 -0
- wl_cli/utils/config/env_exporter.py +86 -0
- wl_cli/utils/config/env_parser.py +108 -0
- wl_cli/utils/config/validation.py +280 -0
- wl_cli/utils/error_handler.py +165 -0
- wl_cli/utils/file_copier.py +56 -0
- wl_cli/utils/file_operations.py +506 -0
- wl_cli/utils/language_utils.py +122 -0
- wl_cli/utils/lazy_import.py +240 -0
- wl_cli/utils/log_levels.py +35 -0
- wl_cli/utils/log_rotators.py +74 -0
- wl_cli/utils/logging.py +458 -0
- wl_cli/utils/output_context.py +142 -0
- wl_cli/utils/performance/__init__.py +1 -0
- wl_cli/utils/performance/decorators.py +111 -0
- wl_cli/utils/performance/exporter.py +38 -0
- wl_cli/utils/performance/models.py +16 -0
- wl_cli/utils/performance/monitor.py +161 -0
- wl_cli/utils/performance_monitor.py +32 -0
- wl_cli/utils/platform_adapter.py +131 -0
- wl_cli/utils/pre_commit.py +266 -0
- wl_cli/utils/pre_commit_config.py +72 -0
- wl_cli/utils/root.py +521 -0
- wl_cli/utils/root_fallback.py +256 -0
- wl_cli/utils/run_evidence.py +236 -0
- wl_cli/utils/structured_logger.py +18 -0
- wl_cli/utils/structured_logging/__init__.py +19 -0
- wl_cli/utils/structured_logging/config.py +337 -0
- wl_cli/utils/structured_logging/core.py +177 -0
- wl_cli/utils/structured_logging/env.py +56 -0
- wl_cli/utils/structured_logging/filtering.py +75 -0
- wl_cli/utils/structured_logging/filters.py +55 -0
- wl_cli/utils/structured_logging/formatters.py +251 -0
- wl_cli/utils/structured_logging/handlers.py +308 -0
- wl_cli/utils/structured_logging/logger.py +354 -0
- wl_cli/utils/subprocess.py +71 -0
- wl_cli/utils/subprocess_utils.py +260 -0
- wl_cli/utils/test_utils/__init__.py +5 -0
- wl_cli/utils/test_utils/test_filter.py +36 -0
- wl_cli/utils/uv_run.py +79 -0
- wl_cli/utils/uv_tool.py +117 -0
- wl_cli/utils/uv_tree.py +71 -0
- wl_cli/utils/uv_workspace.py +174 -0
- wl_cli/utils/version/__init__.py +15 -0
- wl_cli/utils/version/pypi_version_checker.py +95 -0
- wl_cli/utils/version/version_comparator.py +133 -0
- wl_cli/utils/version/version_detectors.py +312 -0
- wl_cli/utils/version/version_service.py +131 -0
- wl_cli/utils/version/version_updater.py +198 -0
- wl_cli/utils/wl_dir_updater.py +1440 -0
- wl_cli/utils/workspace_detector/__init__.py +28 -0
- wl_cli/utils/workspace_detector/core.py +161 -0
- wl_cli/utils/workspace_detector/detection_strategies.py +107 -0
- wl_cli/utils/workspace_detector/exceptions.py +5 -0
- wl_cli/utils/workspace_detector/types.py +17 -0
- wl_cli/utils/workspace_detector/venv_resolver.py +84 -0
- wl_cli/vendors/config/.codespellrc +2 -0
- wl_cli/vendors/config/dependencies.toml +10 -0
- wl_cli/vendors/config/mypy.ini +62 -0
- wl_cli/vendors/config/pyproject_defaults.toml +28 -0
- wl_cli/vendors/config/template_config.toml +24 -0
- wl_cli/vendors/config/tool_configs.toml +76 -0
- wl_cli/vendors/git/.gitignore +122 -0
- wl_cli/vendors/git/.pre-commit-config.yaml +37 -0
- wl_cli/vendors/hooks/applypatch-msg.sample +15 -0
- wl_cli/vendors/hooks/commit-msg.sample +24 -0
- wl_cli/vendors/hooks/fsmonitor-watchman.sample +174 -0
- wl_cli/vendors/hooks/post-update.sample +8 -0
- wl_cli/vendors/hooks/pre-applypatch.sample +14 -0
- wl_cli/vendors/hooks/pre-commit +45 -0
- wl_cli/vendors/hooks/pre-commit.legacy +31 -0
- wl_cli/vendors/hooks/pre-commit.sample +49 -0
- wl_cli/vendors/hooks/pre-merge-commit.sample +13 -0
- wl_cli/vendors/hooks/pre-push.sample +60 -0
- wl_cli/vendors/hooks/pre-rebase.sample +169 -0
- wl_cli/vendors/hooks/pre-receive.sample +24 -0
- wl_cli/vendors/hooks/prepare-commit-msg.sample +42 -0
- wl_cli/vendors/hooks/push-to-checkout.sample +78 -0
- wl_cli/vendors/hooks/sendemail-validate.sample +77 -0
- wl_cli/vendors/hooks/unix/applypatch-msg.sample +15 -0
- wl_cli/vendors/hooks/unix/check_lockfile.py +58 -0
- wl_cli/vendors/hooks/unix/check_lockfile.sh +36 -0
- wl_cli/vendors/hooks/unix/commit-msg.sample +24 -0
- wl_cli/vendors/hooks/unix/fsmonitor-watchman.sample +174 -0
- wl_cli/vendors/hooks/unix/post-update.sample +8 -0
- wl_cli/vendors/hooks/unix/pre-applypatch.sample +14 -0
- wl_cli/vendors/hooks/unix/pre-commit +190 -0
- wl_cli/vendors/hooks/unix/pre-commit.legacy +31 -0
- wl_cli/vendors/hooks/unix/pre-commit.py +81 -0
- wl_cli/vendors/hooks/unix/pre-commit.sample +49 -0
- wl_cli/vendors/hooks/unix/pre-merge-commit.sample +13 -0
- wl_cli/vendors/hooks/unix/pre-push +417 -0
- wl_cli/vendors/hooks/unix/pre-push.sample +53 -0
- wl_cli/vendors/hooks/unix/pre-rebase.sample +169 -0
- wl_cli/vendors/hooks/unix/pre-receive.sample +24 -0
- wl_cli/vendors/hooks/unix/prepare-commit-msg.sample +42 -0
- wl_cli/vendors/hooks/update.sample +128 -0
- wl_cli/vendors/hooks/win/work.bat +63 -0
- wl_cli/vendors/project/.gitignore +90 -0
- wl_cli/vendors/project/.python-version +1 -0
- wl_cli/vendors/project/LICENSE +21 -0
- wl_cli/vendors/project/README.md +0 -0
- wl_cli/vendors/project/pyproject.toml +27 -0
- wl_cli/vendors/project/pyproject_no_workspace.toml +16 -0
- wl_cli/vendors/project/pyproject_workspace.toml +14 -0
- wl_cli/vendors/readme/README.md +120 -0
- wl_cli/vendors/rust/Cargo.toml +14 -0
- wl_cli/vendors/rust/lib/__init__.py +3 -0
- wl_cli/vendors/rust/lib/rust_utils.py +31 -0
- wl_cli/vendors/rust/src/lib.rs +13 -0
- wl_cli/vendors/rust/src/main.rs +3 -0
- wl_cli/vendors/rust/tests/test_rust_fallback.py +40 -0
- wl_cli/vendors/sop_doc/git-dev-sop.md +220 -0
- wl_cli/vendors/sop_doc/industrial-standard-sop.md +145 -0
- wl_cli/vendors/sop_doc/python-sop.md +342 -0
- wl_cli/vendors/sop_doc/rust-readme.md +219 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""Git Flow 分支管理器"""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
from wl_cli.utils.logging import log_info_pair
|
|
7
|
+
|
|
8
|
+
from .branch_creator import BranchCreator
|
|
9
|
+
from .git_flow_interface import GitFlowInterface
|
|
10
|
+
|
|
11
|
+
# 导入新创建的模块
|
|
12
|
+
from .git_utility import GitUtility
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class GitFlowManager(GitFlowInterface):
|
|
16
|
+
"""Git Flow 分支管理器,实现 Git Flow 接口"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, env: dict[str, str], skip_remote_ops: bool = False) -> None:
|
|
19
|
+
"""
|
|
20
|
+
初始化 GitFlowManager 类
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
env: 执行 Git 命令时使用的环境变量
|
|
24
|
+
skip_remote_ops: 是否跳过远程操作
|
|
25
|
+
"""
|
|
26
|
+
self.env = env
|
|
27
|
+
self.skip_remote_ops = skip_remote_ops
|
|
28
|
+
self.git_utility = GitUtility(env)
|
|
29
|
+
self.branch_creator = BranchCreator(self.git_utility)
|
|
30
|
+
|
|
31
|
+
def _cleanup_index_lock(self) -> None:
|
|
32
|
+
"""
|
|
33
|
+
检查并清理可能存在的 index.lock 文件
|
|
34
|
+
在 Windows 上,Git 操作可能会留下未及时删除的 index.lock 文件
|
|
35
|
+
"""
|
|
36
|
+
result = self.git_utility._run_git_command(
|
|
37
|
+
["git", "rev-parse", "--git-dir"],
|
|
38
|
+
capture_output=True,
|
|
39
|
+
text=True,
|
|
40
|
+
check=False,
|
|
41
|
+
)
|
|
42
|
+
if result.returncode != 0:
|
|
43
|
+
return
|
|
44
|
+
git_dir = result.stdout.strip()
|
|
45
|
+
index_lock_path = os.path.join(git_dir, "index.lock")
|
|
46
|
+
if os.path.exists(index_lock_path):
|
|
47
|
+
if os.path.islink(index_lock_path):
|
|
48
|
+
log_info_pair(
|
|
49
|
+
"Warning: index.lock is a symbolic link, skipping removal",
|
|
50
|
+
"警告: index.lock 是符号链接,跳过删除",
|
|
51
|
+
)
|
|
52
|
+
return
|
|
53
|
+
if time.time() - os.path.getmtime(index_lock_path) < 5:
|
|
54
|
+
log_info_pair(
|
|
55
|
+
"Warning: index.lock was recently modified, skipping removal",
|
|
56
|
+
"警告: index.lock 最近被修改,跳过删除",
|
|
57
|
+
)
|
|
58
|
+
return
|
|
59
|
+
try:
|
|
60
|
+
os.remove(index_lock_path)
|
|
61
|
+
log_info_pair(
|
|
62
|
+
"✓ Cleaned up existing index.lock file",
|
|
63
|
+
"✓ 清理了存在的 index.lock 文件",
|
|
64
|
+
)
|
|
65
|
+
except OSError as e:
|
|
66
|
+
log_info_pair(
|
|
67
|
+
f"Warning: Failed to remove index.lock file: {e}",
|
|
68
|
+
f"警告: 无法删除 index.lock 文件: {e}",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def setup_git_flow_branches(self) -> None:
|
|
72
|
+
"""
|
|
73
|
+
设置 Git Flow 分支结构
|
|
74
|
+
|
|
75
|
+
创建主开发分支 (main)、开发分支 (develop)、功能分支 (features),
|
|
76
|
+
并确保所有分支都拉取最新代码。如果分支已存在,则直接切换到该分支。
|
|
77
|
+
"""
|
|
78
|
+
if self.skip_remote_ops:
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
log_info_pair("Setting up Git Flow branches...", "设置 Git Flow 分支结构...")
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
# 检查并清理可能存在的 index.lock 文件
|
|
85
|
+
self._cleanup_index_lock()
|
|
86
|
+
|
|
87
|
+
# Check if we're in a git repository
|
|
88
|
+
if not self.git_utility.is_git_repository():
|
|
89
|
+
log_info_pair(
|
|
90
|
+
"Warning: Not inside a git repository", "警告: 不在 git 仓库内"
|
|
91
|
+
)
|
|
92
|
+
return
|
|
93
|
+
|
|
94
|
+
# 设置初始分支(main 和 develop)
|
|
95
|
+
self.branch_creator.setup_initial_branches()
|
|
96
|
+
|
|
97
|
+
# 检查并清理可能存在的 index.lock 文件
|
|
98
|
+
self._cleanup_index_lock()
|
|
99
|
+
|
|
100
|
+
# 检查并创建 features 分支(默认快速开发分支)
|
|
101
|
+
log_info_pair("Creating features branch...", "创建 features 分支...")
|
|
102
|
+
|
|
103
|
+
if not self.git_utility.branch_exists("features"):
|
|
104
|
+
# 直接创建 features 分支,不依赖 develop 分支的提交
|
|
105
|
+
self.git_utility.create_and_checkout_branch("features")
|
|
106
|
+
log_info_pair("✓ features branch created", "✓ features 分支创建成功")
|
|
107
|
+
else:
|
|
108
|
+
log_info_pair("features branch already exists", "features 分支已存在")
|
|
109
|
+
# 确保我们在 features 分支上
|
|
110
|
+
self.git_utility.checkout_branch("features")
|
|
111
|
+
|
|
112
|
+
log_info_pair("Git Flow branches setup completed", "Git Flow 分支设置完成")
|
|
113
|
+
except Exception as e:
|
|
114
|
+
log_info_pair(
|
|
115
|
+
f"Warning: Failed to set up Git Flow branches: {e}",
|
|
116
|
+
f"警告: 设置 Git Flow 分支失败: {e}",
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
def setup_git_flow_branches_by_work_type(self, work_type: str) -> None:
|
|
120
|
+
"""
|
|
121
|
+
根据工作类型设置 Git Flow 分支
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
work_type: 工作类型 (feature, fix, hotfix, release)
|
|
125
|
+
"""
|
|
126
|
+
# Define branch sources based on work type
|
|
127
|
+
branch_sources = {
|
|
128
|
+
"feature": "develop",
|
|
129
|
+
"features": "develop",
|
|
130
|
+
"fix": "develop",
|
|
131
|
+
"hotfix": "main",
|
|
132
|
+
"release": "develop",
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
# Validate work type
|
|
136
|
+
if work_type not in branch_sources:
|
|
137
|
+
from ...common.exceptions import GitInitError
|
|
138
|
+
|
|
139
|
+
raise GitInitError(
|
|
140
|
+
f"Invalid work type: {work_type}. Valid types are: {', '.join(branch_sources.keys())}"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Get the source branch for the current work type
|
|
144
|
+
source_branch = branch_sources[work_type]
|
|
145
|
+
|
|
146
|
+
# Check and pull main branch
|
|
147
|
+
if work_type in ["feature", "features", "fix", "release"]:
|
|
148
|
+
self.branch_creator.check_and_pull_branch("main")
|
|
149
|
+
|
|
150
|
+
# Check and pull develop branch
|
|
151
|
+
if work_type in ["feature", "features", "fix", "release"]:
|
|
152
|
+
self.branch_creator.check_and_pull_branch("develop")
|
|
153
|
+
|
|
154
|
+
# Check and pull source branch (if it's different from main/develop) or for hotfix
|
|
155
|
+
if source_branch not in ["main", "develop"] or work_type == "hotfix":
|
|
156
|
+
self.branch_creator.check_and_pull_branch(source_branch)
|
|
157
|
+
|
|
158
|
+
# Create the new branch based on work type
|
|
159
|
+
self.branch_creator.create_git_flow_branch(work_type, source_branch)
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"""Git LFS manager utility."""
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
import subprocess
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from wl_cli.utils.logging import log_error_pair, log_info_pair, log_warning_pair
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class GitLFSManager:
|
|
11
|
+
"""Git LFS manager for initializing and configuring Git LFS."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, env: dict[str, str]) -> None:
|
|
14
|
+
"""Initialize Git LFS manager.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
env: Environment variables for subprocess calls.
|
|
18
|
+
"""
|
|
19
|
+
self.env = env
|
|
20
|
+
# 常见的3D模型文件类型
|
|
21
|
+
self.three_d_file_types = [
|
|
22
|
+
# SolidWorks
|
|
23
|
+
"*.sldprt",
|
|
24
|
+
"*.sldasm",
|
|
25
|
+
"*.slddrw",
|
|
26
|
+
# CAD
|
|
27
|
+
"*.dwg",
|
|
28
|
+
"*.dxf",
|
|
29
|
+
# 3D交换格式
|
|
30
|
+
"*.iges",
|
|
31
|
+
"*.igs",
|
|
32
|
+
"*.step",
|
|
33
|
+
"*.stp",
|
|
34
|
+
# 3D打印格式
|
|
35
|
+
"*.stl",
|
|
36
|
+
# 其他常见的3D模型格式
|
|
37
|
+
"*.obj",
|
|
38
|
+
"*.fbx",
|
|
39
|
+
"*.blend",
|
|
40
|
+
"*.3ds",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
def _check_git_lfs_installed(self) -> bool:
|
|
44
|
+
"""检查Git LFS是否已安装.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
True if Git LFS is installed, False otherwise.
|
|
48
|
+
"""
|
|
49
|
+
try:
|
|
50
|
+
result = subprocess.run(
|
|
51
|
+
["git", "lfs", "version"],
|
|
52
|
+
check=True,
|
|
53
|
+
capture_output=True,
|
|
54
|
+
env=self.env,
|
|
55
|
+
text=True,
|
|
56
|
+
)
|
|
57
|
+
log_info_pair(
|
|
58
|
+
f"✓ Git LFS is installed: {result.stdout.strip()}",
|
|
59
|
+
f"✓ Git LFS 已安装: {result.stdout.strip()}",
|
|
60
|
+
)
|
|
61
|
+
return True
|
|
62
|
+
except subprocess.CalledProcessError:
|
|
63
|
+
log_error_pair("✗ Git LFS is not installed", "✗ Git LFS 未安装")
|
|
64
|
+
log_error_pair(
|
|
65
|
+
"Please install Git LFS from https://git-lfs.com/",
|
|
66
|
+
"请从 https://git-lfs.com/ 安装 Git LFS",
|
|
67
|
+
)
|
|
68
|
+
return False
|
|
69
|
+
except FileNotFoundError:
|
|
70
|
+
log_error_pair("✗ Git is not installed", "✗ Git 未安装")
|
|
71
|
+
return False
|
|
72
|
+
|
|
73
|
+
def _install_git_lfs(self) -> bool:
|
|
74
|
+
"""安装Git LFS.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
True if Git LFS is installed successfully, False otherwise.
|
|
78
|
+
"""
|
|
79
|
+
try:
|
|
80
|
+
log_info_pair("Initializing Git LFS...", "初始化 Git LFS...")
|
|
81
|
+
subprocess.run(
|
|
82
|
+
["git", "lfs", "install", "--force"],
|
|
83
|
+
check=True,
|
|
84
|
+
capture_output=False,
|
|
85
|
+
env=self.env,
|
|
86
|
+
)
|
|
87
|
+
log_info_pair("✓ Git LFS initialized", "✓ Git LFS 初始化完成")
|
|
88
|
+
return True
|
|
89
|
+
except subprocess.CalledProcessError as e:
|
|
90
|
+
log_error_pair(
|
|
91
|
+
f"✗ Failed to initialize Git LFS: {e}", f"✗ 初始化 Git LFS 失败: {e}"
|
|
92
|
+
)
|
|
93
|
+
return False
|
|
94
|
+
|
|
95
|
+
def _configure_lfs_tracking(self) -> None:
|
|
96
|
+
"""配置Git LFS跟踪3D模型文件类型."""
|
|
97
|
+
log_info_pair(
|
|
98
|
+
"Configuring Git LFS to track 3D model files...",
|
|
99
|
+
"配置 Git LFS 跟踪3D模型文件...",
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# 跟踪所有3D模型文件类型
|
|
103
|
+
for file_type in self.three_d_file_types:
|
|
104
|
+
try:
|
|
105
|
+
subprocess.run(
|
|
106
|
+
["git", "lfs", "track", file_type],
|
|
107
|
+
check=True,
|
|
108
|
+
capture_output=False,
|
|
109
|
+
env=self.env,
|
|
110
|
+
)
|
|
111
|
+
log_info_pair(f"✓ Tracking {file_type}", f"✓ 跟踪 {file_type}")
|
|
112
|
+
except subprocess.CalledProcessError as e:
|
|
113
|
+
log_error_pair(
|
|
114
|
+
f"✗ Failed to track {file_type}: {e}",
|
|
115
|
+
f"✗ 跟踪 {file_type} 失败: {e}",
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# 确保.gitattributes文件被添加到Git
|
|
119
|
+
if Path(".gitattributes").exists():
|
|
120
|
+
try:
|
|
121
|
+
subprocess.run(
|
|
122
|
+
["git", "add", ".gitattributes"],
|
|
123
|
+
check=True,
|
|
124
|
+
capture_output=False,
|
|
125
|
+
env=self.env,
|
|
126
|
+
)
|
|
127
|
+
log_info_pair(
|
|
128
|
+
"✓ Added .gitattributes to Git", "✓ 将 .gitattributes 添加到 Git"
|
|
129
|
+
)
|
|
130
|
+
except subprocess.CalledProcessError as e:
|
|
131
|
+
log_error_pair(
|
|
132
|
+
f"✗ Failed to add .gitattributes to Git: {e}",
|
|
133
|
+
f"✗ 将 .gitattributes 添加到 Git 失败: {e}",
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
def _check_nested_git(self, auto_fix: bool = False) -> bool:
|
|
137
|
+
"""检查项目中是否存在嵌套的.git目录,并可选择自动修复
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
auto_fix: 是否自动移除嵌套的.git目录
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
True if no nested .git directories are found or all were successfully fixed,
|
|
144
|
+
False otherwise.
|
|
145
|
+
"""
|
|
146
|
+
# 获取当前目录
|
|
147
|
+
current_dir = Path.cwd()
|
|
148
|
+
nested_git_dirs = []
|
|
149
|
+
|
|
150
|
+
# 查找所有嵌套的.git目录(不包括根目录的.git)
|
|
151
|
+
for git_dir in current_dir.glob("**/.git"):
|
|
152
|
+
# 跳过根目录的.git文件夹
|
|
153
|
+
if git_dir.parent == current_dir:
|
|
154
|
+
continue
|
|
155
|
+
|
|
156
|
+
# 检查是否是目录
|
|
157
|
+
if git_dir.is_dir():
|
|
158
|
+
nested_git_dirs.append(git_dir)
|
|
159
|
+
|
|
160
|
+
# 如果找到嵌套的.git目录,记录警告并可选择自动修复
|
|
161
|
+
if nested_git_dirs:
|
|
162
|
+
log_warning_pair(
|
|
163
|
+
"Found nested .git directories, which may cause Git operations to fail:",
|
|
164
|
+
"发现嵌套的.git目录,这可能导致Git操作失败:",
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
for git_dir in nested_git_dirs:
|
|
168
|
+
# Language-neutral payload (a path): both variants are identical by design.
|
|
169
|
+
log_warning_pair(f" - {git_dir}", f" - {git_dir}")
|
|
170
|
+
|
|
171
|
+
# 自动修复逻辑
|
|
172
|
+
if auto_fix:
|
|
173
|
+
log_info_pair(
|
|
174
|
+
"\nAutomatically removing nested .git directories...",
|
|
175
|
+
"\n正在自动移除嵌套的.git目录...",
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
all_removed = True
|
|
179
|
+
for git_dir in nested_git_dirs:
|
|
180
|
+
try:
|
|
181
|
+
shutil.rmtree(git_dir)
|
|
182
|
+
log_info_pair(
|
|
183
|
+
f"✓ Successfully removed: {git_dir}",
|
|
184
|
+
f"✓ 成功移除: {git_dir}",
|
|
185
|
+
)
|
|
186
|
+
except Exception as e:
|
|
187
|
+
log_error_pair(
|
|
188
|
+
f"✗ Failed to remove {git_dir}: {e}",
|
|
189
|
+
f"✗ 移除失败 {git_dir}: {e}",
|
|
190
|
+
)
|
|
191
|
+
all_removed = False
|
|
192
|
+
|
|
193
|
+
return all_removed
|
|
194
|
+
else:
|
|
195
|
+
log_warning_pair(
|
|
196
|
+
"\nConsider removing these nested .git directories to avoid Git operation problems.",
|
|
197
|
+
"\n建议移除这些嵌套的.git目录以避免Git操作问题。",
|
|
198
|
+
)
|
|
199
|
+
log_warning_pair(
|
|
200
|
+
"Use the --fix option to automatically remove these nested .git directories.",
|
|
201
|
+
"使用 --fix 选项可以自动移除这些嵌套的.git目录。",
|
|
202
|
+
)
|
|
203
|
+
return False
|
|
204
|
+
|
|
205
|
+
return True
|
|
206
|
+
|
|
207
|
+
def _check_git_repo(self) -> bool:
|
|
208
|
+
"""检查当前目录是否是Git仓库.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
True if current directory is a Git repository, False otherwise.
|
|
212
|
+
"""
|
|
213
|
+
git_dir = Path(".git")
|
|
214
|
+
if git_dir.is_dir():
|
|
215
|
+
log_info_pair(
|
|
216
|
+
"✓ Current directory is a Git repository", "✓ 当前目录是Git仓库"
|
|
217
|
+
)
|
|
218
|
+
return True
|
|
219
|
+
else:
|
|
220
|
+
log_error_pair(
|
|
221
|
+
"✗ Current directory is not a Git repository", "✗ 当前目录不是Git仓库"
|
|
222
|
+
)
|
|
223
|
+
log_error_pair(
|
|
224
|
+
"Please initialize Git repository first with: git init",
|
|
225
|
+
"请先使用 git init 初始化Git仓库",
|
|
226
|
+
)
|
|
227
|
+
return False
|
|
228
|
+
|
|
229
|
+
def initialize(self, auto_fix: bool = False) -> None:
|
|
230
|
+
"""初始化Git LFS.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
auto_fix: 是否自动移除嵌套的.git目录
|
|
234
|
+
|
|
235
|
+
Raises:
|
|
236
|
+
Exception: If Git LFS initialization fails.
|
|
237
|
+
"""
|
|
238
|
+
# 检查当前目录是否是Git仓库
|
|
239
|
+
if not self._check_git_repo():
|
|
240
|
+
raise RuntimeError("Not a Git repository. Please run 'git init' first.")
|
|
241
|
+
|
|
242
|
+
# 检查是否存在嵌套的.git目录
|
|
243
|
+
self._check_nested_git(auto_fix)
|
|
244
|
+
|
|
245
|
+
# 检查Git LFS是否已安装
|
|
246
|
+
if not self._check_git_lfs_installed():
|
|
247
|
+
raise RuntimeError(
|
|
248
|
+
"Git LFS is not installed. Please install Git LFS first."
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
# 安装Git LFS
|
|
252
|
+
if not self._install_git_lfs():
|
|
253
|
+
raise RuntimeError("Failed to initialize Git LFS.")
|
|
254
|
+
|
|
255
|
+
# 配置Git LFS跟踪3D模型文件类型
|
|
256
|
+
self._configure_lfs_tracking()
|
|
257
|
+
|
|
258
|
+
log_info_pair(
|
|
259
|
+
"\n✓ Git LFS initialization completed successfully!",
|
|
260
|
+
"✓ Git LFS 初始化成功完成!",
|
|
261
|
+
)
|
|
262
|
+
log_info_pair(
|
|
263
|
+
"\nGit LFS is now configured to track the following file types:",
|
|
264
|
+
"Git LFS 现已配置为跟踪以下文件类型:",
|
|
265
|
+
)
|
|
266
|
+
for file_type in self.three_d_file_types:
|
|
267
|
+
# Language-neutral payload (a glob): both variants are identical by design.
|
|
268
|
+
log_info_pair(f" - {file_type}", f" - {file_type}")
|
|
269
|
+
log_info_pair(
|
|
270
|
+
"\nYou can now use Git commands as usual to manage your 3D model files.",
|
|
271
|
+
"现在您可以像往常一样使用 Git 命令来管理您的 3D 模型文件。",
|
|
272
|
+
)
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"""Git 工具类,封装基础的 Git 操作"""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
import time
|
|
5
|
+
from typing import cast
|
|
6
|
+
|
|
7
|
+
from wl_cli.utils.logging import log_info
|
|
8
|
+
from wl_cli.utils.subprocess_utils import SubprocessExecutor
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class GitUtility:
|
|
12
|
+
"""提供基础的 Git 操作功能"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, env: dict[str, str]):
|
|
15
|
+
"""
|
|
16
|
+
初始化 GitUtility 类
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
env: 执行 Git 命令时使用的环境变量
|
|
20
|
+
"""
|
|
21
|
+
self.env = env
|
|
22
|
+
self.max_retries = 3
|
|
23
|
+
self.retry_delay = 1.0
|
|
24
|
+
self._executor = SubprocessExecutor()
|
|
25
|
+
|
|
26
|
+
def _run_git_command(
|
|
27
|
+
self, command, check=False, capture_output=False, text=True
|
|
28
|
+
) -> subprocess.CompletedProcess[str]:
|
|
29
|
+
"""
|
|
30
|
+
执行 Git 命令并处理 index.lock 错误,包含重试机制
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
command: 要执行的 Git 命令
|
|
34
|
+
check: 是否检查返回码
|
|
35
|
+
capture_output: 是否捕获输出
|
|
36
|
+
text: 是否返回文本输出
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
subprocess.CompletedProcess: 命令执行结果
|
|
40
|
+
"""
|
|
41
|
+
for attempt in range(self.max_retries):
|
|
42
|
+
try:
|
|
43
|
+
prepared_env = self._executor._prepare_environment(self.env)
|
|
44
|
+
return cast(
|
|
45
|
+
"subprocess.CompletedProcess[str]",
|
|
46
|
+
subprocess.run(
|
|
47
|
+
command,
|
|
48
|
+
check=check,
|
|
49
|
+
capture_output=capture_output,
|
|
50
|
+
text=text,
|
|
51
|
+
env=prepared_env,
|
|
52
|
+
),
|
|
53
|
+
)
|
|
54
|
+
except subprocess.CalledProcessError as e:
|
|
55
|
+
# 检查是否是 index.lock 错误
|
|
56
|
+
output = (e.stdout or "") + (e.stderr or "")
|
|
57
|
+
if (
|
|
58
|
+
"index.lock" in str(output)
|
|
59
|
+
if isinstance(output, str)
|
|
60
|
+
else b"index.lock" in output
|
|
61
|
+
) or "index.lock" in str(e):
|
|
62
|
+
if attempt < self.max_retries - 1:
|
|
63
|
+
# 等待一段时间后重试
|
|
64
|
+
time.sleep(self.retry_delay)
|
|
65
|
+
continue
|
|
66
|
+
raise
|
|
67
|
+
except Exception as e:
|
|
68
|
+
# 检查是否是 index.lock 相关的文件操作错误
|
|
69
|
+
if "index.lock" in str(e):
|
|
70
|
+
if attempt < self.max_retries - 1:
|
|
71
|
+
# 等待一段时间后重试
|
|
72
|
+
time.sleep(self.retry_delay)
|
|
73
|
+
continue
|
|
74
|
+
raise
|
|
75
|
+
# 如果所有重试都失败,再次执行命令让它抛出原始错误
|
|
76
|
+
prepared_env = self._executor._prepare_environment(self.env)
|
|
77
|
+
return cast(
|
|
78
|
+
"subprocess.CompletedProcess[str]",
|
|
79
|
+
subprocess.run(
|
|
80
|
+
command,
|
|
81
|
+
check=check,
|
|
82
|
+
capture_output=capture_output,
|
|
83
|
+
text=text,
|
|
84
|
+
env=prepared_env,
|
|
85
|
+
),
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def run_git_command(
|
|
89
|
+
self,
|
|
90
|
+
cmd: list[str],
|
|
91
|
+
description: str = "",
|
|
92
|
+
max_retries: int | None = None,
|
|
93
|
+
) -> bool:
|
|
94
|
+
retries = max_retries if max_retries is not None else self.max_retries
|
|
95
|
+
desc = description or " ".join(cmd)
|
|
96
|
+
log_info(f"执行 Git 命令: {desc}")
|
|
97
|
+
for attempt in range(retries):
|
|
98
|
+
try:
|
|
99
|
+
self._run_git_command(cmd, check=True, capture_output=True, text=True)
|
|
100
|
+
log_info(f"Git 命令执行成功: {desc}")
|
|
101
|
+
return True
|
|
102
|
+
except FileNotFoundError:
|
|
103
|
+
log_info(f"Git 命令未找到: {desc}")
|
|
104
|
+
return False
|
|
105
|
+
except PermissionError:
|
|
106
|
+
log_info(f"Git 命令权限不足: {desc}")
|
|
107
|
+
return False
|
|
108
|
+
except subprocess.CalledProcessError:
|
|
109
|
+
if attempt < retries - 1:
|
|
110
|
+
time.sleep(self.retry_delay)
|
|
111
|
+
continue
|
|
112
|
+
log_info(f"Git 命令执行失败: {desc}")
|
|
113
|
+
return False
|
|
114
|
+
return False
|
|
115
|
+
|
|
116
|
+
def is_git_repository(self) -> bool:
|
|
117
|
+
"""
|
|
118
|
+
检查当前目录是否在 Git 仓库内
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
bool: 如果在 Git 仓库内返回 True,否则返回 False
|
|
122
|
+
"""
|
|
123
|
+
result = self._run_git_command(
|
|
124
|
+
["git", "rev-parse", "--is-inside-work-tree"],
|
|
125
|
+
capture_output=True,
|
|
126
|
+
text=True,
|
|
127
|
+
check=False,
|
|
128
|
+
)
|
|
129
|
+
return result.returncode == 0
|
|
130
|
+
|
|
131
|
+
def get_current_branch(self) -> str:
|
|
132
|
+
"""
|
|
133
|
+
获取当前分支名称
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
str: 当前分支名称
|
|
137
|
+
"""
|
|
138
|
+
result = self._run_git_command(
|
|
139
|
+
["git", "rev-parse", "--abbrev-ref", "HEAD"],
|
|
140
|
+
capture_output=True,
|
|
141
|
+
text=True,
|
|
142
|
+
check=False,
|
|
143
|
+
)
|
|
144
|
+
return result.stdout.strip()
|
|
145
|
+
|
|
146
|
+
def branch_exists(self, branch_name: str) -> bool:
|
|
147
|
+
"""
|
|
148
|
+
检查指定分支是否存在
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
branch_name: 要检查的分支名称
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
bool: 如果分支存在返回 True,否则返回 False
|
|
155
|
+
"""
|
|
156
|
+
result = self._run_git_command(
|
|
157
|
+
["git", "branch", "--list", branch_name],
|
|
158
|
+
capture_output=True,
|
|
159
|
+
text=True,
|
|
160
|
+
check=False,
|
|
161
|
+
)
|
|
162
|
+
for line in result.stdout.strip().splitlines():
|
|
163
|
+
cleaned = line.strip().lstrip("* ")
|
|
164
|
+
if cleaned == branch_name:
|
|
165
|
+
return True
|
|
166
|
+
return False
|
|
167
|
+
|
|
168
|
+
def checkout_branch(self, branch_name: str) -> None:
|
|
169
|
+
"""
|
|
170
|
+
切换到指定分支
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
branch_name: 要切换到的分支名称
|
|
174
|
+
"""
|
|
175
|
+
self._run_git_command(
|
|
176
|
+
["git", "checkout", branch_name],
|
|
177
|
+
check=False,
|
|
178
|
+
capture_output=False,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
def create_and_checkout_branch(
|
|
182
|
+
self, branch_name: str, source_branch: str | None = None
|
|
183
|
+
) -> None:
|
|
184
|
+
"""
|
|
185
|
+
创建并切换到新分支
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
branch_name: 新分支名称
|
|
189
|
+
source_branch: 源分支名称(可选)
|
|
190
|
+
"""
|
|
191
|
+
if source_branch:
|
|
192
|
+
self._run_git_command(
|
|
193
|
+
["git", "checkout", "-b", branch_name, source_branch],
|
|
194
|
+
check=False,
|
|
195
|
+
capture_output=False,
|
|
196
|
+
)
|
|
197
|
+
else:
|
|
198
|
+
self._run_git_command(
|
|
199
|
+
["git", "checkout", "-b", branch_name],
|
|
200
|
+
check=False,
|
|
201
|
+
capture_output=False,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
def pull_branch(self, branch_name: str, remote: str = "origin") -> None:
|
|
205
|
+
"""
|
|
206
|
+
拉取指定分支的最新更改
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
branch_name: 分支名称
|
|
210
|
+
remote: 远程仓库名称(默认: origin)
|
|
211
|
+
"""
|
|
212
|
+
self._run_git_command(
|
|
213
|
+
["git", "pull", remote, branch_name],
|
|
214
|
+
check=True,
|
|
215
|
+
capture_output=False,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
def create_branch_from_remote(
|
|
219
|
+
self, branch_name: str, remote: str = "origin"
|
|
220
|
+
) -> None:
|
|
221
|
+
"""
|
|
222
|
+
从远程创建本地分支
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
branch_name: 分支名称
|
|
226
|
+
remote: 远程仓库名称(默认: origin)
|
|
227
|
+
"""
|
|
228
|
+
self._run_git_command(
|
|
229
|
+
["git", "checkout", "-b", branch_name, f"{remote}/{branch_name}"],
|
|
230
|
+
check=True,
|
|
231
|
+
capture_output=False,
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
def local_branch_exists(self, branch_name: str) -> bool:
|
|
235
|
+
"""
|
|
236
|
+
检查本地分支是否存在
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
branch_name: 分支名称
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
bool: 如果本地分支存在返回 True,否则返回 False
|
|
243
|
+
"""
|
|
244
|
+
result = self._run_git_command(
|
|
245
|
+
["git", "branch", "--list", branch_name],
|
|
246
|
+
check=False,
|
|
247
|
+
capture_output=True,
|
|
248
|
+
text=True,
|
|
249
|
+
)
|
|
250
|
+
for line in result.stdout.strip().splitlines():
|
|
251
|
+
cleaned = line.strip().lstrip("* ")
|
|
252
|
+
if cleaned == branch_name:
|
|
253
|
+
return True
|
|
254
|
+
return False
|