From 575b07e9e50b1c4959e0fb9922f0be5bde88a9b5 Mon Sep 17 00:00:00 2001 From: "sayurinana(vm)" Date: Thu, 18 Dec 2025 20:33:37 +0800 Subject: [PATCH] =?UTF-8?q?7148487=E7=9A=84=E5=BC=BA=E5=88=B6=E6=B8=85?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .aide/branches.json | 14 +- .aide/branches.md | 59 ++-- .aide/logs/2025-12-18T18-38-48-status.json | 195 ++++++++++++ CHANGELOG.md | 77 +++++ aide-program/aide/core/config.py | 15 +- aide-program/aide/flow/branch.py | 336 ++++++++++++++++++--- aide-program/aide/flow/git.py | 7 + aide-program/aide/flow/tracker.py | 43 ++- aide-program/aide/main.py | 12 + task-now.md | 17 -- 10 files changed, 693 insertions(+), 82 deletions(-) create mode 100644 .aide/logs/2025-12-18T18-38-48-status.json diff --git a/.aide/branches.json b/.aide/branches.json index 791afb6..bbac717 100644 --- a/.aide/branches.json +++ b/.aide/branches.json @@ -1,5 +1,5 @@ { - "next_number": 18, + "next_number": 19, "branches": [ { "number": 1, @@ -198,6 +198,18 @@ "status": "finished", "end_commit": "f4a82c96eec49822c39da4f68c87351fe527b0fe", "finished_at": "2025-12-18T18:18:43+08:00" + }, + { + "number": 18, + "branch_name": "aide/018", + "source_branch": "master", + "start_commit": "714848745d2e443ff4ee1d907b3e3df0b5e65dc4", + "task_id": "2025-12-18T18-38-48", + "task_summary": "开始任务准备: aide flow clean 指令与分支概况文档分析", + "started_at": "2025-12-18T18:38:48+08:00", + "status": "force-cleaned", + "end_commit": "19aa94e2e2e084b1c72b8b9e55d565948b8ac19d", + "finished_at": "2025-12-18T20:33:37+08:00" } ] } diff --git a/.aide/branches.md b/.aide/branches.md index 3ef1739..96358d3 100644 --- a/.aide/branches.md +++ b/.aide/branches.md @@ -1,5 +1,16 @@ # Git 分支概况 +## aide/018 + +- **任务**: 开始任务准备: aide flow clean 指令与分支概况文档分析 +- **任务ID**: 2025-12-18T18-38-48 +- **源分支**: master +- **起始提交**: 7148487 +- **结束提交**: 19aa94e +- **状态**: force-cleaned +- **起始时间**: 2025-12-18 18:38 +- **结束时间**: 2025-12-18 20:33 + ## aide/017 - **任务**: 开始任务准备: 调整aide收尾清理逻辑 @@ -8,7 +19,8 @@ - **起始提交**: f4a82c9 - **结束提交**: f4a82c9 - **状态**: finished -- **时间**: 2025-12-18 14:09 ~ 18:18 +- **起始时间**: 2025-12-18 14:09 +- **结束时间**: 2025-12-18 18:18 ## aide/016 @@ -18,7 +30,8 @@ - **起始提交**: 645a30c - **结束提交**: 645a30c - **状态**: finished -- **时间**: 2025-12-18 13:29 ~ 13:58 +- **起始时间**: 2025-12-18 13:29 +- **结束时间**: 2025-12-18 13:58 ## aide/015 @@ -28,7 +41,8 @@ - **起始提交**: 80ebbd4 - **结束提交**: 80ebbd4 - **状态**: finished -- **时间**: 2025-12-18 12:14 ~ 12:50 +- **起始时间**: 2025-12-18 12:14 +- **结束时间**: 2025-12-18 12:50 ## aide/014 @@ -38,7 +52,8 @@ - **起始提交**: d2882c9 - **结束提交**: d2882c9 - **状态**: finished -- **时间**: 2025-12-18 11:53 ~ 11:53 +- **起始时间**: 2025-12-18 11:53 +- **结束时间**: 2025-12-18 11:53 ## aide/013 @@ -48,7 +63,8 @@ - **起始提交**: f4b9620 - **结束提交**: f4b9620 - **状态**: finished -- **时间**: 2025-12-18 10:41 ~ 11:22 +- **起始时间**: 2025-12-18 10:41 +- **结束时间**: 2025-12-18 11:22 ## aide/012 @@ -57,7 +73,7 @@ - **源分支**: master - **起始提交**: c6f45f9 - **状态**: active -- **时间**: 2025-12-18 10:09 +- **起始时间**: 2025-12-18 10:09 ## aide/011 @@ -67,7 +83,8 @@ - **起始提交**: 2042238 - **结束提交**: 8ea9ce7 - **状态**: finished -- **时间**: 2025-12-18 10:03 ~ 10:03 +- **起始时间**: 2025-12-18 10:03 +- **结束时间**: 2025-12-18 10:03 ## aide/010 @@ -77,7 +94,8 @@ - **起始提交**: c8d20db - **结束提交**: 2034962 - **状态**: finished -- **时间**: 2025-12-17 06:34 ~ 06:35 +- **起始时间**: 2025-12-17 06:34 +- **结束时间**: 2025-12-17 06:35 ## aide/009 @@ -87,7 +105,8 @@ - **起始提交**: 6305f61 - **结束提交**: d705a73 - **状态**: finished -- **时间**: 2025-12-17 06:31 ~ 06:32 +- **起始时间**: 2025-12-17 06:31 +- **结束时间**: 2025-12-17 06:32 ## aide/008 @@ -97,7 +116,8 @@ - **起始提交**: c685c31 - **结束提交**: 92eb2ba - **状态**: finished -- **时间**: 2025-12-17 06:27 ~ 06:28 +- **起始时间**: 2025-12-17 06:27 +- **结束时间**: 2025-12-17 06:28 ## aide/007 @@ -107,7 +127,8 @@ - **起始提交**: 8d4cae6 - **结束提交**: 346ec90 - **状态**: finished -- **时间**: 2025-12-17 06:07 ~ 06:07 +- **起始时间**: 2025-12-17 06:07 +- **结束时间**: 2025-12-17 06:07 ## aide/006 @@ -117,7 +138,8 @@ - **起始提交**: 2b0c007 - **结束提交**: d60f0ba - **状态**: finished -- **时间**: 2025-12-17 06:03 ~ 06:05 +- **起始时间**: 2025-12-17 06:03 +- **结束时间**: 2025-12-17 06:05 ## aide/005 @@ -127,7 +149,8 @@ - **起始提交**: a1d7263 - **结束提交**: b2f922b - **状态**: finished -- **时间**: 2025-12-17 06:00 ~ 06:02 +- **起始时间**: 2025-12-17 06:00 +- **结束时间**: 2025-12-17 06:02 ## aide/004 @@ -137,7 +160,8 @@ - **起始提交**: 6179833 - **结束提交**: 6179833 - **状态**: finished -- **时间**: 2025-12-17 05:54 ~ 05:55 +- **起始时间**: 2025-12-17 05:54 +- **结束时间**: 2025-12-17 05:55 ## aide/003 @@ -147,7 +171,8 @@ - **起始提交**: aa067fc - **结束提交**: aa067fc - **状态**: finished -- **时间**: 2025-12-17 05:42 ~ 05:48 +- **起始时间**: 2025-12-17 05:42 +- **结束时间**: 2025-12-17 05:48 ## aide/002 @@ -156,7 +181,7 @@ - **源分支**: master - **起始提交**: be25738 - **状态**: active -- **时间**: 2025-12-17 05:33 +- **起始时间**: 2025-12-17 05:33 ## aide/001 @@ -165,4 +190,4 @@ - **源分支**: master - **起始提交**: bd72362 - **状态**: active -- **时间**: 2025-12-17 04:54 +- **起始时间**: 2025-12-17 04:54 diff --git a/.aide/logs/2025-12-18T18-38-48-status.json b/.aide/logs/2025-12-18T18-38-48-status.json new file mode 100644 index 0000000..3d00385 --- /dev/null +++ b/.aide/logs/2025-12-18T18-38-48-status.json @@ -0,0 +1,195 @@ +{ + "task_id": "2025-12-18T18-38-48", + "current_phase": "task-optimize", + "current_step": 23, + "started_at": "2025-12-18T18:38:48+08:00", + "history": [ + { + "timestamp": "2025-12-18T18:38:48+08:00", + "action": "start", + "phase": "task-optimize", + "step": 1, + "summary": "开始任务准备: aide flow clean 指令与分支概况文档分析", + "git_commit": "f74306e46f459e6642eb84c8463e86d9e0bc697b" + }, + { + "timestamp": "2025-12-18T18:42:34+08:00", + "action": "next-step", + "phase": "task-optimize", + "step": 2, + "summary": "任务内容解析完成,生成结构化任务描述", + "git_commit": "e5211e7e1299da688db14baa65c37a6920f4c9ed" + }, + { + "timestamp": "2025-12-18T18:50:34+08:00", + "action": "next-step", + "phase": "task-optimize", + "step": 3, + "summary": "用户确认任务需求,发现分支哈希相同的 bug 原因", + "git_commit": "3417151e189396a1b2ae8369603045e5c040057b" + }, + { + "timestamp": "2025-12-18T18:53:07+08:00", + "action": "next-step", + "phase": "task-optimize", + "step": 4, + "summary": "任务细则已确认", + "git_commit": "345c44455cc05bbb1c128b96b98c9c6c4b46cb81" + }, + { + "timestamp": "2025-12-18T18:53:18+08:00", + "action": "next-part", + "phase": "flow-design", + "step": 5, + "summary": "进入流程设计环节", + "git_commit": "ca07defc6e5760dccade2969aae608d5d27b9f4b" + }, + { + "timestamp": "2025-12-18T18:55:03+08:00", + "action": "next-step", + "phase": "flow-design", + "step": 6, + "summary": "流程图设计完成", + "git_commit": "aa456b7f3f09d3e089df0ddd16fcc432648db8f9" + }, + { + "timestamp": "2025-12-18T18:55:18+08:00", + "action": "next-part", + "phase": "impl", + "step": 7, + "summary": "流程设计完成,进入实现环节", + "git_commit": "65660e4265e1a67420bc62b503923444236323d7" + }, + { + "timestamp": "2025-12-18T18:56:06+08:00", + "action": "next-step", + "phase": "impl", + "step": 8, + "summary": "完成: decisions 清理逻辑修改为直接删除不备份", + "git_commit": "9ed16d17f24c3e962dc91d85efdd198f434c5f6b" + }, + { + "timestamp": "2025-12-18T18:59:53+08:00", + "action": "next-step", + "phase": "impl", + "step": 9, + "summary": "完成: finish 流程重构,修复哈希 bug,添加 clean_branch_merge 方法", + "git_commit": "e0cc0a4556577b953cec406f79d05857d2840829" + }, + { + "timestamp": "2025-12-18T19:01:34+08:00", + "action": "next-step", + "phase": "impl", + "step": 10, + "summary": "完成: 添加 aide flow clean 指令", + "git_commit": "57cc51781e9a9e5aae02c7fef6604c739c6b4f47" + }, + { + "timestamp": "2025-12-18T19:02:57+08:00", + "action": "next-step", + "phase": "impl", + "step": 11, + "summary": "测试验证: clean 命令正确检测工作区状态", + "git_commit": "f28cb62ccb5154afdc09a626608c56f0e23c3892" + }, + { + "timestamp": "2025-12-18T19:03:11+08:00", + "action": "next-part", + "phase": "verify", + "step": 12, + "summary": "实现完成,进入验证环节", + "git_commit": "0f2de0a72b93b36a55e35dba718f78b2e50982e5" + }, + { + "timestamp": "2025-12-18T19:04:52+08:00", + "action": "next-step", + "phase": "verify", + "step": 13, + "summary": "验证完成: finish 流程、clean 指令、decisions 清理逻辑均已验证", + "git_commit": "2c20dd4d11d804583e2e5ebe78203909a1b61dbf" + }, + { + "timestamp": "2025-12-18T19:05:06+08:00", + "action": "next-part", + "phase": "docs", + "step": 14, + "summary": "验证通过,进入文档环节", + "git_commit": "52dde4809fd8359c962ea77d37f78f03b7560e84" + }, + { + "timestamp": "2025-12-18T19:07:09+08:00", + "action": "next-step", + "phase": "docs", + "step": 15, + "summary": "CHANGELOG.md 更新完成", + "git_commit": "5e7b1381b9b23c72eed2a7af7486f3b2a1875fe6" + }, + { + "timestamp": "2025-12-18T19:07:24+08:00", + "action": "next-part", + "phase": "confirm", + "step": 16, + "summary": "文档更新完成,进入用户确认环节", + "git_commit": "d3c1986de7b08f69051a6a2f5aa7d19fd5743456" + }, + { + "timestamp": "2025-12-18T19:29:56+08:00", + "action": "back-part", + "phase": "task-optimize", + "step": 17, + "summary": "返工:修复三个问题 - 1.结束提交哈希未记录到md 2.md时间格式调整 3.clean应自动提交脏工作区", + "git_commit": "9858fa48c393b6b21f0ab8d51f54aec5d5b12a8e" + }, + { + "timestamp": "2025-12-18T19:33:43+08:00", + "action": "next-part", + "phase": "flow-design", + "step": 18, + "summary": "开始修复三个问题", + "git_commit": "f166f406b52586a4b9e357b8f3b29d19b24dfb64" + }, + { + "timestamp": "2025-12-18T19:36:49+08:00", + "action": "next-part", + "phase": "impl", + "step": 19, + "summary": "开始验证修复", + "git_commit": "878d10cd7b79d2a9aaf2889a37de122a2e025c31" + }, + { + "timestamp": "2025-12-18T19:38:06+08:00", + "action": "next-part", + "phase": "verify", + "step": 20, + "summary": "验证修复完成", + "git_commit": "64552682b4f2d3f86ce7d97f3930d9251f0add4a" + }, + { + "timestamp": "2025-12-18T19:38:18+08:00", + "action": "next-part", + "phase": "docs", + "step": 21, + "summary": "更新文档", + "git_commit": "64d626d32c861125dd636281eea8fd974d3bd90c" + }, + { + "timestamp": "2025-12-18T19:39:28+08:00", + "action": "next-part", + "phase": "confirm", + "step": 22, + "summary": "返工修复完成,进入用户确认", + "git_commit": "1bf8ff39654b7d9c915f90684c1587f3e63a173b" + }, + { + "timestamp": "2025-12-18T19:50:49+08:00", + "action": "back-part", + "phase": "task-optimize", + "step": 23, + "summary": "返工:修复 find_project_root 向上查找逻辑", + "git_commit": "05dace3dd55e5cf33236270180eb105dac6e3175" + } + ], + "source_branch": "master", + "start_commit": "714848745d2e443ff4ee1d907b3e3df0b5e65dc4", + "task_branch": "aide/018" +} diff --git a/CHANGELOG.md b/CHANGELOG.md index d619ea8..cfcd7fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,83 @@ ## 2025-12-18 +### 修复 + +**修复 end_commit 记录逻辑** +- `end_commit` 现在记录 tracker 创建的 finish 提交哈希(`[aide] finish: {summary}`) +- `finished_at` 使用 finish 提交的时间戳 +- tracker.py:移除多余的 `[aide] finish: 更新状态文件` 提交,改为传递 end_commit 和 finished_at 给 branch_mgr +- branch.py:finish_branch_merge 接收 end_commit 和 finished_at 参数 +- 正常 finish 流程不再创建额外的结束提交,直接使用 tracker 传入的值 +- 强制清理(clean)仍会创建自己的结束提交 +- flow-status.json 和 branches.json/md 的更新合并到同一个提交 `[aide] finish: 更新状态` +- 新增 `git.amend()` 方法(备用) + +**修复 find_project_root 在子项目中定位错误的问题** +- 原因:从复制的测试目录运行时,因没有 `flow-status.json` 而向上查找到父项目 +- 解决:添加"步骤 0",如果当前目录已有 `.aide` 目录,直接使用不向上查找 +- 新的查找策略: + 0. 当前目录有 `.aide` → 直接使用 + 1. 向上查找有 `flow-status.json` 的目录(活跃任务) + 2. 向上查找有 `config.toml` 的目录 + +### 新增功能 + +**`aide flow clean` 强制清理命令** +- 用于在任务中途强制终止流程 +- 如果工作区不干净,自动创建一个提交 +- 执行流程与 finish 类似,但: + - 提交消息格式为 `{start_commit[:7]}的强制清理` + - 分支状态标记为 `force-cleaned` +- 保留任务分支供后续参考 + +### 修复 + +**修复 finish 流程中起始/结束提交哈希相同的 bug** +- 原因:切回源分支后才调用 `record_branch_finish`,导致 `rev_parse_head()` 获取的是源分支 HEAD +- 解决:在任务分支上先创建"结束提交"并记录哈希,在最终保存时直接使用保存的变量 +- 新增 `record_end_commit` 方法:仅记录结束提交和时间,不更新状态 +- 在 `_merge_normal` 和 `_merge_with_temp_branch` 中,直接使用保存的 `end_commit` 变量,确保不会丢失 + +### 变更 + +**finish/clean 流程重构** +- 新流程:结束提交 → 记录哈希 → 清理 → 清理提交 → 切回源分支 → squash 合并 → 更新状态(使用保存的end_commit) → 收尾提交 +- `_merge_normal` 方法新增 `is_force_clean` 参数,不再调用 `update_branch_status`,直接内联更新 +- `_merge_with_temp_branch` 方法也同步更新 + +**decisions 清理逻辑简化** +- 清理时不再备份 `decisions/*.json`,直接删除 +- 移除备份目录创建逻辑 + +**branches.md 时间格式调整** +- 起始时间和结束时间分别单独列出 +- 原格式:`- **时间**: 2025-12-18 10:30 ~ 11:45` +- 新格式: + - `- **起始时间**: 2025-12-18 10:30` + - `- **结束时间**: 2025-12-18 11:45` + +### 修改的文件 +- `aide-program/aide/core/config.py` + - `find_project_root` 函数:添加步骤 0,当前目录有 .aide 时不向上查找 +- `aide-program/aide/flow/git.py` + - 新增 `amend` 方法:将暂存区内容追加到上一次提交 +- `aide-program/aide/flow/branch.py` + - `_cleanup_task_files` 方法:decisions 直接删除不备份 + - `record_branch_finish` 方法:保持兼容 + - 新增 `record_end_commit` 方法 + - `clean_branch_merge` 方法:脏工作区时自动创建提交 + - `_merge_normal` 方法:重构流程,结束提交后立即更新状态再清理 + - `_merge_with_temp_branch` 方法:同步重构 + - `_generate_markdown` 方法:时间格式分开显示 +- `aide-program/aide/flow/tracker.py` + - 新增 `clean` 方法 +- `aide-program/aide/main.py` + - 新增 `flow clean` 子命令 + - 新增 `handle_flow_clean` 处理函数 + +--- + ### 改进 **收尾清理逻辑增强** diff --git a/aide-program/aide/core/config.py b/aide-program/aide/core/config.py index 5fdf70e..ac8edd9 100644 --- a/aide-program/aide/core/config.py +++ b/aide-program/aide/core/config.py @@ -17,11 +17,14 @@ def find_project_root(start_path: Path | None = None) -> Path: 类似于 git 查找 .git 目录的逻辑:从当前目录开始向上遍历, 直到找到包含有效 .aide 目录的父目录。 - 查找策略(两遍遍历): + 查找策略(三遍遍历): + 0. 首先:如果当前目录有 .aide 目录,直接使用(不向上查找) 1. 第一遍:优先查找包含 flow-status.json 的目录(活跃任务) 2. 第二遍:如果第一遍未找到,查找包含 config.toml 的目录 - 这样可以确保从子目录运行时,优先找到有活跃任务的项目根目录。 + 这样可以确保: + - 在子项目目录运行时,使用子项目的配置 + - 从子目录运行时,优先找到有活跃任务的项目根目录 Args: start_path: 起始路径,默认为当前工作目录 @@ -46,6 +49,10 @@ def find_project_root(start_path: Path | None = None) -> Path: return current return None + def has_aide_dir(path: Path) -> bool: + """检查是否有 .aide 目录""" + return (path / ".aide").is_dir() + def has_flow_status(path: Path) -> bool: """检查是否有活跃任务状态文件""" return (path / ".aide" / "flow-status.json").exists() @@ -54,6 +61,10 @@ def find_project_root(start_path: Path | None = None) -> Path: """检查是否有配置文件""" return (path / ".aide" / "config.toml").exists() + # 步骤 0:如果当前目录有 .aide 目录,直接使用(不向上查找) + if has_aide_dir(start_path): + return start_path + # 第一遍:优先查找有活跃任务的目录 result = search_upward(has_flow_status) if result is not None: diff --git a/aide-program/aide/flow/branch.py b/aide-program/aide/flow/branch.py index 449ec67..ece9215 100644 --- a/aide-program/aide/flow/branch.py +++ b/aide-program/aide/flow/branch.py @@ -162,20 +162,14 @@ class BranchManager: except OSError: pass - # 5. 备份并删除 decisions/*.json + # 5. 直接删除 decisions/*.json(不备份) decisions_dir = self.aide_dir / "decisions" if decisions_dir.exists(): - decision_files = list(decisions_dir.glob("*.json")) - if decision_files: - # 创建备份目录 - backup_decisions_dir = self.logs_dir / f"{task_id}-decisions" - backup_decisions_dir.mkdir(parents=True, exist_ok=True) - for decision_file in decision_files: - try: - shutil.copy2(decision_file, backup_decisions_dir / decision_file.name) - decision_file.unlink() - except OSError: - pass + for decision_file in decisions_dir.glob("*.json"): + try: + decision_file.unlink() + except OSError: + pass # 6. 删除 pending-items.json pending_items_path = self.aide_dir / "pending-items.json" @@ -252,10 +246,13 @@ class BranchManager: if branch.end_commit: lines.append(f"- **结束提交**: {branch.end_commit[:7]}") lines.append(f"- **状态**: {branch.status}") - time_str = branch.started_at[:16].replace("T", " ") + # 起始时间 + start_time_str = branch.started_at[:16].replace("T", " ") + lines.append(f"- **起始时间**: {start_time_str}") + # 结束时间(单独列出) if branch.finished_at: - time_str += f" ~ {branch.finished_at[11:16]}" - lines.append(f"- **时间**: {time_str}") + end_time_str = branch.finished_at[:16].replace("T", " ") + lines.append(f"- **结束时间**: {end_time_str}") if branch.temp_branch: lines.append(f"- **临时分支**: {branch.temp_branch}") lines.append("") @@ -349,7 +346,7 @@ class BranchManager: end_commit: str | None = None, temp_branch: str | None = None, ) -> None: - """记录分支结束信息""" + """记录分支结束信息(兼容旧接口,一次性更新所有字段)""" data = self.load_branches() branch_info = self.get_active_branch_info() @@ -378,9 +375,97 @@ class BranchManager: self._current_branch_info = None self.save_branches() - def finish_branch_merge(self, task_summary: str) -> tuple[bool, str]: + def record_end_commit(self, end_commit: str) -> None: + """记录结束提交和时间(不更新状态) + + 用于在任务分支上先记录结束点,再执行清理操作。 + """ + data = self.load_branches() + branch_info = self.get_active_branch_info() + + if branch_info is None: + return + + # 更新分支信息(只更新 end_commit 和 finished_at) + for i, branch in enumerate(data.branches): + if branch.number == branch_info.number: + data.branches[i] = BranchInfo( + number=branch.number, + branch_name=branch.branch_name, + source_branch=branch.source_branch, + start_commit=branch.start_commit, + end_commit=end_commit, + task_id=branch.task_id, + task_summary=branch.task_summary, + started_at=branch.started_at, + finished_at=now_iso(), + status=branch.status, # 保持原状态 + temp_branch=branch.temp_branch, + ) + break + + self._data = data + self.save_branches() + + def update_branch_status(self, status: str) -> None: + """更新分支状态(不修改其他字段) + + 用于在切回源分支后更新最终状态。 + """ + data = self.load_branches() + + # 查找当前任务分支(可能已不是当前分支) + current_branch = self.git.get_current_branch() + + # 优先使用缓存的分支信息 + target_number = None + if self._current_branch_info is not None: + target_number = self._current_branch_info.number + else: + # 查找最近的活跃分支 + for branch in reversed(data.branches): + if branch.status == "active": + target_number = branch.number + break + + if target_number is None: + return + + # 更新分支状态 + for i, branch in enumerate(data.branches): + if branch.number == target_number: + data.branches[i] = BranchInfo( + number=branch.number, + branch_name=branch.branch_name, + source_branch=branch.source_branch, + start_commit=branch.start_commit, + end_commit=branch.end_commit, + task_id=branch.task_id, + task_summary=branch.task_summary, + started_at=branch.started_at, + finished_at=branch.finished_at, + status=status, + temp_branch=branch.temp_branch, + ) + break + + self._data = data + self._current_branch_info = None + self.save_branches() + + def finish_branch_merge( + self, + task_summary: str, + end_commit: str | None = None, + finished_at: str | None = None, + ) -> tuple[bool, str]: """执行分支合并逻辑 + Args: + task_summary: 任务摘要 + end_commit: 结束提交哈希(由 tracker 传入) + finished_at: 结束时间(由 tracker 传入) + 返回 (是否成功, 消息) """ branch_info = self.get_active_branch_info() @@ -398,49 +483,148 @@ class BranchManager: return self._merge_with_temp_branch( branch_info=branch_info, task_summary=task_summary, + end_commit=end_commit, + finished_at=finished_at, ) else: # 正常合并流程 return self._merge_normal( branch_info=branch_info, task_summary=task_summary, + end_commit=end_commit, + finished_at=finished_at, + ) + + def clean_branch_merge(self) -> tuple[bool, str]: + """强制清理当前任务分支 + + 如果工作区不干净,会自动创建一个提交。 + 返回 (是否成功, 消息) + """ + # 如果工作区不干净,自动创建提交 + if not self.git.is_clean(): + self.git.add_all() + self.git.commit("[aide] 强制清理前保存未提交的变更") + + branch_info = self.get_active_branch_info() + + if branch_info is None: + return False, "未找到活跃的任务分支" + + source_branch = branch_info.source_branch + start_commit = branch_info.start_commit + + # 检查源分支是否有新提交 + if self.git.has_commits_since(start_commit, source_branch): + # 源分支有新提交,使用临时分支策略(强制清理模式) + return self._merge_with_temp_branch( + branch_info=branch_info, + task_summary="强制清理", + is_force_clean=True, + ) + else: + # 正常合并流程(强制清理模式) + return self._merge_normal( + branch_info=branch_info, + task_summary="强制清理", + is_force_clean=True, ) def _merge_normal( self, branch_info: BranchInfo, task_summary: str, + is_force_clean: bool = False, + end_commit: str | None = None, + finished_at: str | None = None, ) -> tuple[bool, str]: - """正常合并流程:清理 → 临时提交 → squash 合并 → 收尾提交""" + """正常合并流程:更新状态 → 清理 → squash 合并 → 收尾提交 + + Args: + branch_info: 分支信息 + task_summary: 任务摘要 + is_force_clean: 是否为强制清理模式 + end_commit: 结束提交哈希(finish 时由 tracker 传入) + finished_at: 结束时间(finish 时由 tracker 传入) + """ source_branch = branch_info.source_branch task_branch = branch_info.branch_name task_id = branch_info.task_id start_commit = branch_info.start_commit + branch_number = branch_info.number - # 1. 执行任务文件清理(在工作分支上) + # === 在任务分支上执行 === + + # 对于强制清理,需要创建结束提交;对于正常 finish,使用 tracker 传入的 + if is_force_clean: + # 1. 创建"结束提交"(强制清理模式) + self.git.add_all() + end_commit_msg = f"[aide] 强制清理: {task_summary}" + self.git.commit(end_commit_msg) + end_commit = self.git.rev_parse_head() + finished_at = now_iso() + + # 如果没有 end_commit(兜底),使用当前 HEAD + if end_commit is None: + end_commit = self.git.rev_parse_head() + if finished_at is None: + finished_at = now_iso() + + # 2. 更新分支状态(记录 end_commit 和完成时间) + status = "force-cleaned" if is_force_clean else "finished" + data = self.load_branches() + for i, branch in enumerate(data.branches): + if branch.number == branch_number: + data.branches[i] = BranchInfo( + number=branch.number, + branch_name=branch.branch_name, + source_branch=branch.source_branch, + start_commit=branch.start_commit, + end_commit=end_commit, + task_id=branch.task_id, + task_summary=branch.task_summary, + started_at=branch.started_at, + finished_at=finished_at, + status=status, + temp_branch=branch.temp_branch, + ) + break + self._data = data + self._current_branch_info = None + self.save_branches() + + # 3. 提交状态更新(包含 flow-status.json 和 branches.json/md) + self.git.add_all() + if is_force_clean: + self.git.commit("[aide] 强制清理: 更新状态") + else: + self.git.commit("[aide] finish: 更新状态") + + # 4. 执行任务文件清理 self._cleanup_task_files(task_id) - # 2. 创建临时提交保存清理后的变更 + # 5. 创建"清理提交" self.git.add_all() self.git.commit("[aide] 清理任务临时文件") - # 3. 切回源分支 + # === 切回源分支执行 === + + # 6. 切回源分支 self.git.checkout(source_branch) # 切换分支后清理 lock 文件 self._cleanup_lock_file() - # 4. squash 合并任务分支 + # 7. squash 合并任务分支 self.git.merge_squash(task_branch) - # 5. 先更新分支记录(不再记录 end_commit) - self.record_branch_finish(status="finished") - - # 6. 创建收尾提交(包含 squash 内容和分支记录更新) - # 格式:{起始哈希}的任务收尾 + # 8. 创建收尾提交 self.git.add_all() short_hash = start_commit[:7] if start_commit else "unknown" - commit_msg = f"{short_hash}的任务收尾" + if is_force_clean: + commit_msg = f"{short_hash}的强制清理" + else: + commit_msg = f"{short_hash}的任务收尾" self.git.commit(commit_msg) return True, f"任务分支已合并到 {source_branch}" @@ -449,35 +633,103 @@ class BranchManager: self, branch_info: BranchInfo, task_summary: str, + is_force_clean: bool = False, + end_commit: str | None = None, + finished_at: str | None = None, ) -> tuple[bool, str]: - """临时分支合并策略:源分支有新提交时使用""" + """临时分支合并策略:源分支有新提交时使用 + + 流程:更新状态 → 清理 → 临时分支 squash 合并 + + Args: + branch_info: 分支信息 + task_summary: 任务摘要 + is_force_clean: 是否为强制清理模式 + end_commit: 结束提交哈希(finish 时由 tracker 传入) + finished_at: 结束时间(finish 时由 tracker 传入) + """ start_commit = branch_info.start_commit task_branch = branch_info.branch_name + task_id = branch_info.task_id + branch_number = branch_info.number temp_branch = f"{task_branch}-merge" - # 从起始提交检出临时分支 + # === 在任务分支上执行 === + + # 对于强制清理,需要创建结束提交;对于正常 finish,使用 tracker 传入的 + if is_force_clean: + # 1. 创建"结束提交"(强制清理模式) + self.git.add_all() + end_commit_msg = f"[aide] 强制清理: {task_summary}" + self.git.commit(end_commit_msg) + end_commit = self.git.rev_parse_head() + finished_at = now_iso() + + # 如果没有 end_commit(兜底),使用当前 HEAD + if end_commit is None: + end_commit = self.git.rev_parse_head() + if finished_at is None: + finished_at = now_iso() + + # 2. 更新分支状态(记录 end_commit 和完成时间) + status = "force-cleaned-to-temp" if is_force_clean else "merged-to-temp" + data = self.load_branches() + for i, branch in enumerate(data.branches): + if branch.number == branch_number: + data.branches[i] = BranchInfo( + number=branch.number, + branch_name=branch.branch_name, + source_branch=branch.source_branch, + start_commit=branch.start_commit, + end_commit=end_commit, + task_id=branch.task_id, + task_summary=branch.task_summary, + started_at=branch.started_at, + finished_at=finished_at, + status=status, + temp_branch=temp_branch, + ) + break + self._data = data + self._current_branch_info = None + self.save_branches() + + # 3. 提交状态更新(包含 flow-status.json 和 branches.json/md) + self.git.add_all() + if is_force_clean: + self.git.commit("[aide] 强制清理: 更新状态") + else: + self.git.commit("[aide] finish: 更新状态") + + # 4. 执行任务文件清理 + self._cleanup_task_files(task_id) + + # 5. 创建"清理提交" + self.git.add_all() + self.git.commit("[aide] 清理任务临时文件") + + # === 切换到临时分支执行 === + + # 6. 从起始提交检出临时分支 self.git.checkout_new_branch(temp_branch, start_commit) # 切换分支后清理 lock 文件 self._cleanup_lock_file() - # 在临时分支执行 squash 合并 + # 7. 在临时分支执行 squash 合并 self.git.merge_squash(task_branch) - # 创建提交 + # 8. 创建压缩提交 self.git.add_all() - commit_msg = f"[aide] 任务压缩提交: {task_summary}" - end_commit = self.git.commit(commit_msg) - - # 记录完成(保留任务分支和临时分支) - self.record_branch_finish( - status="merged-to-temp", - end_commit=end_commit, - temp_branch=temp_branch, - ) + if is_force_clean: + commit_msg = f"[aide] 强制清理压缩提交: {task_summary}" + else: + commit_msg = f"[aide] 任务压缩提交: {task_summary}" + self.git.commit(commit_msg) + action_name = "强制清理" if is_force_clean else "任务完成" return False, ( f"⚠ 源分支 {branch_info.source_branch} 有新提交\n" - f"已在临时分支 {temp_branch} 完成合并\n" + f"已在临时分支 {temp_branch} 完成{action_name}合并\n" f"请手动处理后续操作" ) diff --git a/aide-program/aide/flow/git.py b/aide-program/aide/flow/git.py index cae825a..452fc12 100644 --- a/aide-program/aide/flow/git.py +++ b/aide-program/aide/flow/git.py @@ -127,6 +127,13 @@ class GitIntegration: if result.returncode != 0: raise FlowError(_format_git_error(f"squash 合并分支 {branch} 失败", result)) + def amend(self) -> str: + """将暂存区内容追加到上一次提交(不修改提交消息)""" + result = self._run(["commit", "--amend", "--no-edit"], check=False) + if result.returncode != 0: + raise FlowError(_format_git_error("git commit --amend 失败", result)) + return self.rev_parse_head() + def _run(self, args: list[str], check: bool) -> subprocess.CompletedProcess[str]: return subprocess.run( ["git", *args], diff --git a/aide-program/aide/flow/tracker.py b/aide-program/aide/flow/tracker.py index ba60c40..83bb569 100644 --- a/aide-program/aide/flow/tracker.py +++ b/aide-program/aide/flow/tracker.py @@ -47,6 +47,37 @@ class FlowTracker: def error(self, description: str) -> bool: return self._run(action="error", to_phase=None, text=description) + def clean(self) -> bool: + """强制清理当前任务 + + 前提条件:工作区必须干净 + """ + try: + self.storage.ensure_ready() + + with self.storage.lock(): + status = self.storage.load_status() + if status is None: + output.err("未找到活跃任务,无需清理") + return False + + # 执行强制清理 + success, msg = self.branch_mgr.clean_branch_merge() + + if success: + output.ok(f"强制清理完成: {msg}") + else: + if "工作区不干净" in msg or "未找到活跃" in msg: + output.err(msg) + else: + # 临时分支情况,需要手动处理 + output.warn(msg) + return success + + except FlowError as exc: + output.err(str(exc)) + return False + def _run(self, *, action: str, to_phase: str | None, text: str) -> bool: try: self.storage.ensure_ready() @@ -129,12 +160,18 @@ class FlowTracker: # 如果进入 finish 环节,执行分支合并(必须在提交后执行) if action == "next-part" and to_phase == "finish": - # 再次提交状态文件(因为 save_status 更新了 git_commit hash) - self.git.add_all() - self.git.commit("[aide] finish: 更新状态文件") + # finish 提交的哈希就是 end_commit + finish_commit = None + finish_timestamp = None + if final_status.history: + last_entry = final_status.history[-1] + finish_commit = last_entry.git_commit + finish_timestamp = last_entry.timestamp success, merge_msg = self.branch_mgr.finish_branch_merge( task_summary=normalized_text, + end_commit=finish_commit, + finished_at=finish_timestamp, ) if not success: output.warn(merge_msg) diff --git a/aide-program/aide/main.py b/aide-program/aide/main.py index b1490de..12a975c 100644 --- a/aide-program/aide/main.py +++ b/aide-program/aide/main.py @@ -140,6 +140,10 @@ def build_parser() -> argparse.ArgumentParser: flow_show.add_argument("task_id", help="任务 ID(时间戳格式,如 20251215-103000)") flow_show.set_defaults(func=handle_flow_show) + # aide flow clean + flow_clean = flow_sub.add_parser("clean", help="强制清理当前任务(工作区需干净)") + flow_clean.set_defaults(func=handle_flow_clean) + flow_parser.set_defaults(func=handle_flow_help) # aide decide @@ -310,6 +314,14 @@ def handle_flow_error(args: argparse.Namespace) -> bool: return tracker.error(args.description) +def handle_flow_clean(args: argparse.Namespace) -> bool: + """aide flow clean - 强制清理当前任务。""" + root = find_project_root() + cfg = ConfigManager(root) + tracker = FlowTracker(root, cfg) + return tracker.clean() + + def handle_flow_status(args: argparse.Namespace) -> bool: """aide flow status - 查看当前任务状态。""" from aide.flow.storage import FlowStorage diff --git a/task-now.md b/task-now.md index 6660be7..e69de29 100644 --- a/task-now.md +++ b/task-now.md @@ -1,17 +0,0 @@ -1. - -创建一个aide flow clean指令,用于强制结束flow流程: - -如果当前工作仓库状态干净,则按照finish时需要清理的东西,同样的对那些文件进行清理, - -然后创建一个清理提交,回到原分支, - -然后压缩提交,但不同的是此时提交消息不是任务收尾,而是abcdef123的强制清理 - -2. - -调整aide程序,清理时不用备份decisions,而是直接删除, - -3. - -你需要查看文档以及程序实现,然后告诉我关于git分支概况文档的创建和维护、原理及其工作流程逻辑,然后我需要对它做出一些调整 \ No newline at end of file