def refile_with_link_update(old_id, new_id, old_path, new_path, workspace):
# 0. 前置检查:工作区必须干净
if not is_working_tree_clean():
raise RefileError(
"Refile requires a clean working tree.\n"
"Please commit or stash your changes first:\n"
" syn commit -m 'WIP' 或 git stash"
)
try:
# 1. 移动源文件(暂存)
git_mv(old_path, new_path)
# 1b. 更新源文件的 Frontmatter(jd_number 字段)
source_content = read_file(new_path) # 文件已移动,从新路径读取
source_fm, source_body = parse_frontmatter(source_content)
source_fm['jd_number'] = new_id # 更新 ID
source_fm['modified'] = datetime.now().isoformat() # 更新修改时间
write_file(new_path, serialize_frontmatter(source_fm) + source_body)
git_add(new_path) # ⚠️ 必须暂存源文件的内容修改(git_mv 只暂存重命名)
# 2. 查找并更新所有引用(其他文件)
# ⚠️ 顺序重要:先 YAML 精确处理,再全文替换,避免竞态
modified_files = set()
files_with_related = set() # 记录有 related 字段的文件
# 2a. 先处理 Frontmatter 中的 related 字段(YAML 精确解析)
# 必须先于全文替换,否则 old_id 已被替换,条件判断会失败
related_refs = db_query(
"SELECT file_path FROM resources WHERE related LIKE ?",
(f'%"{old_id}"%',) # 在 JSON 数组字符串中搜索
)
for (file_path,) in related_refs:
content = read_file(file_path)
frontmatter, body = parse_frontmatter(content)
# 安全检查:确保 related 是非空列表
related = frontmatter.get('related')
if isinstance(related, list) and old_id in related:
# 精确更新 YAML 数组
frontmatter['related'] = [
new_id if rid == old_id else rid
for rid in related
]
# 只替换 wiki-style 链接(避免误改非引用上下文)
body = body.replace(f"[[{old_id}]]", f"[[{new_id}]]")
content = serialize_frontmatter(frontmatter) + body
write_file(file_path, content)
modified_files.add(file_path)
files_with_related.add(file_path)
# 2b. 搜索 wiki-style 链接:[[old_id]]
# ⚠️ 只更新明确的引用语法,不盲目替换正文中的 ID 字符串
wiki_refs = rg_search(f"\\[\\[{old_id}\\]\\]", workspace)
for file_path in wiki_refs:
if file_path in files_with_related:
continue # 已被 2a 处理,跳过
content = read_file(file_path)
content = content.replace(f"[[{old_id}]]", f"[[{new_id}]]")
write_file(file_path, content)
modified_files.add(file_path)
# 3. 暂存所有修改
for f in modified_files:
git_add(f)
# 4. 提交事务(+1 是源文件本身)
git_commit(f"refactor: refile {old_id} → {new_id}, update {len(modified_files) + 1} files")
except Exception as e:
# 4. 安全回滚(因为工作区干净,此时可以安全 reset)
git_reset_hard()
logger.error(f"Refile failed, rolled back: {e}")
raise RefileError("操作失败,已回滚所有修改")
def is_working_tree_clean() -> bool:
"""检查工作区是否干净"""
result = subprocess.run(
["git", "status", "--porcelain"],
capture_output=True, text=True
)
return len(result.stdout.strip()) == 0
策略 B:智能回滚(复杂场景)
如果必须允许脏工作区 Refile,回滚时需精确还原:
def smart_rollback(modified_files: list, old_path: str, new_path: str):
"""精确回滚,不影响其他未提交修改"""
# 1. Unstage 暂存的文件
for f in modified_files:
subprocess.run(["git", "reset", "HEAD", f])
# 2. 还原被修改的文件内容
for f in modified_files:
subprocess.run(["git", "checkout", "--", f])
# 3. 移回源文件
if os.path.exists(new_path):
subprocess.run(["git", "mv", new_path, old_path])