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])
 
 
Back to Top