Skip to main content
Glama
version_control.py6.36 kB
""" 版本控制管理器 用于管理生成文件的版本,支持增量更新和删除检测 """ import hashlib import json from pathlib import Path from typing import Dict, Set, Optional, List, Tuple from datetime import datetime class VersionControlManager: """版本控制管理器""" def __init__(self, output_dir: Path): """ 初始化版本控制管理器 Args: output_dir: 输出目录 """ self.output_dir = Path(output_dir) self.cache_dir = self.output_dir / ".android_codegen_cache" self.index_file = self.cache_dir / "file_index.json" # 创建缓存目录 self.cache_dir.mkdir(parents=True, exist_ok=True) # 加载现有的文件索引 self.file_index = self._load_index() def _load_index(self) -> Dict[str, Dict[str, any]]: """加载文件索引""" if not self.index_file.exists(): return {} try: with open(self.index_file, 'r', encoding='utf-8') as f: return json.load(f) except Exception: return {} def _save_index(self): """保存文件索引""" try: with open(self.index_file, 'w', encoding='utf-8') as f: json.dump(self.file_index, f, ensure_ascii=False, indent=2) except Exception as e: print(f"[警告] 保存文件索引失败: {e}") def _calculate_file_hash(self, file_path: Path) -> Optional[str]: """计算文件哈希值""" if not file_path.exists(): return None try: with open(file_path, 'rb') as f: content = f.read() return hashlib.md5(content).hexdigest() except Exception: return None def _calculate_content_hash(self, content: str) -> str: """计算内容哈希值""" return hashlib.md5(content.encode('utf-8')).hexdigest() def get_file_info(self, relative_path: str) -> Optional[Dict[str, any]]: """ 获取文件的版本信息 Args: relative_path: 相对于输出目录的文件路径 Returns: 文件信息字典,包含 hash、mtime 等,如果文件不存在则返回 None """ return self.file_index.get(relative_path) def record_file(self, relative_path: str, content_hash: str, api_key: Optional[Tuple[str, str]] = None): """ 记录生成的文件 Args: relative_path: 相对于输出目录的文件路径 content_hash: 文件内容的哈希值 api_key: 关联的 API 键 (url, method),用于删除检测 """ full_path = self.output_dir / relative_path mtime = datetime.now().isoformat() if full_path.exists() else None self.file_index[relative_path] = { "hash": content_hash, "mtime": mtime, "api_key": list(api_key) if api_key else None } def compare_files( self, new_files: Dict[str, str] ) -> Tuple[Dict[str, str], Set[str], Set[str]]: """ 比较新旧文件列表 Args: new_files: 新文件字典 {relative_path: content_hash} Returns: (需要更新的文件, 需要删除的文件, 保持不变的文件) """ new_paths = set(new_files.keys()) old_paths = set(self.file_index.keys()) # 需要更新的文件(新增或内容有变化) to_update = {} for path, new_hash in new_files.items(): old_info = self.file_index.get(path) if not old_info or old_info.get("hash") != new_hash: to_update[path] = new_hash # 需要删除的文件(旧文件不在新列表中) to_delete = old_paths - new_paths # 保持不变的文件 unchanged = new_paths - set(to_update.keys()) return to_update, to_delete, unchanged def get_orphaned_api_files(self, api_keys: Set[Tuple[str, str]]) -> List[str]: """ 获取孤立的 API 相关文件(API 已不存在但文件还在) Args: api_keys: 当前所有 API 的键集合 Returns: 孤立的文件路径列表 """ orphaned = [] api_keys_list = [list(key) for key in api_keys] for file_path, file_info in self.file_index.items(): api_key = file_info.get("api_key") # 如果是 API 相关文件,且对应的 API 已不存在 if api_key and api_key not in api_keys_list: orphaned.append(file_path) return orphaned def remove_file(self, relative_path: str): """从索引中移除文件记录""" if relative_path in self.file_index: del self.file_index[relative_path] def commit(self): """提交更改(保存索引)""" self._save_index() def clean_orphaned_files(self, orphaned_files: List[str], delete_files: bool = False): """ 清理孤立的文件 Args: orphaned_files: 孤立的文件路径列表 delete_files: 是否同时删除文件本身 """ for file_path in orphaned_files: self.remove_file(file_path) if delete_files: full_path = self.output_dir / file_path try: if full_path.exists(): full_path.unlink() # 尝试删除空目录 parent = full_path.parent try: if not any(parent.iterdir()): parent.rmdir() except: pass except Exception as e: print(f"[警告] 删除文件失败 {file_path}: {e}") def get_statistics(self) -> Dict[str, int]: """获取统计信息""" total = len(self.file_index) return { "total_files": total, "indexed_files": total }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/yfcyfc123234/showdoc_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server