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.
Files changed (320) hide show
  1. py_wlcli-0.8.0.dist-info/METADATA +139 -0
  2. py_wlcli-0.8.0.dist-info/RECORD +320 -0
  3. py_wlcli-0.8.0.dist-info/WHEEL +4 -0
  4. py_wlcli-0.8.0.dist-info/entry_points.txt +2 -0
  5. py_wlcli-0.8.0.dist-info/licenses/LICENSE +21 -0
  6. wl_cli/__init__.py +27 -0
  7. wl_cli/__main__.py +130 -0
  8. wl_cli/capabilities.py +79 -0
  9. wl_cli/cc/__init__.py +5 -0
  10. wl_cli/cc/checks.py +1019 -0
  11. wl_cli/cc/cli.py +370 -0
  12. wl_cli/cc/mutate.py +1350 -0
  13. wl_cli/cli_click.py +156 -0
  14. wl_cli/commands/__init__.py +84 -0
  15. wl_cli/commands/base.py +80 -0
  16. wl_cli/commands/buildcommands/__init__.py +1 -0
  17. wl_cli/commands/buildcommands/build.py +71 -0
  18. wl_cli/commands/buildcommands/build_utils.py +47 -0
  19. wl_cli/commands/buildcommands/builders/__init__.py +18 -0
  20. wl_cli/commands/buildcommands/builders/cleanup.py +35 -0
  21. wl_cli/commands/buildcommands/builders/coordinator.py +122 -0
  22. wl_cli/commands/buildcommands/builders/environment.py +81 -0
  23. wl_cli/commands/buildcommands/builders/rust_builder.py +39 -0
  24. wl_cli/commands/buildcommands/builders/stubs.py +58 -0
  25. wl_cli/commands/buildcommands/dist.py +326 -0
  26. wl_cli/commands/buildcommands/test.py +89 -0
  27. wl_cli/commands/clean/__init__.py +1 -0
  28. wl_cli/commands/clean/build.py +151 -0
  29. wl_cli/commands/clean/clean.py +294 -0
  30. wl_cli/commands/clean/clean_utils.py +77 -0
  31. wl_cli/commands/clean/core.py +236 -0
  32. wl_cli/commands/clean/dead_code/__init__.py +0 -0
  33. wl_cli/commands/clean/dead_code/dead_code_command.py +70 -0
  34. wl_cli/commands/clean/dead_code/detector.py +161 -0
  35. wl_cli/commands/clean/dead_code/fixer.py +176 -0
  36. wl_cli/commands/clean/dead_code/reporter.py +295 -0
  37. wl_cli/commands/clean/dead_code/utils.py +0 -0
  38. wl_cli/commands/clean/lfs.py +215 -0
  39. wl_cli/commands/clean/python.py +81 -0
  40. wl_cli/commands/clean/rust.py +91 -0
  41. wl_cli/commands/clean/utils/__init__.py +34 -0
  42. wl_cli/commands/clean/venv.py +91 -0
  43. wl_cli/commands/cleanup.py +73 -0
  44. wl_cli/commands/click_base.py +79 -0
  45. wl_cli/commands/config/__init__.py +1 -0
  46. wl_cli/commands/config/config.py +291 -0
  47. wl_cli/commands/debug/__init__.py +6 -0
  48. wl_cli/commands/debug/debug.py +309 -0
  49. wl_cli/commands/debug/debug_cache.py +86 -0
  50. wl_cli/commands/debug/debug_subagent.py +156 -0
  51. wl_cli/commands/debug/output_formatter.py +165 -0
  52. wl_cli/commands/debug/source_hasher.py +92 -0
  53. wl_cli/commands/format/__init__.py +13 -0
  54. wl_cli/commands/format/format_cache_adapter.py +125 -0
  55. wl_cli/commands/format/format_command.py +197 -0
  56. wl_cli/commands/format/format_coordinator.py +191 -0
  57. wl_cli/commands/format/format_executor.py +470 -0
  58. wl_cli/commands/format/format_outcome.py +44 -0
  59. wl_cli/commands/format/format_path_handler.py +1016 -0
  60. wl_cli/commands/format/format_progress.py +62 -0
  61. wl_cli/commands/format/format_reporter.py +31 -0
  62. wl_cli/commands/format/python_formatter.py +506 -0
  63. wl_cli/commands/format/rust_formatter.py +106 -0
  64. wl_cli/commands/gate/__init__.py +1 -0
  65. wl_cli/commands/gate/gate_command.py +862 -0
  66. wl_cli/commands/gate/preflight.py +445 -0
  67. wl_cli/commands/git/__init__.py +1 -0
  68. wl_cli/commands/git/config.py +169 -0
  69. wl_cli/commands/git/git_facts.py +60 -0
  70. wl_cli/commands/git/git_flow.py +86 -0
  71. wl_cli/commands/git/hook.py +112 -0
  72. wl_cli/commands/git/policy.py +216 -0
  73. wl_cli/commands/git/push_update.py +82 -0
  74. wl_cli/commands/initenv/__init__.py +1 -0
  75. wl_cli/commands/initenv/common/__init__.py +23 -0
  76. wl_cli/commands/initenv/common/check_git_bash.py +78 -0
  77. wl_cli/commands/initenv/common/error_handler.py +68 -0
  78. wl_cli/commands/initenv/common/exceptions.py +52 -0
  79. wl_cli/commands/initenv/components/__init__.py +4 -0
  80. wl_cli/commands/initenv/components/cargo_sync.py +240 -0
  81. wl_cli/commands/initenv/components/config_manager.py +106 -0
  82. wl_cli/commands/initenv/components/directory_utils.py +71 -0
  83. wl_cli/commands/initenv/components/git/__init__.py +9 -0
  84. wl_cli/commands/initenv/components/git/branch_creator.py +143 -0
  85. wl_cli/commands/initenv/components/git/git_flow_interface.py +24 -0
  86. wl_cli/commands/initenv/components/git/git_flow_manager.py +159 -0
  87. wl_cli/commands/initenv/components/git/git_lfs_manager.py +272 -0
  88. wl_cli/commands/initenv/components/git/git_utility.py +254 -0
  89. wl_cli/commands/initenv/components/git/gitignore_manager.py +53 -0
  90. wl_cli/commands/initenv/components/git_initializer.py +212 -0
  91. wl_cli/commands/initenv/components/i18n_manager.py +104 -0
  92. wl_cli/commands/initenv/components/log_manager.py +241 -0
  93. wl_cli/commands/initenv/components/project_structure/__init__.py +23 -0
  94. wl_cli/commands/initenv/components/project_structure/config_generator.py +360 -0
  95. wl_cli/commands/initenv/components/project_structure/directory_creator.py +184 -0
  96. wl_cli/commands/initenv/components/project_structure/hook_source.py +110 -0
  97. wl_cli/commands/initenv/components/project_structure/hooks_manager.py +995 -0
  98. wl_cli/commands/initenv/components/project_structure/readme_handler.py +56 -0
  99. wl_cli/commands/initenv/components/project_structure/rust_files_handler.py +30 -0
  100. wl_cli/commands/initenv/components/project_structure/setup_handler.py +103 -0
  101. wl_cli/commands/initenv/components/project_structure/sop_generator.py +73 -0
  102. wl_cli/commands/initenv/components/project_structure/template_copier.py +104 -0
  103. wl_cli/commands/initenv/components/project_structure/template_manager.py +195 -0
  104. wl_cli/commands/initenv/components/pyproject_generator.py +194 -0
  105. wl_cli/commands/initenv/components/rust_initializer.py +145 -0
  106. wl_cli/commands/initenv/components/symlink_cleaner.py +99 -0
  107. wl_cli/commands/initenv/components/symlink_manager.py +52 -0
  108. wl_cli/commands/initenv/components/venv_manager.py +118 -0
  109. wl_cli/commands/initenv/initenv.py +169 -0
  110. wl_cli/commands/initenv/services/__init__.py +11 -0
  111. wl_cli/commands/initenv/services/environment_initializer.py +288 -0
  112. wl_cli/commands/initenv/services/git_lfs_initializer.py +26 -0
  113. wl_cli/commands/initenv/services/project_structure_initializer.py +52 -0
  114. wl_cli/commands/lfs/README.md +221 -0
  115. wl_cli/commands/lfs/__init__.py +5 -0
  116. wl_cli/commands/lfs/cli.py +106 -0
  117. wl_cli/commands/lfs/config.py +164 -0
  118. wl_cli/commands/lfs/hook.py +61 -0
  119. wl_cli/commands/lfs/initializer.py +531 -0
  120. wl_cli/commands/lfs/manager.py +95 -0
  121. wl_cli/commands/lfs/policy.py +165 -0
  122. wl_cli/commands/lfs/watcher.py +294 -0
  123. wl_cli/commands/lint/__init__.py +1 -0
  124. wl_cli/commands/lint/lint_command.py +297 -0
  125. wl_cli/commands/lint/lint_executor.py +409 -0
  126. wl_cli/commands/lint/lint_reporter.py +579 -0
  127. wl_cli/commands/lint/lint_run.py +258 -0
  128. wl_cli/commands/lint/mypy_config.py +90 -0
  129. wl_cli/commands/registry.py +103 -0
  130. wl_cli/commands/self/__init__.py +7 -0
  131. wl_cli/commands/self/common/__init__.py +8 -0
  132. wl_cli/commands/self/common/capabilities.py +30 -0
  133. wl_cli/commands/self/common/config.py +27 -0
  134. wl_cli/commands/self/common/version_cache.py +85 -0
  135. wl_cli/commands/self/handlers/__init__.py +19 -0
  136. wl_cli/commands/self/handlers/base_handler.py +28 -0
  137. wl_cli/commands/self/handlers/install_handler.py +85 -0
  138. wl_cli/commands/self/handlers/status_handler.py +66 -0
  139. wl_cli/commands/self/handlers/uninstall_handler.py +58 -0
  140. wl_cli/commands/self/handlers/update_handler.py +102 -0
  141. wl_cli/commands/self/handlers/version_handler.py +16 -0
  142. wl_cli/commands/self/self_command.py +237 -0
  143. wl_cli/commands/self/services/__init__.py +15 -0
  144. wl_cli/commands/self/services/installation_manager.py +187 -0
  145. wl_cli/commands/self/services/local_installer.py +165 -0
  146. wl_cli/commands/self/services/pypi_updater.py +278 -0
  147. wl_cli/commands/self/services/windows_update.py +174 -0
  148. wl_cli/commands/testcommands/__init__.py +8 -0
  149. wl_cli/commands/testcommands/base_dependency_analyzer.py +198 -0
  150. wl_cli/commands/testcommands/batch_processor.py +278 -0
  151. wl_cli/commands/testcommands/cache_manager.py +1156 -0
  152. wl_cli/commands/testcommands/coverage_analyzer.py +955 -0
  153. wl_cli/commands/testcommands/coverage_cache_manager.py +183 -0
  154. wl_cli/commands/testcommands/progress_manager.py +79 -0
  155. wl_cli/commands/testcommands/python_env_detector.py +207 -0
  156. wl_cli/commands/testcommands/report_generator.py +101 -0
  157. wl_cli/commands/testcommands/single_test_executor.py +250 -0
  158. wl_cli/commands/testcommands/speed_checker.py +118 -0
  159. wl_cli/commands/testcommands/test_command_builder.py +165 -0
  160. wl_cli/commands/testcommands/test_commands.py +786 -0
  161. wl_cli/commands/testcommands/test_reporter.py +181 -0
  162. wl_cli/commands/testcommands/test_result_handler.py +96 -0
  163. wl_cli/commands/testcommands/test_runner.py +1343 -0
  164. wl_cli/commands/testcommands/test_utils.py +197 -0
  165. wl_cli/commands/testcommands/utils/__init__.py +17 -0
  166. wl_cli/commands/testcommands/utils/coverage_parser.py +98 -0
  167. wl_cli/commands/testcommands/utils/env_info.py +36 -0
  168. wl_cli/commands/testcommands/utils/junit_parser.py +75 -0
  169. wl_cli/commands/testcommands/utils/markdown_utils.py +20 -0
  170. wl_cli/commands/testcommands/utils/project_root.py +61 -0
  171. wl_cli/commands/update/__init__.py +9 -0
  172. wl_cli/commands/update/services/__init__.py +21 -0
  173. wl_cli/commands/update/services/backup_manager.py +207 -0
  174. wl_cli/commands/update/services/component_manager.py +83 -0
  175. wl_cli/commands/update/services/conflict_resolver.py +189 -0
  176. wl_cli/commands/update/services/vendors_updater.py +85 -0
  177. wl_cli/commands/update/services/version_tracker.py +127 -0
  178. wl_cli/commands/update/update_command.py +270 -0
  179. wl_cli/commands/update/utils/__init__.py +3 -0
  180. wl_cli/commands/version/__init__.py +9 -0
  181. wl_cli/commands/version/version_command.py +155 -0
  182. wl_cli/commands/workflow.py +477 -0
  183. wl_cli/env/__init__.py +5 -0
  184. wl_cli/env/cli.py +167 -0
  185. wl_cli/env_status.py +157 -0
  186. wl_cli/envelope.py +91 -0
  187. wl_cli/exceptions.py +85 -0
  188. wl_cli/lib/__init__.py +3 -0
  189. wl_cli/lib/rust_utils.py +35 -0
  190. wl_cli/mcp/__init__.py +6 -0
  191. wl_cli/mcp/capabilities.py +19 -0
  192. wl_cli/mcp/cli.py +22 -0
  193. wl_cli/mcp/gitlab.py +553 -0
  194. wl_cli/mcp/k8s.py +344 -0
  195. wl_cli/mcp/protocol.py +159 -0
  196. wl_cli/platform.py +139 -0
  197. wl_cli/quality/__init__.py +18 -0
  198. wl_cli/utils/__init__.py +7 -0
  199. wl_cli/utils/cache.py +491 -0
  200. wl_cli/utils/cache_ledger.py +355 -0
  201. wl_cli/utils/cache_store.py +546 -0
  202. wl_cli/utils/config/__init__.py +27 -0
  203. wl_cli/utils/config/config_loader.py +163 -0
  204. wl_cli/utils/config/core.py +862 -0
  205. wl_cli/utils/config/defaults.py +142 -0
  206. wl_cli/utils/config/env_exporter.py +86 -0
  207. wl_cli/utils/config/env_parser.py +108 -0
  208. wl_cli/utils/config/validation.py +280 -0
  209. wl_cli/utils/error_handler.py +165 -0
  210. wl_cli/utils/file_copier.py +56 -0
  211. wl_cli/utils/file_operations.py +506 -0
  212. wl_cli/utils/language_utils.py +122 -0
  213. wl_cli/utils/lazy_import.py +240 -0
  214. wl_cli/utils/log_levels.py +35 -0
  215. wl_cli/utils/log_rotators.py +74 -0
  216. wl_cli/utils/logging.py +458 -0
  217. wl_cli/utils/output_context.py +142 -0
  218. wl_cli/utils/performance/__init__.py +1 -0
  219. wl_cli/utils/performance/decorators.py +111 -0
  220. wl_cli/utils/performance/exporter.py +38 -0
  221. wl_cli/utils/performance/models.py +16 -0
  222. wl_cli/utils/performance/monitor.py +161 -0
  223. wl_cli/utils/performance_monitor.py +32 -0
  224. wl_cli/utils/platform_adapter.py +131 -0
  225. wl_cli/utils/pre_commit.py +266 -0
  226. wl_cli/utils/pre_commit_config.py +72 -0
  227. wl_cli/utils/root.py +521 -0
  228. wl_cli/utils/root_fallback.py +256 -0
  229. wl_cli/utils/run_evidence.py +236 -0
  230. wl_cli/utils/structured_logger.py +18 -0
  231. wl_cli/utils/structured_logging/__init__.py +19 -0
  232. wl_cli/utils/structured_logging/config.py +337 -0
  233. wl_cli/utils/structured_logging/core.py +177 -0
  234. wl_cli/utils/structured_logging/env.py +56 -0
  235. wl_cli/utils/structured_logging/filtering.py +75 -0
  236. wl_cli/utils/structured_logging/filters.py +55 -0
  237. wl_cli/utils/structured_logging/formatters.py +251 -0
  238. wl_cli/utils/structured_logging/handlers.py +308 -0
  239. wl_cli/utils/structured_logging/logger.py +354 -0
  240. wl_cli/utils/subprocess.py +71 -0
  241. wl_cli/utils/subprocess_utils.py +260 -0
  242. wl_cli/utils/test_utils/__init__.py +5 -0
  243. wl_cli/utils/test_utils/test_filter.py +36 -0
  244. wl_cli/utils/uv_run.py +79 -0
  245. wl_cli/utils/uv_tool.py +117 -0
  246. wl_cli/utils/uv_tree.py +71 -0
  247. wl_cli/utils/uv_workspace.py +174 -0
  248. wl_cli/utils/version/__init__.py +15 -0
  249. wl_cli/utils/version/pypi_version_checker.py +95 -0
  250. wl_cli/utils/version/version_comparator.py +133 -0
  251. wl_cli/utils/version/version_detectors.py +312 -0
  252. wl_cli/utils/version/version_service.py +131 -0
  253. wl_cli/utils/version/version_updater.py +198 -0
  254. wl_cli/utils/wl_dir_updater.py +1440 -0
  255. wl_cli/utils/workspace_detector/__init__.py +28 -0
  256. wl_cli/utils/workspace_detector/core.py +161 -0
  257. wl_cli/utils/workspace_detector/detection_strategies.py +107 -0
  258. wl_cli/utils/workspace_detector/exceptions.py +5 -0
  259. wl_cli/utils/workspace_detector/types.py +17 -0
  260. wl_cli/utils/workspace_detector/venv_resolver.py +84 -0
  261. wl_cli/vendors/config/.codespellrc +2 -0
  262. wl_cli/vendors/config/dependencies.toml +10 -0
  263. wl_cli/vendors/config/mypy.ini +62 -0
  264. wl_cli/vendors/config/pyproject_defaults.toml +28 -0
  265. wl_cli/vendors/config/template_config.toml +24 -0
  266. wl_cli/vendors/config/tool_configs.toml +76 -0
  267. wl_cli/vendors/git/.gitignore +122 -0
  268. wl_cli/vendors/git/.pre-commit-config.yaml +37 -0
  269. wl_cli/vendors/hooks/applypatch-msg.sample +15 -0
  270. wl_cli/vendors/hooks/commit-msg.sample +24 -0
  271. wl_cli/vendors/hooks/fsmonitor-watchman.sample +174 -0
  272. wl_cli/vendors/hooks/post-update.sample +8 -0
  273. wl_cli/vendors/hooks/pre-applypatch.sample +14 -0
  274. wl_cli/vendors/hooks/pre-commit +45 -0
  275. wl_cli/vendors/hooks/pre-commit.legacy +31 -0
  276. wl_cli/vendors/hooks/pre-commit.sample +49 -0
  277. wl_cli/vendors/hooks/pre-merge-commit.sample +13 -0
  278. wl_cli/vendors/hooks/pre-push.sample +60 -0
  279. wl_cli/vendors/hooks/pre-rebase.sample +169 -0
  280. wl_cli/vendors/hooks/pre-receive.sample +24 -0
  281. wl_cli/vendors/hooks/prepare-commit-msg.sample +42 -0
  282. wl_cli/vendors/hooks/push-to-checkout.sample +78 -0
  283. wl_cli/vendors/hooks/sendemail-validate.sample +77 -0
  284. wl_cli/vendors/hooks/unix/applypatch-msg.sample +15 -0
  285. wl_cli/vendors/hooks/unix/check_lockfile.py +58 -0
  286. wl_cli/vendors/hooks/unix/check_lockfile.sh +36 -0
  287. wl_cli/vendors/hooks/unix/commit-msg.sample +24 -0
  288. wl_cli/vendors/hooks/unix/fsmonitor-watchman.sample +174 -0
  289. wl_cli/vendors/hooks/unix/post-update.sample +8 -0
  290. wl_cli/vendors/hooks/unix/pre-applypatch.sample +14 -0
  291. wl_cli/vendors/hooks/unix/pre-commit +190 -0
  292. wl_cli/vendors/hooks/unix/pre-commit.legacy +31 -0
  293. wl_cli/vendors/hooks/unix/pre-commit.py +81 -0
  294. wl_cli/vendors/hooks/unix/pre-commit.sample +49 -0
  295. wl_cli/vendors/hooks/unix/pre-merge-commit.sample +13 -0
  296. wl_cli/vendors/hooks/unix/pre-push +417 -0
  297. wl_cli/vendors/hooks/unix/pre-push.sample +53 -0
  298. wl_cli/vendors/hooks/unix/pre-rebase.sample +169 -0
  299. wl_cli/vendors/hooks/unix/pre-receive.sample +24 -0
  300. wl_cli/vendors/hooks/unix/prepare-commit-msg.sample +42 -0
  301. wl_cli/vendors/hooks/update.sample +128 -0
  302. wl_cli/vendors/hooks/win/work.bat +63 -0
  303. wl_cli/vendors/project/.gitignore +90 -0
  304. wl_cli/vendors/project/.python-version +1 -0
  305. wl_cli/vendors/project/LICENSE +21 -0
  306. wl_cli/vendors/project/README.md +0 -0
  307. wl_cli/vendors/project/pyproject.toml +27 -0
  308. wl_cli/vendors/project/pyproject_no_workspace.toml +16 -0
  309. wl_cli/vendors/project/pyproject_workspace.toml +14 -0
  310. wl_cli/vendors/readme/README.md +120 -0
  311. wl_cli/vendors/rust/Cargo.toml +14 -0
  312. wl_cli/vendors/rust/lib/__init__.py +3 -0
  313. wl_cli/vendors/rust/lib/rust_utils.py +31 -0
  314. wl_cli/vendors/rust/src/lib.rs +13 -0
  315. wl_cli/vendors/rust/src/main.rs +3 -0
  316. wl_cli/vendors/rust/tests/test_rust_fallback.py +40 -0
  317. wl_cli/vendors/sop_doc/git-dev-sop.md +220 -0
  318. wl_cli/vendors/sop_doc/industrial-standard-sop.md +145 -0
  319. wl_cli/vendors/sop_doc/python-sop.md +342 -0
  320. 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