From eba36c46dacd0ac9db35b134302231e89a79dcd8 Mon Sep 17 00:00:00 2001 From: JOJO <1498581755@qq.com> Date: Sat, 11 Apr 2026 17:02:39 +0800 Subject: [PATCH] =?UTF-8?q?fix(versioning):=20=E5=8E=8B=E7=BC=A9=E7=BB=A7?= =?UTF-8?q?=E6=89=BF=E7=89=88=E6=9C=AC=E6=8E=A7=E5=88=B6=E5=B9=B6=E5=BF=BD?= =?UTF-8?q?=E7=95=A5=E7=B3=BB=E7=BB=9F=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/versioning_manager.py | 49 ++++++++++++++++++++++++++--------- server/deep_compression.py | 43 ++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 12 deletions(-) diff --git a/modules/versioning_manager.py b/modules/versioning_manager.py index 533ca4e..f316aa2 100644 --- a/modules/versioning_manager.py +++ b/modules/versioning_manager.py @@ -27,6 +27,7 @@ class VersioningPaths: class ConversationVersioningManager: """Manage hidden git snapshots bound to a conversation.""" + SYSTEM_AUTO_DIRS = ("compact_result", "skills", "user_upload") def __init__(self, project_path: Path | str, data_dir: Path | str, conversation_id: str): self.project_path = Path(project_path).expanduser().resolve() @@ -81,10 +82,6 @@ class ConversationVersioningManager: def _ensure_exclude_paths(self) -> None: """Avoid recursive tracking if save root is inside work tree.""" - try: - rel_save = self.paths.save_root.relative_to(self.project_path) - except ValueError: - return info_exclude = self.paths.git_dir / "info" / "exclude" info_exclude.parent.mkdir(parents=True, exist_ok=True) existing = "" @@ -93,18 +90,46 @@ class ConversationVersioningManager: existing = info_exclude.read_text(encoding="utf-8", errors="ignore") except OSError: existing = "" - marker = str(rel_save).replace("\\", "/").rstrip("/") - if not marker: + existing_lines = set(existing.splitlines()) + lines_to_add: List[str] = [] + + try: + rel_save = self.paths.save_root.relative_to(self.project_path) + marker = str(rel_save).replace("\\", "/").rstrip("/") + if marker: + lines_to_add.append(f"{marker}/") + except ValueError: + pass + + for dirname in self.SYSTEM_AUTO_DIRS: + clean = str(dirname or "").strip().strip("/").replace("\\", "/") + if not clean: + continue + # 同时忽略根目录与任意层级同名目录 + lines_to_add.append(f"/{clean}/") + lines_to_add.append(f"{clean}/") + + pending = [line for line in lines_to_add if line not in existing_lines] + if not pending: return - line = f"{marker}/" - if line not in existing.splitlines(): - with info_exclude.open("a", encoding="utf-8") as fh: - if existing and not existing.endswith("\n"): - fh.write("\n") - fh.write(line + "\n") + if existing and not existing.endswith("\n"): + pending.insert(0, "") + with info_exclude.open("a", encoding="utf-8") as fh: + fh.write("\n".join(pending) + "\n") + + def _drop_ignored_from_index(self) -> None: + """ + Ensure ignored system directories are not kept as tracked files in hidden git index. + """ + for dirname in self.SYSTEM_AUTO_DIRS: + clean = str(dirname or "").strip().strip("/").replace("\\", "/") + if not clean: + continue + self._run_git(["rm", "-r", "--cached", "--ignore-unmatch", "--", clean], check=False) def _stage_all(self) -> None: self._ensure_exclude_paths() + self._drop_ignored_from_index() # Stage everything under work tree; exclusions are handled via info/exclude. self._run_git(["add", "-A", "--", "."]) diff --git a/server/deep_compression.py b/server/deep_compression.py index 23f5888..ea2d99e 100644 --- a/server/deep_compression.py +++ b/server/deep_compression.py @@ -6,6 +6,8 @@ from datetime import datetime from pathlib import Path from typing import Any, Dict, List, Optional, Tuple +from modules.versioning_manager import ConversationVersioningManager + SUMMARY_PROMPT = ( "由于当前对话过长,系统正在自动压缩。请你基于已有上下文输出一份可继续执行的工作总结,要求:\n" @@ -249,6 +251,8 @@ async def run_deep_compression( return {"success": False, "error": "对话正在压缩中", "in_progress": True} old_title = conv_data.get("title") or "未命名" + source_versioning = metadata.get("versioning") if isinstance(metadata.get("versioning"), dict) else {} + source_versioning_enabled = bool(source_versioning.get("enabled")) compression_count = int(metadata.get("compression_count", 0) or 0) previous_records = _normalize_deep_compression_records(metadata) previous_max_count = max([int(item.get("count") or 0) for item in previous_records], default=0) @@ -337,6 +341,45 @@ async def run_deep_compression( "last_deep_compression_record": current_record, }, ) + + if source_versioning_enabled: + try: + vm = ConversationVersioningManager( + project_path=workspace.project_path, + data_dir=workspace.data_dir, + conversation_id=new_conv_id, + ) + vm_meta = vm.set_enabled(enabled=True, mode="overwrite") + new_conv_data = cm.conversation_manager.load_conversation(new_conv_id) or {} + snapshot_payload = { + "conversation_id": new_conv_id, + "title": new_conv_data.get("title"), + "metadata": new_conv_data.get("metadata") or {}, + "messages": new_conv_data.get("messages") or [], + "message_index": -1, + "run_status": "initial", + } + init_result = vm.ensure_initial_checkpoint( + workspace_path=str(workspace.project_path), + conversation_snapshot=snapshot_payload, + ) + init_row = init_result.get("row") or {} + last_commit = init_row.get("commit") or vm_meta.get("last_commit") + cm.conversation_manager.update_conversation_metadata( + new_conv_id, + { + "versioning": { + "enabled": True, + "mode": "overwrite", + "updated_at": datetime.now().isoformat(), + "last_commit": last_commit, + "last_input_seq": int(vm_meta.get("last_input_seq") or 0), + } + }, + ) + except Exception: + pass + cm.conversation_manager.update_conversation_title(new_conv_id, f"{old_title}对话 压缩后") web_terminal.load_conversation(new_conv_id) guide_message = _build_guide_message(