ClickHouse continue-pr 技能深度解析:用 AI Agent 自动接手、修复并推进 Pull Request 的完整工作流
【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse
本文围绕 ClickHouse 仓库中的.claude/skills/continue-pr/SKILL.md展开,系统讲解该技能如何让 AI Agent 接手一个已有的 Pull Request:拉取 PR 元数据、检出 PR 分支并建立基线快照、与基线分支合并解决冲突、基于 CI 报告定位并修复测试失败、逐条处理评审意见、在多重安全门(safety gate)校验后推送更新,以及最后独立修复与本 PR 无关的 CI 故障。读完后,你将掌握一套完整的"PR 接力"方法论:从输入校验、跨 fork 分支检出,到不可变基线(immutable baseline)与推送前祖先关系校验等工程细节,并理解它与无人值守自动化脚本utils/continue-all-prs.sh的协作关系。
技能定位与 Frontmatter 元数据
continue-pr是 ClickHouse 仓库内置的一套 Claude Code 技能(Skill),其目标用一句话概括:接手一个已有的 PR——解决冲突、修复 CI 失败、回应评审反馈,并推送更新。技能定义文件位于 .claude/skills/continue-pr/SKILL.md,其 YAML frontmatter 声明了它的全部行为约束:
--- name: continue-pr description: Continue work on an existing PR - resolve conflicts, fix CI failures, address reviewer feedback, and push updates. Use when the user wants to pick up and advance a pull request. argument-hint: <pr-number> disable-model-invocation: false allowed-tools: Agent, Task, Bash, Read, Write, Edit, Glob, Grep, WebFetch, WebSearch, AskUserQuestion ---几个关键字段的含义:
argument-hint: <pr-number>:调用该技能时必须传入当前仓库内的 PR 编号(如12345),在正文中记为$0/$PR_NUMBER。disable-model-invocation: false:允许模型(而非仅用户显式命令)主动调用该技能。allowed-tools:声明了技能执行期间可用的工具白名单,其中包括AskUserQuestion——这是它区别于无人值守变体continue-pr-auto的核心特征:交互式版本在遇到歧义时可以停下来问用户,而无人值守版本绝不允许(详见 无人值守变体)。
该技能与仓库中的另外几处基础设施紧密配合:
- .claude/CLAUDE.md:项目级指令文件,规定了"不用 rebase/amend、每次修改都新增 commit""C++ 使用 Allman 大括号""用
.claude/tools/fetch_ci_report.js分析 CI 链接"等本项目约定,技能正文的"Notes"一节与其保持一致; - .claude/tools/fetch_ci_report.js:CI 报告抓取工具,直接拉取底层 JSON 数据,无需浏览器;
- utils/continue-all-prs.sh:批量驱动
continue-pr-auto技能并行推进所有相关 PR 的自动化脚本; - .claude/skills/fix-sync/SKILL.md:当唯一失败项是 "CH Inc sync" 时被 continue-pr 调用的专项修复技能。
第一步:参数解析与 PR 元数据获取
输入校验与仓库身份判定
技能对输入的处置遵循"先校验、后使用"原则:
- 从
$0提取 PR 编号;若未提供,交互式版本使用AskUserQuestion向用户询问; - 强制数字校验:PR 编号必须只含数字,任何非数字输入立即拒绝——原文档明确要求"不要把未校验的输入传递给 shell 命令或 GraphQL 查询",这是防止注入的基本防线;
- 判定当前操作的仓库身份——公共仓库
ClickHouse/ClickHouse还是私有仓库ClickHouse/clickhouse-private:
gh repo view --json nameWithOwner --jq .nameWithOwner # or: git remote get-url origin判定结果记为$REPO,之后所有gh命令、API URL 与 GraphQL 的repository(owner:, name:)参数都使用它,而不是硬编码ClickHouse/ClickHouse。仓库身份还决定了第 4 步中"向谁求助":公共仓库问@groeneai,私有仓库问@oranjeai。
拉取元数据:gh 优先,WebFetch 兜底
优先使用ghCLI 一次性取回 PR 的全部关键字段:
gh pr view "$PR_NUMBER" --json number,title,body,headRefName,baseRefName,state,mergeable,mergeStateStatus,author,url,headRepository,headRepositoryOwner,statusCheckRollup,reviews,comments,reviewRequests其中headRefName(PR 分支)、baseRefName(目标分支)、headRepository/headRepositoryOwner(判断分支在哪个仓库)、mergeable/mergeStateStatus(合并状态)是后续各步骤的核心输入。
若gh不可用或未认证,则回退到WebFetch请求 GitHub REST API,并必须处理分页(追加?per_page=100并跟随Link头中rel="next"的 URL 取完所有页):
https://api.github.com/repos/ClickHouse/ClickHouse/pulls/$PR_NUMBERhttps://api.github.com/repos/ClickHouse/ClickHouse/pulls/$PR_NUMBER/reviews?per_page=100https://api.github.com/repos/ClickHouse/ClickHouse/pulls/$PR_NUMBER/comments?per_page=100
最后,向用户汇报 PR 标题、作者、分支与当前状态,确认上下文无误再往下走。
1a. 关闭 PR 前必须验证"已合入"声明
这是文档中一段非常严谨的防错逻辑:把 PR 当作"已集成"或"已过期"而关闭,属于高置信度决策,必须独立验证两件事,且都要基于刚拉取的新鲜 ref:
1. 历史祖先关系(Historical ancestry)
先解析并打印出精确的 base 分支与 PR head 的 OID,再只沿一个方向检查候选提交:
HEAD_REMOTE=origin if [ "$IS_CROSS_REPOSITORY" = "true" ]; then HEAD_REMOTE="pr-$AUTHOR_LOGIN" git remote add "$HEAD_REMOTE" "$FORK_URL" 2>/dev/null || git remote set-url "$HEAD_REMOTE" "$FORK_URL" fi git fetch origin "$BASE_BRANCH" git fetch "$HEAD_REMOTE" "$HEAD_BRANCH" git rev-parse "origin/$BASE_BRANCH" "$HEAD_REMOTE/$HEAD_BRANCH" git merge-base --is-ancestor "$CANDIDATE_COMMIT" "origin/$BASE_BRANCH"退出状态0表示 base 分支包含该提交,否则不包含。文档特别强调:绝不能对HEAD、PR 分支、--all或 worktree 做检查后,把结果描述为"被 base 分支包含";若该检查非零,就不能宣称提交已在 base 中,更不能以"已集成"为由关闭 PR。
2. 当前有效状态(Current effective state)
祖先提交后来可能被 revert 或回退。git branch --contains、git cherry与补丁等价(patch-equivalence)都只是历史证据,都不能证明改动现在仍然生效。正确做法是:
- 检查 base 分支上所有后续触及改动路径的提交,搜索显式或手动的 revert/backout;
- 对比当前 base 树与 PR 的预期效果;
- 在可行时,针对当前 base 运行复现脚本或回归测试。
若改动被全部或部分回退,则按未集成处理;若无法确信当前效果,保持 PR 开启交人工判断。发布关闭评论前,必须写明所拉取的 base OID 以及上述两方面的证据。文档禁止仅凭"记忆中的 ref、过期的本地分支、相似的 commit subject、或提交恰好在仓库某处可见"就关闭 PR。
第二步:本地检出 PR 分支并建立不可变基线
干净工作区是硬性前提
交互式技能可能运行在开发者的日常 checkout 中,因此必须fail closed:工作区不干净就直接失败退出,而不是丢弃暂存、未暂存或未跟踪的工作:
test -z "$(git status --porcelain)" || { echo "The worktree is dirty; use a clean worktree before continuing this PR" >&2 exit 1 }(对比之下,无人值守变体continue-pr-auto运行在专用 worktree 中,改用git reset --hard+git clean -ffdx强制清场,见 .claude/skills/continue-pr-auto/SKILL.md。)
区分主仓库分支与 fork 分支
- 分支在主仓库中:直接从
origin拉取并以 detach 模式检出:
HEAD_REMOTE=origin git fetch "$HEAD_REMOTE" "$HEAD_BRANCH" git checkout --detach "$HEAD_REMOTE/$HEAD_BRANCH"- 分支在作者的 fork 中:fork 的克隆 URL 必须从 PR 元数据推导(
headRepository.url或headRepository.owner.login+headRepository.name),不能硬编码仓库名——fork 可能被重命名。远端名使用pr-前缀以避免与origin等现有远端冲突:
REMOTE_NAME="pr-$AUTHOR_LOGIN" FORK_URL="https://github.com/$FORK_OWNER/$FORK_REPO.git" # from headRepository in PR metadata git remote add "$REMOTE_NAME" "$FORK_URL" 2>/dev/null || git remote set-url "$REMOTE_NAME" "$FORK_URL" HEAD_REMOTE="$REMOTE_NAME" git fetch "$HEAD_REMOTE" "$HEAD_BRANCH" git checkout --detach "$HEAD_REMOTE/$HEAD_BRANCH"记录不可变基线快照
干净检出完成后,立即把"worker 开始工作时 PR 的表面"固化为唯一权威记录:
git fetch origin "$BASE_BRANCH" mkdir -p tmp PR_BASELINE_DIR="$(pwd)/tmp/continue-pr-${PR_NUMBER}-baseline" rm -rf "$PR_BASELINE_DIR" mkdir -p "$PR_BASELINE_DIR" INITIAL_PR_HEAD=$(git rev-parse "$HEAD_REMOTE/$HEAD_BRANCH") test "$(git rev-parse HEAD)" = "$INITIAL_PR_HEAD" INITIAL_BASE_HEAD=$(git rev-parse "origin/$BASE_BRANCH") printf '%s\n%s\n' "$INITIAL_PR_HEAD" "$INITIAL_BASE_HEAD" > "$PR_BASELINE_DIR/state" git diff --name-status "origin/$BASE_BRANCH"...HEAD > "$PR_BASELINE_DIR/name-status" git diff --stat "origin/$BASE_BRANCH"...HEAD > "$PR_BASELINE_DIR/stat" git diff --binary "origin/$BASE_BRANCH"...HEAD > "$PR_BASELINE_DIR/diff"注意临时目录用的是仓库内的tmp/而非/tmp——这与 CLAUDE.md 中"临时文件一律放当前目录的tmp子目录"的约定一致。基线目录包含四个文件:state(两行 OID:初始 PR head 与初始 base head)、name-status、stat、binary diff。文档强调:会话期间不得修改、暂存或删除这些产物,它们必须保留到第 7 步的推送安全门。之所以用确定性的路径加state文件,是因为 Agent 每个恢复的 turn 都会启动全新的 shell 进程,shell 变量无法继承,只有落盘的文件能跨进程恢复。
第三步:与基线分支合并解决冲突
关键细节:使用 PR 元数据中的真实 base 分支(baseRefName),不要硬编码master——这样 backport 到其他分支的 PR 也能被正确处理。
先判断 PR 是否落后于 base 分支:
git fetch origin "$BASE_BRANCH" git merge-base --is-ancestor "origin/$BASE_BRANCH" HEAD || echo "needs merge"满足以下任一条件就执行合并:
- 分支落后于 base 且 CI 变红(有检查未通过);
- 分支落后 base 超过一周(无论检查是否通过);
- 存在冲突。
git merge "origin/$BASE_BRANCH"出现合并冲突时的处理流程:
- 列出冲突文件:
git diff --name-only --diff-filter=U; - 对每个冲突文件,派一个
subagent_type=general-purpose的 Task 子代理去解决:读取冲突文件、分析冲突标记、智能取舍——PR 的改动在它所修改的代码区域优先,master 的改动在不相关区域优先,然后git add <file>暂存;交互式版本若冲突有歧义,用AskUserQuestion展示给用户裁决; - 完成合并:
git commit --no-edit。
第四步:分析 CI 状态并修复失败
ClickHouse 的 CI 报告以 S3 上的 HTML/JSON 形式存在。技能规定使用仓库自带的 fetch_ci_report.js 直接抓取底层数据:
node .claude/tools/fetch_ci_report.js "https://github.com/ClickHouse/ClickHouse/pull/$PR_NUMBER" --failed --cidb从 CLAUDE.md 可以看到该工具的完整能力:接受 GitHub PR URL(抓取全部 CI 报告)、S3 上的 praktika HTML URL 或直接的result_*.jsonURL;常用选项包括--test <name>按名称过滤、--failed只看失败、--all看全部、--links显示日志等产物链接、--cidb为失败测试生成 CIDB 链接、--report <n>只取第 n 个报告、--download-logs [path]下载日志包、--credentials <user,password>供私有仓库做 HTTP Basic 认证。从源码看,该工具还会按 magic bytes 透明解压 zstd/gzip 存储的大文本产物(见 fetch_ci_report.js 中的 maybeDecompress),并对卡死的zstd子进程设置了 2GB 缓冲与 120 秒超时保护。
下载日志后的典型分析手法(同样来自 CLAUDE.md):
tar -xzf /tmp/ci_logs.tar.gz ci/tmp/pytest_parallel.jsonl grep "test_name" ci/tmp/pytest_parallel.jsonl | python3 -c "import sys,json; [print(json.loads(l).get('longrepr','')) for l in sys.stdin if 'failed' in l]"对每个 CI 失败的标准处置循环
- 先查是否为已知问题:搜索已有的 open issue / PR:
gh issue list --repo ClickHouse/ClickHouse --state open --search "<failure_description>" --limit 5 gh pr list --repo ClickHouse/ClickHouse --state open --search "<failure_description>" --limit 5只有存在能对应上的具体 open issue 或 PR 时,才允许把失败判定为"与本 PR 无关"——**没有证据不得 dismiss**。- 调查失败:必要时下载日志:
node .claude/tools/fetch_ci_report.js "<report_url>" --failed --download-logs提取分析相关日志,阅读失败的测试文件及其执行的代码。修复失败:做必要的代码或测试修改;每个修复单独一个 commit,message 说明"错在哪、为什么"。
若唯一失败项是"CH Inc sync",转由 /fix-sync 技能 处理。该技能先把失败诊断为三类之一(
conflicts found/build failed/tests failed),再分别走对应修复路径——它修复的是私有仓库clickhouse-private中自动生成的sync-upstream/pr/<PR_NUMBER>同步 PR。若确信失败与本次改动无关,在 PR 上留言求助
$REPO对应的 reviewer(@groeneai或@oranjeai):
🕵 @<reviewer>, investigate the failure: <link> and provide a fix in a separate PR. If the fix is already in progress, link it here.- 循环,直到所有失败都被处理,或都被"带链接的已知问题"确认。
第五步:逐条处理评审反馈
先用gh拉取评审意见:
gh pr view "$PR_NUMBER" --json reviews,comments --jq '.reviews[] | select(.state != "COMMENTED" or .body != "") | {author: .author.login, state: .state, body: .body}' gh api "repos/ClickHouse/ClickHouse/pulls/$PR_NUMBER/comments" --paginate --jq '.[] | select(.in_reply_to_id == null or .in_reply_to_id == 0) | {author: .user.login, body: .body, path: .path, line: .line, created_at: .created_at}'注意第二条用in_reply_to_id == null or == 0只保留线程的根评论,避免把回复链中的每句话都当成独立反馈。
拉取 review thread 并区分已解决/未解决
线程的解决状态只能从 GraphQL API 拿到。技能给出一段完整的游标分页脚本(PR 的 review thread 可能超过 100 条):
CURSOR="" while true; do AFTER_CLAUSE="" if [ -n "$CURSOR" ]; then AFTER_CLAUSE=", after: \"$CURSOR\"" fi RESULT=$(gh api graphql -f query=" { repository(owner: \"ClickHouse\", name: \"ClickHouse\") { pullRequest(number: $PR_NUMBER) { reviewThreads(first: 100${AFTER_CLAUSE}) { pageInfo { hasNextPage endCursor } nodes { id isResolved comments(first: 100) { pageInfo { hasNextPage endCursor } nodes { author { login } body path line } } } } } } }") echo "$RESULT" HAS_NEXT=$(echo "$RESULT" | jq -r '.data.repository.pullRequest.reviewThreads.pageInfo.hasNextPage') [ "$HAS_NEXT" = "true" ] || break CURSOR=$(echo "$RESULT" | jq -r '.data.repository.pullRequest.reviewThreads.pageInfo.endCursor') done若某个线程自身的comments.pageInfo.hasNextPage == true,再用该线程的id与其endCursor发起node(id: "<thread_id>") { ... on PullRequestReviewThread { comments(first: 100, after: "<end_cursor>") ... } }的追问,直到hasNextPage为false。
WebFetch 兜底路径:GraphQL 需要认证,纯 REST 无法获取isResolved。此时按 REST 的pull_request_review_id与in_reply_to_id重建线程结构,**把所有线程都当作"可能未解决"**处理,并在输出中明确声明"线程解决状态无法确定"。
处理规则:先过滤掉isResolved == true的线程(避免把已处理的反馈重新引入),然后对每个未解决线程:理解评论意图 → 阅读相关代码上下文 → 若合理且正确就做出修改 → 以 "Address review: <改动摘要>" 为前缀提交;若评审者的建议看似不正确或含糊,记入输出交由用户判断(这是交互式版本的行为;无人值守版本则自行做出最佳判断或在线程里给出理由性回复)。
第六步:整体复查 PR diff
所有修复落地后,审视整个 PR:
git diff "origin/$BASE_BRANCH"...HEAD --stat git log "origin/$BASE_BRANCH"..HEAD --oneline从整体上评估四个问题:改动是否正确且完整?是否还有遗漏?测试是否充分?PR 描述在改动后是否依然准确?评估结论汇报给用户。
第七步:推送安全门(Safety Gate)与推送
推送目标由第 2 步的判定决定:主仓库分支推origin,fork 分支推pr-$AUTHOR_LOGIN远端。推送前必须通过一道**硬性停止(hard stop)**的安全门,共四道校验:
1. 恢复并验证基线状态
恢复的 turn 不继承 shell 变量,所以先从基线目录读回并验证:
PR_BASELINE_DIR="$(pwd)/tmp/continue-pr-${PR_NUMBER}-baseline" mapfile -t BASELINE_STATE < "$PR_BASELINE_DIR/state" test "${#BASELINE_STATE[@]}" = 2 INITIAL_PR_HEAD=${BASELINE_STATE[0]} INITIAL_BASE_HEAD=${BASELINE_STATE[1]} git cat-file -e "$INITIAL_PR_HEAD^{commit}" git cat-file -e "$INITIAL_BASE_HEAD^{commit}" git merge-base --is-ancestor "$INITIAL_PR_HEAD" HEAD核心约束:保持INITIAL_PR_HEAD是祖先——只允许在既有 PR 历史之上追加 commit。永远不许 rebase、把分支 reset 到别的提交、amend 已发布的 commit、删除远端分支、使用+refspec,或给git push传--force、--force-with-lease、--no-verify。
2. 精确暂存
只用git add <path>暂存显式路径;禁止git add -A、git add .、git commit -a。提交前必须检查git diff --cached --name-status与--stat,每个暂存路径都能被"本次要求的修复"或"某次具体的冲突解决"解释;出现解释不了的路径就取消暂存,范围不明时不提交。
3. 推送前再取远端,验证祖先关系
git fetch "$PUSH_REMOTE" "$HEAD_BRANCH" REMOTE_HEAD=$(git rev-parse "$PUSH_REMOTE/$HEAD_BRANCH") git merge-base --is-ancestor "$REMOTE_HEAD" HEAD若检查非零,正常合并远端分支或停下报告血缘(lineage)问题——绝不替换其历史。
4. 与基线快照做全量 diff 对账
diff -u "$PR_BASELINE_DIR/name-status" <(git diff --name-status "origin/$BASE_BRANCH"...HEAD) diff -u "$PR_BASELINE_DIR/stat" <(git diff --stat "origin/$BASE_BRANCH"...HEAD)每一个新增路径与消失路径都要能说明原因(包括"因合并了 base 分支而消失的改动")。文档列出了一批危险信号:范围突然大幅扩张/收缩、不相关子树被改动、批量删除、或单个父提交的 fix commit 里混入了 base 分支的变更——这些意味着错误检出、过期树快照、被污染的工作区或丢失的历史,一律不得推送;范围无法用 PR 意图和本会话工作证明时,停下并报告确切的 diff 异常。
通过全部校验后执行推送并汇报 PR URL:
git push "$PUSH_REMOTE" HEAD:"$HEAD_BRANCH"第八步:独立修复无关的 CI 失败
完成步骤 1–7 后,回头处理第 4 步中被判定为"与本 PR 无关"的失败(已证明非本 PR 引起、且没有其他 open PR 在修)。每个无关失败独立成一个修复 PR:
- 切回 master 建新分支:
git checkout master git pull origin master git checkout -b fix/<descriptive-name>- 调查并修复:下载日志、读失败测试与被执行代码,动手修复;
- 推送并按项目 PR 模板开 PR:
git push -u origin fix/<descriptive-name>使用 [`.github/PULL_REQUEST_TEMPLATE.md`](https://link.gitcode.com/i/b2d7d3f1ac9de18f54fb4dc138496338) 作为 PR 模板,存在对应 issue 则链接,changelog 类别选 "CI Fix or improvement";- 对每个无关失败重复以上流程,一个修复一个 PR;全部提交后切回原 PR 分支,汇报新建 PR 清单。
错误处理与项目约定(Notes)
错误处理四原则:gh不可用时所有元数据改用WebFetch+ GitHub API URL;gh未认证时建议用户执行! gh auth login;远端推送因权限失败时报告错误并建议用户手动推送;CI 日志拿不到时汇报已有信息并在可分析范围内继续。
Notes 一节的硬性约定(与 CLAUDE.md 的项目规范一一对应):
- 发出的每一条GitHub 评论(PR 评论、issue 评论、review thread 回复)必须以
🕵(带空格)开头,使自动化评论可被识别; - 不用 rebase、不用 amend——一律新增 commit;不推送
master分支; - 每个修复单独一个、描述清晰的 commit;
- commit message 中把 ClickHouse SQL 字面量、类名、函数名或日志原文包在 inline code 里(如
MergeTree); - C++ 改动使用 Allman 风格大括号(CI 风格检查强制);
- 构建 ClickHouse 后把输出重定向到构建目录下的日志文件,并用子代理(subagent)分析日志、只返回简要摘要;跑测试同理——这一约定也出现在 continue-all-prs.sh 对 worker 的要求中。
与无人值守自动化的关系:continue-pr-auto 与 continue-all-prs.sh
continue-pr不是孤立存在的。仓库中还有一个姊妹技能 .claude/skills/continue-pr-auto/SKILL.md,它是无人值守变体,由 utils/continue-all-prs.sh 驱动:主进程维护一个共享工作队列,把每个 PR 分派给最先空闲的 worker,每个 worker 在自己的 git worktree 里运行/continue-pr-auto。两者的关键差异可以概括为:
| 维度 | continue-pr(本文) | continue-pr-auto |
|---|---|---|
| 运行环境 | 开发者日常 checkout,要求干净工作区否则退出 | 专用 worker worktree,可reset --hard+clean清场 |
| 交互 | 可AskUserQuestion询问用户 | 绝不停下等待输入,按最佳判断继续 |
| 陈旧分支 | 解决冲突即可 | 增加 step 3a:机械合并不够时重构(rework)该 feature使其对齐最新 master 的 API |
| 推送模式 | 仅update(原分支追加提交) | update与supersede两种模式:推不动他人 fork(maintainerCanModify=false)且改动仍需要时,在主仓库开新分支重建变更、注明替代关系并关闭原 PR |
| 调用方式 | 用户显式调用 | 由continue-all-prs.sh批量 fan-out |
continue-all-prs.sh的头部注释还说明了它如何判定"有进展":不看 agent 的退出码,而看PR head 是否前进;每个 PR 的终态归为PUSHED/MERGED/CLOSED/NO-CHANGE/NEEDS-ATTENTION/FAILED/TIMEOUT之一,每个 PR 的完整 transcript 写入tmp/continue-all-prs/下的日志文件。该自动化行为的回归测试位于 utils/tests/test_continue_all_prs.sh,其中就包含对 supersede 模式关键语句(PUSH_MODE=supersede、PUSH_BRANCH="continue-pr-${PR_NUMBER}-<short-desc>")的断言。
小结
continue-pr技能的价值不在于某一条命令,而在于它把"接手他人 PR"这一高频协作场景编码成了带证据链和防呆机制的可复现流程:输入必须数字校验、仓库身份动态判定、跨 fork 分支用元数据推导 URL、"已合入"关闭需双证据、检出后固化不可变基线、推送前做祖先关系与 diff 对账两道闸门、无关失败独立开 PR。这些设计让 AI Agent 在大规模代码库(如 ClickHouse 这样拥有 tests/queries 下近三万个测试文件)中推进 PR 时,既能自动化,又把"破坏历史""静默吞掉评审反馈""误关 PR"这类高危操作挡在了明确的规则之外。
【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考