<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Wen Ping</title>
    <description>The latest articles on DEV Community by Wen Ping (@wenping).</description>
    <link>https://dev.to/wenping</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4174947%2F7dddb348-e605-4df5-a2ef-2e93a7bc9682.jpg</url>
      <title>DEV Community: Wen Ping</title>
      <link>https://dev.to/wenping</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wenping"/>
    <language>en</language>
    <item>
      <title>从 GitLab 迁到 AngusGit：迁移清单、双跑和回滚</title>
      <dc:creator>Wen Ping</dc:creator>
      <pubDate>Sat, 31 Oct 2026 04:11:14 +0000</pubDate>
      <link>https://dev.to/wenping/cong-gitlab-qian-dao-angusgitqian-yi-qing-dan-shuang-pao-he-hui-gun-h92</link>
      <guid>https://dev.to/wenping/cong-gitlab-qian-dao-angusgitqian-yi-qing-dan-shuang-pao-he-hui-gun-h92</guid>
      <description>&lt;p&gt;这篇讲团队从 GitLab 整体切到 AngusGit 时，怎么保证切换那天不丢代码、出了问题能退回去。看完你可以按准备、首次同步、双跑、切换日、回滚五个阶段逐项验收，并给 GitLab 留两周只归档、不删除的回滚窗口。&lt;/p&gt;

&lt;p&gt;先登录 AngusGit。GitLab 路径 &lt;code&gt;platform/backend/order-service&lt;/code&gt; 只有两级可放：拿不准，或不同子组下有同名项目时，把子组并进仓库名，写成 &lt;code&gt;platform/backend-order-service&lt;/code&gt;。仓库名里的点号改成连字符，例如 &lt;code&gt;docs.site&lt;/code&gt; 改成 &lt;code&gt;docs-site&lt;/code&gt;。在 &lt;strong&gt;设置 → 团队&lt;/strong&gt; 点 &lt;strong&gt;新建团队&lt;/strong&gt;，&lt;strong&gt;团队标识&lt;/strong&gt; 沿用 GitLab 组的英文名。侧栏 &lt;strong&gt;仓库 → 新建仓库&lt;/strong&gt;，&lt;strong&gt;添加 README 文件&lt;/strong&gt;、&lt;strong&gt;添加 .gitignore&lt;/strong&gt;、&lt;strong&gt;选择许可证&lt;/strong&gt; 都不要勾，否则推送会被拒绝。&lt;/p&gt;

&lt;p&gt;到 &lt;strong&gt;我的 → 访问令牌&lt;/strong&gt; 点 &lt;strong&gt;生成新令牌&lt;/strong&gt;，&lt;strong&gt;密码验证&lt;/strong&gt; 填当前登录密码。同步脚本用一把勾了 &lt;code&gt;git:write&lt;/code&gt;、仓库权限 &lt;strong&gt;WRITE&lt;/strong&gt; 的令牌。Jenkins 只读拉代码另生成一把，只勾 &lt;code&gt;git:read&lt;/code&gt;、仓库权限 &lt;strong&gt;READ&lt;/strong&gt;。用户名都是 AngusGit 登录名，密码填令牌，生成后马上复制。先手动成功推一个仓库，在系统凭据窗口里保存，再跑脚本。&lt;/p&gt;

&lt;p&gt;把对应关系写成 &lt;code&gt;repos.txt&lt;/code&gt;，每行两列，先 GitLab 路径，再「所有者/仓库名」：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;platform/backend/order-service platform/backend-order-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;然后执行（域名换成你的）：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; src dst&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;git clone &lt;span class="nt"&gt;--bare&lt;/span&gt; &lt;span class="s2"&gt;"https://gitlab.example.com/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;src&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.git"&lt;/span&gt; tmp.git &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;continue
  &lt;/span&gt;git &lt;span class="nt"&gt;-C&lt;/span&gt; tmp.git branch &lt;span class="nt"&gt;-m&lt;/span&gt; master main 2&amp;gt;/dev/null
  git &lt;span class="nt"&gt;-C&lt;/span&gt; tmp.git push &lt;span class="s2"&gt;"https://git.anguskit.com/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;dst&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.git"&lt;/span&gt; &lt;span class="nt"&gt;--all&lt;/span&gt;
  git &lt;span class="nt"&gt;-C&lt;/span&gt; tmp.git push &lt;span class="s2"&gt;"https://git.anguskit.com/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;dst&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.git"&lt;/span&gt; &lt;span class="nt"&gt;--tags&lt;/span&gt;
  &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; tmp.git
&lt;span class="k"&gt;done&lt;/span&gt; &amp;lt; repos.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;每个仓库用下面的命令对比，没有意外输出才算一致。&lt;code&gt;master&lt;/code&gt; 改成 &lt;code&gt;main&lt;/code&gt; 时，这一对名字不同是预期的：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diff &lt;span class="se"&gt;\&lt;/span&gt;
  &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;git ls-remote &lt;span class="nt"&gt;--heads&lt;/span&gt; &lt;span class="nt"&gt;--tags&lt;/span&gt; https://gitlab.example.com/platform/backend/order-service.git | &lt;span class="nb"&gt;sort&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;git ls-remote &lt;span class="nt"&gt;--heads&lt;/span&gt; &lt;span class="nt"&gt;--tags&lt;/span&gt; https://git.anguskit.com/platform/backend-order-service.git | &lt;span class="nb"&gt;sort&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;分支保护要等切换日最后一次同步之后再开。进入仓库 &lt;strong&gt;设置 → 分支保护&lt;/strong&gt;，点 &lt;strong&gt;添加规则&lt;/strong&gt;，&lt;strong&gt;分支名称&lt;/strong&gt; 选盘点表里的分支（通常是 &lt;code&gt;main&lt;/code&gt;）。打开 &lt;strong&gt;需要代码评审&lt;/strong&gt;，&lt;strong&gt;必需的评审人数&lt;/strong&gt; 填 GitLab 上记下的人数；打开 &lt;strong&gt;对管理员同样生效&lt;/strong&gt;；&lt;strong&gt;允许强制推送&lt;/strong&gt; 和 &lt;strong&gt;允许删除&lt;/strong&gt; 保持关闭。同步期间不要开，否则脚本推 &lt;code&gt;main&lt;/code&gt; 会被拒绝。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyvvhjr3s2w22owdd2844.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyvvhjr3s2w22owdd2844.png" alt="准备、首次同步、双跑、切换日、两周回滚窗口" width="800" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. 这一轮只迁仓库
&lt;/h2&gt;

&lt;p&gt;GitLab 上不只有代码，还有合并请求、议题、CI 流水线和各种集成。一次全部切走，任何一块出问题都会拖住整个迁移。建议分期：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;期次&lt;/th&gt;
&lt;th&gt;内容&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;本轮&lt;/td&gt;
&lt;td&gt;Git 仓库、成员权限、分支保护、Webhook&lt;/td&gt;
&lt;td&gt;切换完成后，团队在 AngusGit 上提交、评审和合并&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;下一轮&lt;/td&gt;
&lt;td&gt;CI&lt;/td&gt;
&lt;td&gt;本轮先让现有构建能被 AngusGit 触发，下一轮再决定是否改写流水线&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;另行安排&lt;/td&gt;
&lt;td&gt;议题、Wiki&lt;/td&gt;
&lt;td&gt;这些在 GitLab 的数据库里，不在 Git 仓库中，推送带不过来&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;CI 虽然放到下一轮，切换日之前也必须有着落。GitLab 不再收到推送之后，GitLab CI 也就不会再触发。本轮最稳妥的过渡做法，是让现有的 Jenkins 任务改从 AngusGit 拉代码，并用 Jenkins 的 &lt;strong&gt;轮询 SCM&lt;/strong&gt;（Poll SCM）定时检查 &lt;code&gt;main&lt;/code&gt; 有没有新提交，例如每两分钟一次。不管代码是命令行推送进来的，还是在拉取请求页点合并进来的，轮询都能发现。Jenkins 拉代码用开头那把只读令牌：只勾 &lt;code&gt;git:read&lt;/code&gt;，仓库权限 &lt;strong&gt;READ&lt;/strong&gt;，用户名填登录名，密码填令牌。&lt;/p&gt;

&lt;h2&gt;
  
  
  2. 准备：先盘点
&lt;/h2&gt;

&lt;p&gt;迁移前把下面每一项都查一遍，结果写进一张表。切换日的每一步都要用到它。&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;盘点项&lt;/th&gt;
&lt;th&gt;在 GitLab 上怎么查&lt;/th&gt;
&lt;th&gt;记下什么&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;仓库清单&lt;/td&gt;
&lt;td&gt;各群组的项目列表&lt;/td&gt;
&lt;td&gt;路径、可见性（谁能看到仓库）、默认分支名、大致大小，以及在 AngusGit 上对应的「团队/仓库名」&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;成员和角色&lt;/td&gt;
&lt;td&gt;群组和项目的 &lt;strong&gt;成员&lt;/strong&gt; 页&lt;/td&gt;
&lt;td&gt;每个人在每个仓库要读还是要写，谁是维护者&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;受保护分支&lt;/td&gt;
&lt;td&gt;项目 &lt;strong&gt;设置 → 仓库 → 受保护分支&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;哪些分支禁止直接推送、合并前要几个人批准&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI&lt;/td&gt;
&lt;td&gt;项目里的 &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; 和 Runner&lt;/td&gt;
&lt;td&gt;哪些仓库有构建；切换后用什么触发&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Webhook 和集成&lt;/td&gt;
&lt;td&gt;项目 &lt;strong&gt;设置 → Webhooks&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;每个 Webhook 的地址和用途，切换后要在 AngusGit 上重建&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;写死 Git 地址的地方&lt;/td&gt;
&lt;td&gt;在代码里搜索 GitLab 域名&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.gitmodules&lt;/code&gt;、CI 和部署脚本、依赖声明里的 Git 地址、文档里的克隆命令&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;进行中的合并请求&lt;/td&gt;
&lt;td&gt;每个项目的 &lt;strong&gt;合并请求&lt;/strong&gt; 列表&lt;/td&gt;
&lt;td&gt;切换日之前要合并或关闭，评审记录带不过来&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Git LFS&lt;/td&gt;
&lt;td&gt;项目里有没有 &lt;code&gt;.gitattributes&lt;/code&gt; 中的 &lt;code&gt;filter=lfs&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;AngusGit 不支持 LFS，用到的仓库要先处理大文件&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;盘点表里「写死 Git 地址的地方」最容易漏。部署脚本、子模块、依赖声明里还写着 GitLab 地址，切换之后不会马上报错，要等 GitLab 下线那天才一起出问题。&lt;/p&gt;

&lt;h2&gt;
  
  
  3. 首次同步
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;在 AngusGit 建团队和空仓库&lt;/strong&gt;
按盘点表的对应关系，建好团队和空仓库。新建仓库时三个初始化选项都不勾。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;成员先只给读权限&lt;/strong&gt;
把成员加进团队或仓库，这个阶段一律只给读取权限。双跑期间，AngusGit 上的代码只能从 GitLab 同步过来，不能有人直接往 AngusGit 推送；只给读权限，就从权限上保证了这一点。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;分支保护先不开&lt;/strong&gt;
同步脚本会直接把分支推到 AngusGit，包括 &lt;code&gt;main&lt;/code&gt;。这时如果已经给 &lt;code&gt;main&lt;/code&gt; 开了分支保护，同步推送会被拒绝。分支保护规则可以先整理好，等到切换日最后一次同步之后再建。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;跑一次同步脚本，然后验收&lt;/strong&gt;
执行同步脚本，把 &lt;code&gt;repos.txt&lt;/code&gt; 里的仓库全部推过去。每个仓库都用开头的 &lt;code&gt;git ls-remote --heads --tags&lt;/code&gt; 对比 GitLab 和 AngusGit，没有意外输出才算同步成功。&lt;code&gt;master&lt;/code&gt; 改成 &lt;code&gt;main&lt;/code&gt; 时，这一对名字不同是预期的。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  4. 双跑：GitLab 写，AngusGit 读
&lt;/h2&gt;

&lt;p&gt;双跑期间，团队照常在 GitLab 上提交、评审、合并。AngusGit 每天从 GitLab 同步一次，让团队提前熟悉新地址和新界面。&lt;/p&gt;

&lt;p&gt;双跑只有一条规则：&lt;strong&gt;任何时候只有一边能写&lt;/strong&gt;。两边都能写，代码就会分叉。分叉之后，同步推送会被拒绝，最后只能手工合并，这正是迁移最怕的情况。&lt;/p&gt;

&lt;p&gt;这段时间可以做这些事：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;用定时任务每天执行一次同步脚本，每次跑完都用 &lt;code&gt;git ls-remote&lt;/code&gt; 对比一遍。&lt;/li&gt;
&lt;li&gt;请几位成员从 AngusGit clone 仓库，确认 SSH 或 HTTPS 访问都没问题，访问令牌都已备好。&lt;/li&gt;
&lt;li&gt;在一个测试仓库里，让大家把拉取请求、评审、合并完整走一遍。&lt;/li&gt;
&lt;li&gt;新建一个测试用的 Jenkins 任务，从 AngusGit 拉代码、打开轮询 SCM，确认它能发现同步过来的新提交并开始构建。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fziy0s9jw0tb2lfkh2pl6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fziy0s9jw0tb2lfkh2pl6.png" alt="开发者只向 GitLab 推送，AngusGit 每天单向同步、只读" width="800" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. 切换日
&lt;/h2&gt;

&lt;p&gt;切换日按下面的顺序做，每一步做完再做下一步。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;提前通知冻结时间&lt;/strong&gt;
至少提前一天通知全员：几点开始停止向 GitLab 推送，几点之后改用 AngusGit。进行中的合并请求，要在冻结之前合并或关闭。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;冻结 GitLab&lt;/strong&gt;
到点后，把每个项目在 GitLab 上归档（项目 &lt;strong&gt;设置 → 通用 → 高级 → 归档项目&lt;/strong&gt;）。归档后项目变成只读，任何人都推不上去，代码和合并请求记录都还在。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;最后一次同步并对比&lt;/strong&gt;
执行同步脚本，然后对每个仓库跑一遍 &lt;code&gt;git ls-remote&lt;/code&gt; 对比。全部一致才往下走；有不一致的，先查清楚是哪个分支、为什么。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;打开 AngusGit 的写权限和分支保护&lt;/strong&gt;
按盘点表把成员的权限从只读改成读写，再在每个仓库的 &lt;strong&gt;设置 → 分支保护&lt;/strong&gt; 里按开头添加规则：打开 &lt;strong&gt;需要代码评审&lt;/strong&gt; 并填上盘点的人数，打开 &lt;strong&gt;对管理员同样生效&lt;/strong&gt;，&lt;strong&gt;允许强制推送&lt;/strong&gt; 和 &lt;strong&gt;允许删除&lt;/strong&gt; 保持关闭。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;切换 CI 和集成&lt;/strong&gt;
把 Jenkins 任务的源码地址改成 AngusGit，打开轮询 SCM，再推一个测试提交，确认构建被触发。盘点表里的其他 Webhook，在 AngusGit 仓库的 &lt;strong&gt;设置 → Webhooks&lt;/strong&gt; 里重建。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;通知全员改地址&lt;/strong&gt;
每个人在自己的本地仓库里执行一次 &lt;code&gt;git remote set-url origin &amp;lt;AngusGit 地址&amp;gt;&lt;/code&gt;，然后 &lt;code&gt;git fetch&lt;/code&gt; 确认能拉取。双跑期间直接从 AngusGit clone 的人，不需要改。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;切换日结束前，还要按盘点表把写死 GitLab 地址的地方，逐个改成 AngusGit 地址并提交。&lt;/p&gt;

&lt;h2&gt;
  
  
  6. 回滚
&lt;/h2&gt;

&lt;p&gt;GitLab 上的项目只归档，不删除。从切换日起留一个回滚窗口，例如两周。窗口内如果遇到无法马上解决的问题，例如权限大面积出错、构建全部断掉，可以退回 GitLab。&lt;/p&gt;

&lt;p&gt;回滚时，AngusGit 上已经有了切换后的新提交，要先把它们带回 GitLab：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;冻结 AngusGit&lt;/strong&gt;
通知全员停止向 AngusGit 推送，把成员权限改回只读。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;取消 GitLab 归档&lt;/strong&gt;
GitLab 上归档的项目拒绝推送，先在项目 &lt;strong&gt;设置 → 通用 → 高级&lt;/strong&gt; 里取消归档。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;把新提交推回 GitLab&lt;/strong&gt;
迁移时把 &lt;code&gt;master&lt;/code&gt; 改成了 &lt;code&gt;main&lt;/code&gt; 的，先把它改回 &lt;code&gt;master&lt;/code&gt;；GitLab 在冻结之后没有新提交，这次推送是普通的追加，不需要强制推送。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;对比后通知全员改回地址&lt;/strong&gt;
用 &lt;code&gt;git ls-remote&lt;/code&gt; 对比两边一致后，通知全员把 &lt;code&gt;origin&lt;/code&gt; 改回 GitLab 地址，CI 也切回 GitLab。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;回滚时用 &lt;code&gt;git clone --bare&lt;/code&gt; 得到一份只存 Git 数据、没有可编辑工作区的副本，再推回 GitLab：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;--bare&lt;/span&gt; https://git.anguskit.com/platform/backend-order-service.git
&lt;span class="nb"&gt;cd &lt;/span&gt;backend-order-service.git
git branch &lt;span class="nt"&gt;-m&lt;/span&gt; main master
git push https://gitlab.example.com/platform/backend/order-service.git &lt;span class="nt"&gt;--all&lt;/span&gt;
git push https://gitlab.example.com/platform/backend/order-service.git &lt;span class="nt"&gt;--tags&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;回滚窗口结束、确认不再需要 GitLab 之后，再按公司的数据保留要求处理 GitLab 上的项目。&lt;/p&gt;

&lt;h2&gt;
  
  
  7. 出错时
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;你看到的&lt;/th&gt;
&lt;th&gt;原因&lt;/th&gt;
&lt;th&gt;怎么处理&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;双跑期间同步推送 &lt;code&gt;main&lt;/code&gt; 被拒绝&lt;/td&gt;
&lt;td&gt;AngusGit 上已经给 &lt;code&gt;main&lt;/code&gt; 开了分支保护&lt;/td&gt;
&lt;td&gt;先删掉这条保护规则，等切换日最后一次同步之后再建&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;同步推送 &lt;code&gt;! [rejected] … (non-fast-forward)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;有人在双跑期间直接往 AngusGit 推了代码，两边分叉&lt;/td&gt;
&lt;td&gt;找到这条提交，确认是否需要保留。要保留的，在 GitLab 上重新提交一次；然后把 AngusGit 上的分支重置成 GitLab 的版本。同时检查成员权限是否真的只读&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;切换日对比有差异&lt;/td&gt;
&lt;td&gt;冻结之前有推送没赶上同步，或者同步中途断开&lt;/td&gt;
&lt;td&gt;再执行一次同步脚本，直到对比一致&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;切换后部分构建不再触发&lt;/td&gt;
&lt;td&gt;Jenkins 任务的源码地址还是 GitLab，或者没打开轮询 SCM；构建脚本里还写着 GitLab 地址&lt;/td&gt;
&lt;td&gt;对照盘点表逐个改 Jenkins 任务和脚本里的地址&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;切换后有人推送报 &lt;code&gt;repository '…' not found&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;本地 &lt;code&gt;origin&lt;/code&gt; 改错了，团队名或仓库名和对应规则不一致&lt;/td&gt;
&lt;td&gt;从 AngusGit 仓库页的 &lt;strong&gt;代码&lt;/strong&gt; 按钮复制地址，重新 &lt;code&gt;git remote set-url&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  常见问题
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;能不能让 GitLab 和 AngusGit 一直双向同步，慢慢切？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;不建议。两边都能写，就一定会在某个时候分叉。分叉后由谁来合并、以哪边为准，都很难说清。双跑期间只保留一个写入方，切换就是一个明确的时间点。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;几十个仓库要一天切完吗？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;不必。可以按团队或业务线分批，每批走一遍第 5 节。但同一批里互相依赖的仓库，例如主仓库和它的子模块，要放在同一天切。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;合并请求的讨论记录怎么办？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;带不过来。重要的设计讨论，在 GitLab 归档前导出或整理成文档，放进仓库的 &lt;code&gt;docs&lt;/code&gt; 目录；进行中的合并请求，冻结前合并或关闭。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;切换后，CI 什么时候迁？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;本轮先让 Jenkins 改从 AngusGit 拉代码，现有构建继续工作，等团队适应了新的仓库和评审流程，再单独安排一轮评估。AngusGit 团队版和企业版自带流水线，新建流水线时可以选 &lt;strong&gt;导入 GitLab CI&lt;/strong&gt;，从 &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; 生成初稿。社区版没有 CI/CD 菜单。&lt;/p&gt;

&lt;h2&gt;
  
  
  小结
&lt;/h2&gt;

&lt;p&gt;从 GitLab 迁到 AngusGit，本轮只迁仓库，CI 和议题另排。先盘点仓库、成员、受保护分支、CI、Webhook 和写死 Git 地址的地方。首次同步之后进入双跑：GitLab 是唯一能写的一边，AngusGit 只读、每日同步。切换日按顺序做：冻结 GitLab，最后同步，对比一致，然后打开写权限和分支保护，切换 CI，通知全员改地址。GitLab 项目只归档不删除，回滚窗口内需要退回时，把 AngusGit 的新提交推回 GitLab，再取消归档。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;结论：&lt;/strong&gt; 批量迁移期间必须保持「单边可写」：双跑期只写 GitLab、每日单向同步到只读的 AngusGit；切换日先归档冻结 GitLab 并用 &lt;code&gt;git ls-remote&lt;/code&gt; 对齐全部引用后，再开启 AngusGit 的写权限与分支保护。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  参考文章
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/move-gitlab-repository-to-angusgit" rel="noopener noreferrer"&gt;从 GitLab 搬仓库到 AngusGit：公开项目直接导入，私有和内部项目推送&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/protect-git-main-branch-pull-request" rel="noopener noreferrer"&gt;怎么保护 Git 主分支：先开分支保护，再用合并请求合代码&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/clone-private-repository-https-access-token" rel="noopener noreferrer"&gt;HTTPS 克隆私有仓库：密码填访问令牌&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/docs/git/manual/03-org-permissions" rel="noopener noreferrer"&gt;AngusGit 手册：组织与权限（团队、协作者）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/docs/git/manual/17-webhook-integration" rel="noopener noreferrer"&gt;AngusGit 手册：Webhook 与集成&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>angusgit</category>
      <category>gitlab</category>
    </item>
    <item>
      <title>GitLab CI 迁到内置流水线：YAML 怎么改写</title>
      <dc:creator>Wen Ping</dc:creator>
      <pubDate>Sat, 31 Oct 2026 04:10:38 +0000</pubDate>
      <link>https://dev.to/wenping/gitlab-ci-qian-dao-nei-zhi-liu-shui-xian-yaml-zen-yao-gai-xie-2ae7</link>
      <guid>https://dev.to/wenping/gitlab-ci-qian-dao-nei-zhi-liu-shui-xian-yaml-zen-yao-gai-xie-2ae7</guid>
      <description>&lt;p&gt;这篇讲怎么把 &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; 用 &lt;strong&gt;导入 GitLab CI&lt;/strong&gt; 转成 AngusGit 流水线，以及保存前要改什么。看完你可以对照未映射清单和注意清单，处理转不过去的 &lt;code&gt;artifacts&lt;/code&gt;、&lt;code&gt;rules&lt;/code&gt; 和 &lt;code&gt;when&lt;/code&gt;，补上分支限制，避免部署在没人点击时就自动跑；导入不会替你新建凭证。&lt;/p&gt;

&lt;p&gt;AngusGit 的流水线在团队版和企业版中提供，社区版没有 &lt;strong&gt;CI/CD&lt;/strong&gt; 菜单。如果你还没写过 AngusGit 的流水线，建议先看文末参考里的 YAML 入门一文，了解 Job（流水线里的一组步骤）、运行器（Runner，真正执行任务的那台机器）这些基本概念。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0w6n546iv9celey9he1i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0w6n546iv9celey9he1i.png" alt="导入 GitLab CI：转换之后先看清单，核对完再创建" width="800" height="217"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. 一份用来转换的 .gitlab-ci.yml
&lt;/h2&gt;

&lt;p&gt;下面这份配置专门用来演示转换，并不是哪个线上仓库正在使用的文件。它分三个阶段：先构建，再测试，最后在 &lt;code&gt;main&lt;/code&gt; 分支上手动触发部署。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;deploy&lt;/span&gt;

&lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;MAVEN_OPTS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-Dmaven.repo.local=.m2"&lt;/span&gt;

&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;maven:3.9-eclipse-temurin-21&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;docker&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mvn -B -DskipTests package&lt;/span&gt;
  &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;target/*.jar&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;maven:3.9-eclipse-temurin-21&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;docker&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mvn -B test&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$CI_PIPELINE_SOURCE&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"merge_request_event"'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$CI_COMMIT_BRANCH&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"main"'&lt;/span&gt;

&lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./deploy.sh $CI_COMMIT_SHORT_SHA&lt;/span&gt;
  &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;manual&lt;/span&gt;
  &lt;span class="na"&gt;only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. 用控制台导入，先拿到初稿
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;打开导入对话框&lt;/strong&gt;
进入 &lt;strong&gt;CI/CD → 流水线&lt;/strong&gt;，点 &lt;strong&gt;新建流水线&lt;/strong&gt;。菜单里有 &lt;strong&gt;空白创建&lt;/strong&gt;、&lt;strong&gt;根据仓库生成&lt;/strong&gt;、&lt;strong&gt;导入 GitHub Actions&lt;/strong&gt;、&lt;strong&gt;导入 GitLab CI&lt;/strong&gt; 四项，后两项下面都有一行小字「部分语法自动转换，导入后请校验」。这里选 &lt;strong&gt;导入 GitLab CI&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;粘贴内容并转换&lt;/strong&gt;
对话框标题是 &lt;strong&gt;导入 GitLab CI&lt;/strong&gt;，说明文字是「子集映射为 AngusCICD YAML。include / 完整 rules / child pipeline 不会展开，请看未映射清单。」也就是说，只有一部分语法会被转换。默认页签是 &lt;strong&gt;粘贴 YAML&lt;/strong&gt;（旁边还有 &lt;strong&gt;扫描仓库&lt;/strong&gt;），输入框的名称是 &lt;strong&gt;GitLab CI&lt;/strong&gt;，提示文字是「粘贴 .gitlab-ci.yml 内容」。把第 1 节的整段 YAML 贴进去，点 &lt;strong&gt;转换&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;看两份清单&lt;/strong&gt;
转换结果显示在输入框下方，从上到下依次是 &lt;strong&gt;未映射（需人工改）&lt;/strong&gt;、&lt;strong&gt;注意&lt;/strong&gt;、&lt;strong&gt;流水线名称&lt;/strong&gt;，最后是 YAML 编辑器。流水线名称会自动填成 &lt;code&gt;imported-from-gitlab&lt;/code&gt;（输入框占位文字是「输入流水线名称」）。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;核对清单后再创建&lt;/strong&gt;
从流水线列表打开时，对话框右下角的按钮是 &lt;strong&gt;创建流水线&lt;/strong&gt;；从空白创建页打开同一个对话框时，这个位置是 &lt;strong&gt;填入编辑器&lt;/strong&gt;。如果清单里的问题还没改完，点 &lt;strong&gt;取消&lt;/strong&gt; 即可，仓库里不会留下流水线。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feggk7oji1dj771v7uaqq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feggk7oji1dj771v7uaqq.png" alt="导入 GitLab CI 的转换结果：未映射四条、注意三条，流水线名称是 imported-from-gitlab" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;转换完成后，对话框中间会滚到两份清单（更上方的粘贴框可能被滚出视野），标题、说明和右下角的 &lt;strong&gt;取消&lt;/strong&gt;、&lt;strong&gt;创建流水线&lt;/strong&gt; 仍在。清单内容如下，四条未映射是：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;jobs.build.artifacts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;jobs.test rules/only/except（仅用于推断触发器；完整条件未映射）&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;jobs.deploy rules/only/except（仅用于推断触发器；完整条件未映射）&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;jobs.deploy.when&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;三条注意是：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;jobs.deploy 无 image，映射为 NATIVE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CI_COMMIT_SHORT_SHA → commitSha（完整 SHA，不是短 SHA）&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GitLab CI_* 预定义变量已近似改写；其余 CI_* 运行时不会注入&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;第一条注意里的 &lt;code&gt;NATIVE&lt;/code&gt;，和下面初稿里的 &lt;code&gt;DOCKER&lt;/code&gt;，是 AngusGit 的两种执行模式：&lt;code&gt;DOCKER&lt;/code&gt; 在指定镜像启动的容器里执行命令，&lt;code&gt;NATIVE&lt;/code&gt; 直接在运行器所在的机器（宿主机）上执行。转换成功的项不会逐条列出，编辑器里生成的初稿如下：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1'&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;imported-from-gitlab&lt;/span&gt;
&lt;span class="na"&gt;triggers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;on'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;push&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;on'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pull_request&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;MAVEN_OPTS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;-Dmaven.repo.local=.m2&lt;/span&gt;
&lt;span class="na"&gt;pipeline&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DOCKER&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;maven:3.9-eclipse-temurin-21&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;script&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mvn -B -DskipTests package&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DOCKER&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;maven:3.9-eclipse-temurin-21&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;script&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mvn -B test&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NATIVE&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;deploy&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;script&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./deploy.sh ${{ commitSha }}&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;初稿里的 &lt;code&gt;'on'&lt;/code&gt; 带引号，是因为 YAML 会把不加引号的 &lt;code&gt;on&lt;/code&gt; 当成布尔值，导出时特意加了引号来避开。这是转换后的初稿，保存前还必须处理未映射项。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F82v92b75b97wcteolwv3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F82v92b75b97wcteolwv3.png" alt="左边是原配置，右边是转换初稿：deploy 仍会自动跑" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. 对照表：哪些转了，哪些没转
&lt;/h2&gt;

&lt;p&gt;先看能自动转换的部分。其中最需要理解的是 &lt;code&gt;stages&lt;/code&gt;：GitLab CI 用阶段来排先后，AngusGit 没有阶段，改为让后一个 Job 用 &lt;code&gt;needs&lt;/code&gt; 声明它要等哪个 Job 完成。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;导入会自动转过去的：&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;GitLab CI&lt;/th&gt;
&lt;th&gt;初稿里的样子&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;script&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;步骤名 &lt;code&gt;script&lt;/code&gt;，命令在 &lt;code&gt;run&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;单行脚本的步骤名就是 &lt;code&gt;script&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;runner.mode: DOCKER&lt;/code&gt; 加 &lt;code&gt;runner.image&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;镜像名原样保留，仍是 &lt;code&gt;maven:3.9-eclipse-temurin-21&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;没写 &lt;code&gt;image&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;runner.mode: NATIVE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;例如 &lt;code&gt;deploy&lt;/code&gt;，注意里写了「jobs.deploy 无 image，映射为 NATIVE」&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tags&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;runner.labels&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;docker&lt;/code&gt;、&lt;code&gt;deploy&lt;/code&gt; 原样变成标签&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;variables&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;流水线级 &lt;code&gt;env&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;MAVEN_OPTS&lt;/code&gt; 写在了顶层 &lt;code&gt;env&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stages&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;后一个阶段的 Job 写 &lt;code&gt;needs&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;test&lt;/code&gt; 依赖 &lt;code&gt;build&lt;/code&gt;，&lt;code&gt;deploy&lt;/code&gt; 依赖 &lt;code&gt;test&lt;/code&gt;。&lt;code&gt;stages&lt;/code&gt; 本身不保留&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;rules&lt;/code&gt; 里的合并请求&lt;/td&gt;
&lt;td&gt;触发器多了一条 &lt;code&gt;pull_request&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;完整的 &lt;code&gt;if&lt;/code&gt; 没有写进 Job&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;allow_failure&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;continue-on-error&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;这份示例里没有&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;environment&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;只保留环境名称&lt;/td&gt;
&lt;td&gt;这份示例里没有。有的话，注意会写「只保留名称，无 GitLab environment 保护规则」&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;接下来是没转过去的部分。下表先列出要点，表后再逐条展开。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;这份示例没转过去、保存前要处理的：&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;你在清单里看到的&lt;/th&gt;
&lt;th&gt;初稿实际做了什么&lt;/th&gt;
&lt;th&gt;保存前怎么改&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jobs.build.artifacts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;target/*.jar&lt;/code&gt; 不会传给后面的 Job&lt;/td&gt;
&lt;td&gt;本例可先不改&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;jobs.test&lt;/code&gt; 和 &lt;code&gt;jobs.deploy&lt;/code&gt; 的 &lt;code&gt;rules&lt;/code&gt; / &lt;code&gt;only&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;只用来推断触发器，&lt;code&gt;main&lt;/code&gt; 没变成 &lt;code&gt;branches&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;给触发器补上 &lt;code&gt;branches: [main]&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jobs.deploy.when&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;丢掉了 &lt;code&gt;when: manual&lt;/code&gt;，&lt;code&gt;deploy&lt;/code&gt; 仍在且 &lt;code&gt;needs: [test]&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;删掉 &lt;code&gt;deploy&lt;/code&gt; 另建一条，或加 &lt;code&gt;approval&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;include&lt;/code&gt;、&lt;code&gt;retry&lt;/code&gt;、&lt;code&gt;timeout&lt;/code&gt;、&lt;code&gt;dependencies&lt;/code&gt;、&lt;code&gt;trigger&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;本例没有，出现时会进未映射清单&lt;/td&gt;
&lt;td&gt;见表后说明&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;artifacts&lt;/code&gt;（构建产物）&lt;/strong&gt;：这份示例的 &lt;code&gt;test&lt;/code&gt; 自己会再跑一遍 &lt;code&gt;mvn test&lt;/code&gt;，不读那个 jar，所以两个 Job 可以先保持原样。如果下游脚本要读上一个 Job 生成的文件，就把相关步骤并进同一个 Job，或者先上传到制品库再下载。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;rules&lt;/code&gt; / &lt;code&gt;only&lt;/code&gt;&lt;/strong&gt;：初稿里的 &lt;code&gt;push&lt;/code&gt; 和 &lt;code&gt;pull_request&lt;/code&gt; 都没有 &lt;code&gt;branches: [main]&lt;/code&gt;。不改的话，其他分支也会触发这条流水线，而且 &lt;code&gt;deploy&lt;/code&gt; 会跟着运行。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;when&lt;/code&gt;&lt;/strong&gt;：&lt;code&gt;when: manual&lt;/code&gt; 被丢掉后，部署会在每次触发时自动执行。不想自动部署，就把 &lt;code&gt;deploy&lt;/code&gt; 从这条流水线里删掉，另建一条用 &lt;code&gt;workflow_dispatch&lt;/code&gt;（只能手动运行）触发的流水线，或者给这个 Job 加 &lt;code&gt;approval&lt;/code&gt;（审批）。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;其他字段&lt;/strong&gt;：用了 &lt;code&gt;include&lt;/code&gt; 的，要先把被引用的片段合并进一份 YAML 再导入；Job 级的 &lt;code&gt;retry&lt;/code&gt;、&lt;code&gt;timeout&lt;/code&gt; 要改写到步骤上，单位是秒；用 &lt;code&gt;trigger&lt;/code&gt; 触发的下游流水线要拆开单独处理。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;定时任务也能导入，但 cron 要自己补。&lt;/strong&gt; cron 是描述定时规则的表达式。GitLab 的定时规则配置在页面上，不写在 YAML 里。所以，YAML 里如果有 &lt;code&gt;only: schedules&lt;/code&gt;，或者 &lt;code&gt;rules&lt;/code&gt; 的条件里写了 schedule，导入会加上 &lt;code&gt;on: schedule&lt;/code&gt;，同时在未映射里写明「schedule cron 未映射（GitLab 定时在 UI，不在 YAML）」。这时 cron 要你自己补上，格式是五段式，例如 &lt;code&gt;0 2 * * *&lt;/code&gt;。第 1 节的示例没有定时，所以初稿里也没有 &lt;code&gt;on: schedule&lt;/code&gt;。&lt;/p&gt;

&lt;h2&gt;
  
  
  4. CI_* 变量要特别检查
&lt;/h2&gt;

&lt;p&gt;GitLab 运行时会自动提供一批以 &lt;code&gt;CI_&lt;/code&gt; 开头的预定义变量，AngusGit 不会注入它们。导入时只会把下表这几个改写成 &lt;code&gt;${{ }}&lt;/code&gt; 表达式，并不会改成环境变量 &lt;code&gt;GIT_COMMIT_SHA&lt;/code&gt;：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;GitLab 变量&lt;/th&gt;
&lt;th&gt;导入后&lt;/th&gt;
&lt;th&gt;这份示例&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$CI_COMMIT_SHA&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${{ commitSha }}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;示例脚本里没有&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$CI_COMMIT_SHORT_SHA&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;同样是 &lt;code&gt;${{ commitSha }}&lt;/code&gt;，完整 SHA&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;deploy&lt;/code&gt; 命令变成 &lt;code&gt;./deploy.sh ${{ commitSha }}&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$CI_COMMIT_BRANCH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${{ branch }}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;只在 &lt;code&gt;test&lt;/code&gt; 的 &lt;code&gt;rules&lt;/code&gt; 里，那条 &lt;code&gt;if&lt;/code&gt; 没写进 Job&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$CI_PIPELINE_SOURCE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${{ event }}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;同样只用来推断出 &lt;code&gt;pull_request&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$CI_COMMIT_TAG&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${{ ref }}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;本例没有，出现时注意写「CI_COMMIT_TAG → ref（近似）」&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;其中 &lt;code&gt;$CI_COMMIT_SHORT_SHA&lt;/code&gt; 最容易出错。注意原文是 &lt;code&gt;CI_COMMIT_SHORT_SHA → commitSha（完整 SHA，不是短 SHA）&lt;/code&gt;，也就是说，改写后拿到的是完整的提交号。如果你要 8 位短 SHA，就在 shell 里自己截取 &lt;code&gt;$GIT_COMMIT_SHA&lt;/code&gt;，不要以为 &lt;code&gt;${{ commitSha }}&lt;/code&gt; 已经是短的。&lt;/p&gt;

&lt;p&gt;表外的其他 &lt;code&gt;CI_&lt;/code&gt; 变量，例如 &lt;code&gt;CI_PROJECT_DIR&lt;/code&gt;、&lt;code&gt;CI_PIPELINE_ID&lt;/code&gt;、&lt;code&gt;CI_REGISTRY&lt;/code&gt;，运行时都不会注入。只要原文有变量被改写过、又还留着 &lt;code&gt;CI_&lt;/code&gt; 变量，注意里就会有这一句：「GitLab CI_* 预定义变量已近似改写；其余 CI_* 运行时不会注入」。自己写的脚本可以改用平台提供的环境变量 &lt;code&gt;GIT_REPO_URL&lt;/code&gt;、&lt;code&gt;GIT_BRANCH&lt;/code&gt;、&lt;code&gt;GIT_COMMIT_SHA&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;还有一类变量要留意：名字以 &lt;code&gt;TOKEN&lt;/code&gt;、&lt;code&gt;SECRET&lt;/code&gt;、&lt;code&gt;PASSWORD&lt;/code&gt;、&lt;code&gt;PASSWD&lt;/code&gt;、&lt;code&gt;PRIVATE_KEY&lt;/code&gt;、&lt;code&gt;ACCESS_KEY&lt;/code&gt; 或 &lt;code&gt;API_KEY&lt;/code&gt; 结尾时，导入会把这个名字写进 Job 的 &lt;code&gt;secrets&lt;/code&gt; 名单，并在注意里写「&lt;code&gt;$名字 → env.名字，并写入 Job secrets 名单&lt;/code&gt;」。这时 AngusGit 里必须先有同名凭证，否则 Job 会失败并提示 &lt;code&gt;Credential not visible or expired&lt;/code&gt;。导入不会替你创建凭证，要自己在 &lt;strong&gt;CI/CD → 配置 → 凭证&lt;/strong&gt; 里新建。第 1 节没有这类变量，所以初稿里没有 &lt;code&gt;secrets&lt;/code&gt;。&lt;/p&gt;

&lt;h2&gt;
  
  
  5. 保存前建议改成什么样
&lt;/h2&gt;

&lt;p&gt;这份初稿不能直接保存，因为有两处和原配置的意思相反：一是 &lt;code&gt;only: [main]&lt;/code&gt; 没有变成 &lt;code&gt;branches: [main]&lt;/code&gt;，其他分支也会触发；二是 &lt;code&gt;when: manual&lt;/code&gt; 丢了，&lt;code&gt;deploy&lt;/code&gt; 仍会在 &lt;code&gt;test&lt;/code&gt; 之后自动运行。&lt;/p&gt;

&lt;p&gt;建议把它拆成两条流水线。第一条保留构建和测试，并补上分支限制。镜像先沿用转换结果；如果内网拉不到 Docker Hub，再把 &lt;code&gt;maven:3.9-eclipse-temurin-21&lt;/code&gt; 换成平台镜像 &lt;code&gt;repo.anguskit.com/docker-public/angus-ci-maven:21&lt;/code&gt;。下面是手动改完后的写法，并非导入器直接给出的初稿：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1"&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;imported-from-gitlab&lt;/span&gt;
&lt;span class="na"&gt;triggers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;on"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;push&lt;/span&gt;
    &lt;span class="s"&gt;branches&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;on"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pull_request&lt;/span&gt;
    &lt;span class="s"&gt;branches&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;MAVEN_OPTS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-Dmaven.repo.local=.m2"&lt;/span&gt;
&lt;span class="na"&gt;pipeline&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DOCKER&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;maven:3.9-eclipse-temurin-21&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;docker&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;script&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mvn -B -DskipTests package&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;runner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DOCKER&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;maven:3.9-eclipse-temurin-21&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;docker&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;script&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mvn -B test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;注意 &lt;code&gt;needs&lt;/code&gt; 只决定先后顺序，不会把 &lt;code&gt;target/*.jar&lt;/code&gt; 传给 &lt;code&gt;test&lt;/code&gt;。这里之所以还能分成两个 Job，是因为 &lt;code&gt;test&lt;/code&gt; 自己执行 &lt;code&gt;mvn test&lt;/code&gt;，用不到那个 jar。如果部署脚本要读这个 jar，就把打包和部署写成同一个 Job 里的两个步骤。&lt;/p&gt;

&lt;p&gt;第二条流水线只放部署，并且只允许手动运行。脚本参数里的 &lt;code&gt;${{ commitSha }}&lt;/code&gt; 仍然是完整 SHA。建议写成：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1"&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy&lt;/span&gt;
&lt;span class="na"&gt;triggers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;on"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;workflow_dispatch&lt;/span&gt;
&lt;span class="na"&gt;pipeline&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NATIVE&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;script&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./deploy.sh "${{ commitSha }}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;如果希望部署仍然跟在构建后面，但要由指定的人批准后才继续，就给这个 Job 加 &lt;code&gt;approval&lt;/code&gt;；已经被丢掉的 &lt;code&gt;when: manual&lt;/code&gt; 起不到这个作用。具体做法见文末参考里的流水线审批一文。&lt;/p&gt;

&lt;h2&gt;
  
  
  6. 真要保存时先验证一次
&lt;/h2&gt;

&lt;p&gt;改完之后，按下面三步把流水线建起来并验证：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;通过运行前检查&lt;/strong&gt;
如果对话框是从列表打开的，点 &lt;strong&gt;创建流水线&lt;/strong&gt;；如果是从空白创建页打开的，先点 &lt;strong&gt;填入编辑器&lt;/strong&gt;，再在创建页保存。保存前平台会做运行前检查：通过时页面显示「检查通过，可以保存。」；提示「请先修复错误」时，按提示修改。最常见的原因是 YAML 里还留着 &lt;code&gt;stages&lt;/code&gt;、&lt;code&gt;variables&lt;/code&gt; 这类 GitLab 字段。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;手动跑一次&lt;/strong&gt;
在流水线详情页点 &lt;strong&gt;触发&lt;/strong&gt;，&lt;strong&gt;分支 / 标签&lt;/strong&gt; 填一个测试分支，然后确认 Job 被带 &lt;code&gt;docker&lt;/code&gt; 标签的运行器接走。要小心：如果 &lt;code&gt;deploy&lt;/code&gt; 还留在同一条流水线里，这次运行就会真的执行部署命令。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;再看自动触发&lt;/strong&gt;
手动运行通过后，再发起一个拉取请求，确认 &lt;code&gt;pull_request&lt;/code&gt; 能正常触发，并且结果写回了拉取请求页的状态检查（页面上标明流水线是否通过的那一项）。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;迁移期间，&lt;code&gt;.gitlab-ci.yml&lt;/code&gt; 可以先留在仓库里，AngusGit 不会执行它。等新的流水线稳定运行之后再删除。&lt;/p&gt;

&lt;h2&gt;
  
  
  常见问题
&lt;/h2&gt;

&lt;p&gt;迁移后最常遇到的情况如下：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;你看到的&lt;/th&gt;
&lt;th&gt;原因&lt;/th&gt;
&lt;th&gt;怎么处理&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Job 一直排队&lt;/td&gt;
&lt;td&gt;没有在线的运行器同时满足 &lt;code&gt;mode&lt;/code&gt; 和 &lt;code&gt;labels&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;运行器要带 &lt;code&gt;docker&lt;/code&gt; 标签，并能跑对应的执行模式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 没在容器里，直接跑在宿主机上&lt;/td&gt;
&lt;td&gt;原 Job 没有写 &lt;code&gt;image&lt;/code&gt;，变成了 &lt;code&gt;NATIVE&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;补上 &lt;code&gt;runner.mode: DOCKER&lt;/code&gt; 和 &lt;code&gt;runner.image&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;后面的 Job 找不到 &lt;code&gt;target/*.jar&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;artifacts&lt;/code&gt; 在未映射里，&lt;code&gt;needs&lt;/code&gt; 不传文件&lt;/td&gt;
&lt;td&gt;合成一个 Job，或经制品库中转&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;部署在没人点击时就跑了&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;when: manual&lt;/code&gt; 未映射，Job 仍在流水线里&lt;/td&gt;
&lt;td&gt;删掉这个 Job，或改成 &lt;code&gt;workflow_dispatch&lt;/code&gt; / &lt;code&gt;approval&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;非 &lt;code&gt;main&lt;/code&gt; 的分支也跑了部署&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;only: [main]&lt;/code&gt; 没有写成 &lt;code&gt;branches&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;给 &lt;code&gt;push&lt;/code&gt; 和 &lt;code&gt;pull_request&lt;/code&gt; 加上 &lt;code&gt;branches: [main]&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;脚本里某个路径或编号是空的&lt;/td&gt;
&lt;td&gt;用了没有注入的 &lt;code&gt;CI_*&lt;/code&gt; 变量&lt;/td&gt;
&lt;td&gt;按第 4 节替换，短 SHA 用 &lt;code&gt;$GIT_COMMIT_SHA&lt;/code&gt; 自己截&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;合并进 &lt;code&gt;main&lt;/code&gt; 后没有自动构建&lt;/td&gt;
&lt;td&gt;拉取请求页的合并不会触发 &lt;code&gt;on: push&lt;/code&gt;，只有 &lt;code&gt;git push&lt;/code&gt; 会&lt;/td&gt;
&lt;td&gt;用 &lt;code&gt;pull_request&lt;/code&gt; 在合并前跑构建和测试&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;能不能一次导入多个文件？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;不能，对话框一次只处理一份 YAML。如果项目用了 &lt;code&gt;include&lt;/code&gt;（在一份 YAML 里引用其他 YAML 片段），要先把被引用的文件合并成一份再导入，否则未映射里会写「include（不拉取远程/本地片段，须先展开再导入）」。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;转换结果能不能直接用？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;这份示例的初稿不能直接用，因为 &lt;code&gt;deploy&lt;/code&gt; 会自动运行，分支限制也丢了。先按第 5 节改好这两处，再保存。&lt;/p&gt;

&lt;h2&gt;
  
  
  小结
&lt;/h2&gt;

&lt;p&gt;把 &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; 贴进 &lt;strong&gt;导入 GitLab CI&lt;/strong&gt; 再点 &lt;strong&gt;转换&lt;/strong&gt;，得到的是一份需要人工核对的初稿，本例名为 &lt;code&gt;imported-from-gitlab&lt;/code&gt;，其中 &lt;code&gt;script&lt;/code&gt;、&lt;code&gt;image&lt;/code&gt;、&lt;code&gt;tags&lt;/code&gt;、&lt;code&gt;variables&lt;/code&gt;、&lt;code&gt;stages&lt;/code&gt; 分别转成了步骤、&lt;code&gt;DOCKER&lt;/code&gt; 模式、标签、&lt;code&gt;env&lt;/code&gt; 和 &lt;code&gt;needs&lt;/code&gt;。以第 1 节为例，未映射的是 &lt;code&gt;jobs.build.artifacts&lt;/code&gt;、两处 &lt;code&gt;rules/only/except&lt;/code&gt; 和 &lt;code&gt;jobs.deploy.when&lt;/code&gt;；注意清单则提醒 &lt;code&gt;deploy&lt;/code&gt; 变成了 &lt;code&gt;NATIVE&lt;/code&gt;，&lt;code&gt;$CI_COMMIT_SHORT_SHA&lt;/code&gt; 变成了完整的 &lt;code&gt;${{ commitSha }}&lt;/code&gt;。最要紧的是，&lt;code&gt;when: manual&lt;/code&gt; 不会变成手动 Job，&lt;code&gt;only: [main]&lt;/code&gt; 也不会变成分支限制，不改的话部署会在其他分支上自动运行。所以要逐项核对、改完再创建流水线，并自己建好需要的凭证，因为导入不会新建凭证。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;结论：&lt;/strong&gt; 导入 GitLab CI 给出的只是转换后的初稿，保存前要处理未映射的 &lt;code&gt;artifacts&lt;/code&gt; 和 &lt;code&gt;when&lt;/code&gt;，把丢掉的 &lt;code&gt;only: [main]&lt;/code&gt; 补成触发器上的 &lt;code&gt;branches: [main]&lt;/code&gt;，并记住 &lt;code&gt;${{ commitSha }}&lt;/code&gt; 拿到的是完整 SHA，不是短 SHA。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  参考文章
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/docs/git/manual/10-pipeline-basics" rel="noopener noreferrer"&gt;AngusGit 手册：流水线基础&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/docs/git/manual/11-pipeline-advanced" rel="noopener noreferrer"&gt;AngusGit 手册：流水线进阶&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/angusgit-yaml-cicd-pipeline-getting-started" rel="noopener noreferrer"&gt;仓库能推之后：内置流水线 YAML 怎么写&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/migrate-gitlab-to-angusgit-checklist" rel="noopener noreferrer"&gt;从 GitLab 迁到 AngusGit：迁移清单、双跑和回滚&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/pipeline-approval-who-can-continue" rel="noopener noreferrer"&gt;流水线跑到审批节点：谁点了才能继续&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/pipeline-runner-not-picking-up-jobs" rel="noopener noreferrer"&gt;运行器不接任务：标签、在线和并发对不上&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>angusgit</category>
      <category>gitlab</category>
    </item>
    <item>
      <title>Issue 怎么关联合并请求：写上 Closes #编号，合并后自动关闭</title>
      <dc:creator>Wen Ping</dc:creator>
      <pubDate>Sat, 31 Oct 2026 04:10:01 +0000</pubDate>
      <link>https://dev.to/wenping/issue-zen-yao-guan-lian-he-bing-qing-qiu-xie-shang-closes-bian-hao-he-bing-hou-zi-dong-guan-bi-c9n</link>
      <guid>https://dev.to/wenping/issue-zen-yao-guan-lian-he-bing-qing-qiu-xie-shang-closes-bian-hao-he-bing-hou-zi-dong-guan-bi-c9n</guid>
      <description>&lt;p&gt;这篇讲怎么让问题和拉取请求连上，并在合并后自动关闭问题。看完你可以在拉取请求的标题或描述里写 &lt;code&gt;Closes #编号&lt;/code&gt;，刷新后确认两边页面互相列出对方；关键字要用英文，写在评论里或只写 &lt;code&gt;#编号&lt;/code&gt; 不会生效。&lt;/p&gt;

&lt;p&gt;下文用线上仓库 &lt;a href="https://git.anguskit.com/xlliu/test-pr-example" rel="noopener noreferrer"&gt;xlliu/test-pr-example&lt;/a&gt;。问题 &lt;a href="https://git.anguskit.com/issues/xlliu/test-pr-example/4" rel="noopener noreferrer"&gt;#4&lt;/a&gt; 标题是「折扣示例没有说明空购物车」，类型是 &lt;strong&gt;功能&lt;/strong&gt;。拉取请求 &lt;a href="https://git.anguskit.com/pulls/xlliu/test-pr-example/9" rel="noopener noreferrer"&gt;#9&lt;/a&gt; 从 &lt;code&gt;feature/link-empty-cart&lt;/code&gt; 合向 &lt;code&gt;main&lt;/code&gt;，描述里写了 &lt;code&gt;Closes #4&lt;/code&gt;。两条都还开着，页面上是合并前的样子。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flf1ii7c5mzn19fwz43ga.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flf1ii7c5mzn19fwz43ga.png" alt="问题 #4 与拉取请求 #9 的关联步骤" width="800" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. 先认清编号
&lt;/h2&gt;

&lt;p&gt;AngusGit 界面上把 Issue 叫作 &lt;strong&gt;问题&lt;/strong&gt;，侧栏里的入口和仓库代码页的页签都叫这个名字。&lt;/p&gt;

&lt;p&gt;问题按仓库单独编号，从 &lt;code&gt;#1&lt;/code&gt; 开始递增。拉取请求也按仓库单独编号，两套编号互不相干，所以同一个仓库里可以同时有问题 &lt;code&gt;#1&lt;/code&gt; 和拉取请求 &lt;code&gt;#1&lt;/code&gt;。下文写的 &lt;code&gt;Closes #4&lt;/code&gt;，指的都是 &lt;strong&gt;问题&lt;/strong&gt; 的编号，不是拉取请求 &lt;code&gt;#4&lt;/code&gt;。&lt;/p&gt;

&lt;h2&gt;
  
  
  2. 建一条问题，记下编号
&lt;/h2&gt;

&lt;p&gt;已经有问题的话，打开它，记下标题旁边的编号，跳到第 3 节。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;打开新建表单&lt;/strong&gt;
侧栏点 &lt;strong&gt;问题&lt;/strong&gt;，在右上角点 &lt;strong&gt;新建问题&lt;/strong&gt;。页面标题是 &lt;strong&gt;创建新问题&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;填写&lt;/strong&gt;
&lt;strong&gt;仓库&lt;/strong&gt; 选 &lt;code&gt;xlliu/test-pr-example&lt;/code&gt;。&lt;strong&gt;类型&lt;/strong&gt; 选 &lt;strong&gt;Bug&lt;/strong&gt;、&lt;strong&gt;功能&lt;/strong&gt;、&lt;strong&gt;问题&lt;/strong&gt; 或 &lt;strong&gt;增强&lt;/strong&gt; 中的一个，创建后不能修改。这条示例选的是 &lt;strong&gt;功能&lt;/strong&gt;，标题是「折扣示例没有说明空购物车」。&lt;strong&gt;描述&lt;/strong&gt; 写清缺了什么。标签、指派人可以先不填。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;创建&lt;/strong&gt;
点 &lt;strong&gt;创建问题&lt;/strong&gt;，提示 &lt;strong&gt;问题创建成功&lt;/strong&gt;。打开这条问题，标题旁边的 &lt;code&gt;#&lt;/code&gt; 加数字就是它的编号。这条示例是 &lt;code&gt;#4&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  3. 在拉取请求里写上关键字
&lt;/h2&gt;

&lt;p&gt;修好代码、推到一个新分支之后，按平常的方式发起拉取请求。在 &lt;strong&gt;标题&lt;/strong&gt; 或 &lt;strong&gt;描述&lt;/strong&gt; 里，写上关键字加问题编号：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docs: 说明空购物车不计算折扣

Closes #4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://git.anguskit.com/pulls/xlliu/test-pr-example/9" rel="noopener noreferrer"&gt;#9&lt;/a&gt; 的描述就是这两行。关键字和编号之间要有一个空格。下面九个关键字都可以，大小写不限，效果完全一样：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;词根&lt;/th&gt;
&lt;th&gt;可用的英文写法&lt;/th&gt;
&lt;th&gt;示例&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;close&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;close&lt;/code&gt;、&lt;code&gt;closes&lt;/code&gt;、&lt;code&gt;closed&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Closes #4&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fix&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;fix&lt;/code&gt;、&lt;code&gt;fixes&lt;/code&gt;、&lt;code&gt;fixed&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Fixes #4&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;resolve&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;resolve&lt;/code&gt;、&lt;code&gt;resolves&lt;/code&gt;、&lt;code&gt;resolved&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Resolves #4&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;一次关闭多个问题时，每个编号前面都要写关键字，例如 &lt;code&gt;Fixes #4, fixes #5&lt;/code&gt;。写成 &lt;code&gt;Fixes #4, #5&lt;/code&gt; 的话，只有 &lt;code&gt;#4&lt;/code&gt; 会被关闭。&lt;code&gt;#5&lt;/code&gt; 在这里只说明写法，这个仓库里没有问题 &lt;code&gt;#5&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;写在不同地方，效果不一样：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;写在哪里&lt;/th&gt;
&lt;th&gt;两边页面会互相列出&lt;/th&gt;
&lt;th&gt;合并后自动关闭&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;拉取请求的标题或描述&lt;/td&gt;
&lt;td&gt;会&lt;/td&gt;
&lt;td&gt;会&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;拉取请求里某个提交的提交说明&lt;/td&gt;
&lt;td&gt;不会&lt;/td&gt;
&lt;td&gt;会&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;拉取请求或问题下面的评论&lt;/td&gt;
&lt;td&gt;不会&lt;/td&gt;
&lt;td&gt;不会&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;只写 &lt;code&gt;#4&lt;/code&gt;，前面没有关键字&lt;/td&gt;
&lt;td&gt;不会&lt;/td&gt;
&lt;td&gt;不会&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;中文「关闭 #4」「修复 #4」&lt;/td&gt;
&lt;td&gt;不会&lt;/td&gt;
&lt;td&gt;不会&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;写成 &lt;code&gt;owner/other-repo#4&lt;/code&gt; 这种带仓库名的引用&lt;/td&gt;
&lt;td&gt;不会&lt;/td&gt;
&lt;td&gt;不会&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;最稳的做法是写在拉取请求的描述里：合并前就能在页面上确认两边已经连上，不用等到合并那一刻才知道。&lt;/p&gt;

&lt;h2&gt;
  
  
  4. 合并之前，确认两边已经连上
&lt;/h2&gt;

&lt;p&gt;关联是页面打开时根据标题和描述实时算出来的，保存描述之后刷新页面就能看到。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;拉取请求页&lt;/strong&gt; 会出现 &lt;strong&gt;关联的 Issue&lt;/strong&gt; 一栏，列出 &lt;code&gt;#4&lt;/code&gt; 和它的标题，旁边标着 &lt;strong&gt;合并后将关闭&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcz48rphgdwvvcum2h7gq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcz48rphgdwvvcum2h7gq.png" alt="拉取请求 #9：关联的 Issue 标着合并后将关闭" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;问题页&lt;/strong&gt; 会出现 &lt;strong&gt;关联的 Pull Request&lt;/strong&gt; 一栏，列出这条拉取请求，状态是 &lt;strong&gt;打开&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feln86xvknkoogu3ni9yk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feln86xvknkoogu3ni9yk.png" alt="问题 #4：关联的拉取请求 #9 仍是打开" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;两边都没出现时，回到描述里检查关键字是不是拼对了、和 &lt;code&gt;#&lt;/code&gt; 之间有没有空格、编号是不是这个仓库的问题。把描述里的关键字删掉，关联也会随之消失，不用另外解除。&lt;/p&gt;

&lt;h2&gt;
  
  
  5. 合并之后，问题自动关闭
&lt;/h2&gt;

&lt;p&gt;拉取请求满足合并条件后，点 &lt;strong&gt;合并拉取请求&lt;/strong&gt;。确认框里会多出一行 &lt;strong&gt;合并该 PR 后将自动关闭以下 1 个 Issue：&lt;/strong&gt;，下面列着 &lt;code&gt;#4&lt;/code&gt;。点 &lt;strong&gt;确认合并&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;这条示例没有合并，所以问题 &lt;code&gt;#4&lt;/code&gt; 和拉取请求 &lt;code&gt;#9&lt;/code&gt; 现在都还是 &lt;strong&gt;打开&lt;/strong&gt;。合并完成后打开问题 &lt;code&gt;#4&lt;/code&gt;，页面会变成：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;状态变成 &lt;strong&gt;已关闭&lt;/strong&gt;，关闭原因是 &lt;strong&gt;已完成&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;时间线里多出一条「某某 关闭了此问题」，「某某」是点合并的那个人。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;关联的 Pull Request&lt;/strong&gt; 里，那条请求的状态变成 &lt;strong&gt;已合并&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;有两点要提前知道：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;合并到任何分支都会关闭。&lt;/strong&gt; 拉取请求的目标分支是 &lt;code&gt;release&lt;/code&gt; 之类的非默认分支时，合并后问题同样会被关闭。如果你希望代码进了 &lt;code&gt;main&lt;/code&gt; 才算修好，就只在合进 &lt;code&gt;main&lt;/code&gt; 的那条拉取请求里写关键字。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;合并的人不需要有关闭问题的权限。&lt;/strong&gt; 能合并这条拉取请求，就能通过它关闭问题。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. 没有自动关闭时
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;你看到的&lt;/th&gt;
&lt;th&gt;原因&lt;/th&gt;
&lt;th&gt;怎么处理&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;拉取请求页没有 &lt;strong&gt;关联的 Issue&lt;/strong&gt; 这一栏&lt;/td&gt;
&lt;td&gt;标题和描述里没有能识别的关键字，例如只写了 &lt;code&gt;#4&lt;/code&gt;、写了中文「关闭」，或者关键字和 &lt;code&gt;#&lt;/code&gt; 连在一起&lt;/td&gt;
&lt;td&gt;改成 &lt;code&gt;Closes #4&lt;/code&gt; 这样的写法，保存后刷新页面&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;关键字写在提交说明里，合并前页面上看不到关联&lt;/td&gt;
&lt;td&gt;提交说明只在合并时检查，页面上的关联只看标题和描述&lt;/td&gt;
&lt;td&gt;这是正常的，合并后照样会关闭。想提前确认，就把关键字也写进描述&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;合并了，问题仍然 &lt;strong&gt;打开&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;关键字写在了评论里；写的是另一个仓库的问题；或者代码是直接推送到分支的，没有经过拉取请求&lt;/td&gt;
&lt;td&gt;只有通过拉取请求合并，并且关键字写在标题、描述或提交说明里，才会自动关闭。需要的话在问题页手动 &lt;strong&gt;关闭&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;一次写了多个编号，只关了第一个&lt;/td&gt;
&lt;td&gt;写成了 &lt;code&gt;Fixes #4, #5&lt;/code&gt;，第二个编号前面没有关键字&lt;/td&gt;
&lt;td&gt;每个编号前都写关键字。漏关的那个手动关闭&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;问题被提前关掉了&lt;/td&gt;
&lt;td&gt;关键字写在了合进非默认分支的拉取请求里&lt;/td&gt;
&lt;td&gt;在问题页点 &lt;strong&gt;重新打开&lt;/strong&gt;，以后只在合进 &lt;code&gt;main&lt;/code&gt; 的那条请求里写关键字&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  常见问题
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;有没有手动把问题和拉取请求连起来的按钮？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;没有。关联完全由拉取请求标题和描述里的关键字决定。要建立关联就写上关键字，要解除就删掉它。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;只想在拉取请求里提一下某个问题，不想合并后关掉它，怎么写？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;不要用关键字，写成「相关问题：#4」这样的普通文字即可。它不会出现在关联列表里，合并后也不会关闭问题。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;问题自动关错了，怎么恢复？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;打开问题，点 &lt;strong&gt;重新打开&lt;/strong&gt;。状态回到 &lt;strong&gt;打开&lt;/strong&gt;，原来的评论和时间线都还在。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;直接推送到 main 的提交里写 &lt;code&gt;Fixes #4&lt;/code&gt;，会关闭问题吗？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;不会。自动关闭只在拉取请求合并时发生。直接推送的提交不会触发。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;手动关闭问题时可以选原因吗？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;可以。在问题页点 &lt;strong&gt;关闭&lt;/strong&gt;，在 &lt;strong&gt;请选择关闭原因&lt;/strong&gt; 里选 &lt;strong&gt;已完成&lt;/strong&gt; 或 &lt;strong&gt;不计划实施&lt;/strong&gt;，再点 &lt;strong&gt;确认关闭&lt;/strong&gt;。自动关闭的原因固定是 &lt;strong&gt;已完成&lt;/strong&gt;。&lt;/p&gt;

&lt;h2&gt;
  
  
  小结
&lt;/h2&gt;

&lt;p&gt;让问题和拉取请求连上，只需要在拉取请求的标题或描述里写一句英文关键字加编号，例如 &lt;code&gt;Closes #4&lt;/code&gt;。写好之后先刷新页面，确认拉取请求页的 &lt;strong&gt;关联的 Issue&lt;/strong&gt; 里标着 &lt;strong&gt;合并后将关闭&lt;/strong&gt;。合并之后，问题自动关闭，原因记为 &lt;strong&gt;已完成&lt;/strong&gt;，时间线写明是谁合并的。中文关键字、只写编号、写在评论里，或者直接推送代码，都不会触发关联和自动关闭。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;结论：&lt;/strong&gt; 在拉取请求的标题或描述里写 &lt;code&gt;Closes #编号&lt;/code&gt;（或 &lt;code&gt;Fixes #编号&lt;/code&gt;、&lt;code&gt;Resolves #编号&lt;/code&gt;），两边页面就会互相列出关联，并在拉取请求合并后自动把问题置为 &lt;strong&gt;已关闭&lt;/strong&gt;（&lt;strong&gt;已完成&lt;/strong&gt;）。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  参考文章
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/docs/git/manual/08-issue-milestone" rel="noopener noreferrer"&gt;AngusGit 手册：问题与里程碑&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/docs/git/manual/06-pull-request" rel="noopener noreferrer"&gt;AngusGit 手册：拉取请求（创建、合并条件）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/protect-git-main-branch-pull-request" rel="noopener noreferrer"&gt;怎么保护 Git 主分支：先开分支保护，再用合并请求合代码&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/code-review-in-pull-request" rel="noopener noreferrer"&gt;在合并请求里做完代码评审：看 Diff、写行评、通过或打回&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>angusgit</category>
      <category>pullrequest</category>
    </item>
    <item>
      <title>导入 Git 仓库：填一个源地址，拿到默认分支的代码</title>
      <dc:creator>Wen Ping</dc:creator>
      <pubDate>Sat, 31 Oct 2026 04:09:24 +0000</pubDate>
      <link>https://dev.to/wenping/dao-ru-git-cang-ku-tian-ge-yuan-di-zhi-na-dao-mo-ren-fen-zhi-de-dai-ma-115c</link>
      <guid>https://dev.to/wenping/dao-ru-git-cang-ku-tian-ge-yuan-di-zhi-na-dao-mo-ren-fen-zhi-de-dai-ma-115c</guid>
      <description>&lt;p&gt;这篇讲只用一个源地址把公开仓库导入 AngusGit 时，会得到什么，以及什么时候该改用推送。看完你可以填写 &lt;strong&gt;导入仓库&lt;/strong&gt; 完成导入，并知道结果只有默认分支当时的文件和一条新提交；源仓库是私有的，或要保留完整历史、其他分支和标签，就先建空仓库，再用 &lt;code&gt;git clone --bare&lt;/code&gt; 推送。&lt;/p&gt;

&lt;p&gt;先登录 AngusGit。导入在侧栏 &lt;strong&gt;仓库&lt;/strong&gt; 右上角的 &lt;strong&gt;导入仓库&lt;/strong&gt;。要克隆导入后的私有仓库，或改用推送保留历史，先准备凭据。走 SSH：本机没有 &lt;code&gt;~/.ssh/id_ed25519.pub&lt;/code&gt; 时执行 &lt;code&gt;ssh-keygen -t ed25519 -C "you@example.com"&lt;/code&gt;，路径直接回车，再 &lt;code&gt;cat ~/.ssh/id_ed25519.pub&lt;/code&gt;，把以 &lt;code&gt;ssh-ed25519&lt;/code&gt; 开头的一整行贴到 &lt;strong&gt;我的 → SSH密钥&lt;/strong&gt; 的 &lt;strong&gt;添加SSH密钥&lt;/strong&gt;。地址用 &lt;code&gt;ssh://git@主机:2222/所有者/仓库.git&lt;/code&gt;，用户名是 &lt;code&gt;git&lt;/code&gt;。走 HTTPS：到 &lt;strong&gt;我的 → 访问令牌&lt;/strong&gt; 点 &lt;strong&gt;生成新令牌&lt;/strong&gt;，&lt;strong&gt;密码验证&lt;/strong&gt; 填登录密码。只克隆勾 &lt;code&gt;git:read&lt;/code&gt;、仓库权限 &lt;strong&gt;READ&lt;/strong&gt;；要推送则勾 &lt;code&gt;git:write&lt;/code&gt;、权限 &lt;strong&gt;WRITE&lt;/strong&gt;。生成后马上复制。用户名填 AngusGit 登录名，密码填令牌。&lt;/p&gt;

&lt;p&gt;文中以 &lt;code&gt;git.anguskit.com&lt;/code&gt; 上的一次真实导入对照：源地址是 &lt;code&gt;https://github.com/octocat/Hello-World.git&lt;/code&gt;，所有者是当前账号 &lt;strong&gt;Angus / xlliu&lt;/strong&gt;，仓库名是 &lt;code&gt;hello-world-import&lt;/code&gt;，并打开了 &lt;strong&gt;设为私有仓库&lt;/strong&gt;，导入完成后代码页地址是 &lt;code&gt;https://git.anguskit.com/code/xlliu/hello-world-import&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;这台实例上已有的 &lt;code&gt;anguskit/AngusRepo&lt;/code&gt; 是制品库产品自己的仓库，并不是从 GitHub 导入的快照，不要用它的名字再导入一次，否则会提示 &lt;strong&gt;仓库「anguskit/AngusRepo」已存在&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F168a7fc733y5dv2a6jul.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F168a7fc733y5dv2a6jul.png" alt="导入只留下源仓库默认分支当时的文件" width="799" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. 导入会带过来什么
&lt;/h2&gt;

&lt;p&gt;动手之前，先弄清导入会得到什么，再决定用导入还是推送。下表前两列是导入的规则，最后一列是 Hello-World 导入后的实际表现，可以对照着看。&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;内容&lt;/th&gt;
&lt;th&gt;是否带过来&lt;/th&gt;
&lt;th&gt;Hello-World 的表现&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;源仓库默认分支当前的全部文件&lt;/td&gt;
&lt;td&gt;带过来&lt;/td&gt;
&lt;td&gt;只有 &lt;code&gt;README&lt;/code&gt;，13 B，内容是 &lt;code&gt;Hello World!&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;原来的提交历史&lt;/td&gt;
&lt;td&gt;不带&lt;/td&gt;
&lt;td&gt;只有 1 条提交，说明是 &lt;code&gt;Initial commit from https://github.com/octocat/Hello-World.git&lt;/code&gt;，短哈希 &lt;code&gt;25861fe&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;默认分支以外的分支&lt;/td&gt;
&lt;td&gt;不带&lt;/td&gt;
&lt;td&gt;页签是 &lt;strong&gt;分支 1&lt;/strong&gt;，名字是 &lt;code&gt;main&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;标签（Tag）&lt;/td&gt;
&lt;td&gt;不带&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;标签 0&lt;/strong&gt;，&lt;strong&gt;发布 0&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;子模块（Submodule）&lt;/td&gt;
&lt;td&gt;不带&lt;/td&gt;
&lt;td&gt;Hello-World 没有子模块&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Issue、合并请求、Wiki、CI、成员权限&lt;/td&gt;
&lt;td&gt;不带&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;问题 0&lt;/strong&gt;，&lt;strong&gt;Wiki 0&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;导入之后源仓库的新提交&lt;/td&gt;
&lt;td&gt;不自动同步&lt;/td&gt;
&lt;td&gt;导入是一次性操作&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;还有一处细节：Hello-World 在 GitHub 上的默认分支叫 &lt;code&gt;master&lt;/code&gt;，导入后文件都在，但 AngusGit 上的分支名是平台默认的 &lt;code&gt;main&lt;/code&gt;，不会沿用 &lt;code&gt;master&lt;/code&gt; 这个名字。&lt;/p&gt;

&lt;p&gt;所以，导入适合只需要代码当前版本的场景，例如拿一个开源项目做内部二次开发，或者旧仓库的历史本来就不重要。如果要保留每一次提交，或者要带上其他分支和标签，请直接看第 5 节。&lt;/p&gt;

&lt;h2&gt;
  
  
  2. 导入之前确认两件事
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;第一，源仓库要不登录就能读。&lt;/strong&gt; 可见性决定谁能看到仓库，导入表单里没有填写源站账号或令牌的地方，AngusGit 只能以匿名身份去拉代码。因此公开仓库没有问题，私有仓库则会在提交表单时报 &lt;strong&gt;源仓库URL「…」无效或无法正常访问&lt;/strong&gt;。也不要把账号或令牌拼进 URL 来绕过这个限制，因为这个地址会原样写进导入生成的提交说明，留在新仓库的历史里，所有能看到仓库的人都看得到。源仓库是私有的，请用第 5 节的推送方式。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;第二，AngusGit 所在的服务器要能连上源站。&lt;/strong&gt; 真正去拉代码的是 AngusGit 服务器，跟你自己的电脑能不能上外网无关，所以内网服务器访问不了 &lt;code&gt;github.com&lt;/code&gt; 时，导入同样会失败。想提前确认，可以在那台服务器上执行一次 &lt;code&gt;git ls-remote &amp;lt;源地址&amp;gt;&lt;/code&gt;，这条命令会列出源仓库的分支，能打印出来就说明网络是通的。以 Hello-World 为例，它能导进来，就说明那台服务器当时访问得到 &lt;code&gt;github.com&lt;/code&gt;。&lt;/p&gt;

&lt;h2&gt;
  
  
  3. 导入一个仓库
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;打开导入页&lt;/strong&gt;
在侧栏点 &lt;strong&gt;仓库&lt;/strong&gt;，再点右上角的 &lt;strong&gt;导入仓库&lt;/strong&gt;。打开的是一整页表单，标题是 &lt;strong&gt;导入仓库&lt;/strong&gt;，下方说明是「从其他版本控制系统导入现有仓库」。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;填写源地址&lt;/strong&gt;
&lt;strong&gt;仓库 URL&lt;/strong&gt; 填源仓库的 HTTPS 克隆地址，以 Hello-World 为例就是 &lt;code&gt;https://github.com/octocat/Hello-World.git&lt;/code&gt;。表单提示写着支持 Github、Gitlab、Gitee、SourceForge；如果地址不是以 &lt;code&gt;https://&lt;/code&gt;、&lt;code&gt;http://&lt;/code&gt; 或 &lt;code&gt;git@&lt;/code&gt; 开头，输入框下方会提示 &lt;strong&gt;请输入正确的仓库URL&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;选所有者，填仓库名称&lt;/strong&gt;
&lt;strong&gt;所有者&lt;/strong&gt; 默认是当前登录账号，下拉里还能选你担任维护者的团队；示例保留了默认的 &lt;strong&gt;Angus / xlliu&lt;/strong&gt;。&lt;strong&gt;仓库名称&lt;/strong&gt; 不会根据地址自动填写，需要手动输入，示例填的是 &lt;code&gt;hello-world-import&lt;/code&gt;，输入框前面会显示命名空间前缀 &lt;code&gt;xlliu /&lt;/code&gt;。名称只能用中文、字母、数字、连字符和下划线，输入的点号会被直接去掉。&lt;strong&gt;描述&lt;/strong&gt; 可以留空，示例就没有填；框里灰色的「从 GitHub 导入的仓库」只是占位文字，并不是已经填好的内容。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;决定是否私有&lt;/strong&gt;
&lt;strong&gt;设为私有仓库&lt;/strong&gt; 默认是关闭的。代码只给团队内部看时，记得把它打开；示例打开了这个开关，所以导入得到的是私有仓库。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;开始导入&lt;/strong&gt;
&lt;strong&gt;仓库 URL&lt;/strong&gt;、&lt;strong&gt;所有者&lt;/strong&gt; 和 &lt;strong&gt;仓库名称&lt;/strong&gt; 都填好之后，&lt;strong&gt;开始导入&lt;/strong&gt; 按钮才能点击。点击后按钮会变成禁用状态，一直等到代码拉完；仓库较大时耗时更长，这期间不要关闭页面。导入完成后表单会自动关闭，新仓库出现在仓库列表里。Hello-World 很小，很快就回到了列表。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy8lht8tu7uqelp1us4xq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy8lht8tu7uqelp1us4xq.png" alt="导入表单已填好：仓库 URL 是 Hello-World，所有者 Angus / xlliu，名称 hello-world-import，私有开关已打开" width="799" height="703"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;上图是点击 &lt;strong&gt;开始导入&lt;/strong&gt; 之前的表单：描述留空，私有开关已经打开。&lt;/p&gt;

&lt;h2&gt;
  
  
  4. 验收：在代码页确认只有一条提交
&lt;/h2&gt;

&lt;p&gt;导入完成后，打开仓库的代码页，以示例为例就是 &lt;code&gt;https://git.anguskit.com/code/xlliu/hello-world-import&lt;/code&gt;，然后按下面几处核对。页头标着 &lt;strong&gt;私有&lt;/strong&gt;，统计栏依次是 &lt;strong&gt;分支 1&lt;/strong&gt;、&lt;strong&gt;标签 0&lt;/strong&gt;、&lt;strong&gt;发布 0&lt;/strong&gt;、&lt;strong&gt;问题 0&lt;/strong&gt;、&lt;strong&gt;Wiki 0&lt;/strong&gt;。分支选择器显示 &lt;code&gt;main&lt;/code&gt;，旁边写着 &lt;strong&gt;1 提交&lt;/strong&gt;；这条提交的作者是 Angus，说明是 &lt;code&gt;Initial commit from https://github.com/octocat/Hello-World.git&lt;/code&gt;，短哈希（提交号的前几位）是 &lt;code&gt;25861fe&lt;/code&gt;。文件列表里只有一个 &lt;code&gt;README&lt;/code&gt;，大小 13 B。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsnadlkejibez164mpnmc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsnadlkejibez164mpnmc.png" alt="私有仓库 hello-world-import：main 上 1 条初始提交，文件只有 README" width="799" height="703"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;需要克隆时，点右上角橙色的 &lt;strong&gt;代码&lt;/strong&gt; 按钮打开克隆面板，面板说明是「使用 Web URL 克隆。」其中 &lt;strong&gt;HTTPS&lt;/strong&gt; 页签里的地址是：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://git.anguskit.com/xlliu/hello-world-import.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;SSH&lt;/strong&gt; 页签里的地址是：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh://git@git.anguskit.com:2222/xlliu/hello-world-import.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;因为仓库是私有的，克隆时会要求输入用户名和访问令牌。用户名填 AngusGit 登录名，密码填开头生成的令牌：只克隆用 &lt;code&gt;git:read&lt;/code&gt;，要推送用 &lt;code&gt;git:write&lt;/code&gt;。不要把令牌写进页面或截图。到这里验收就完成了：代码页上有一条初始提交、一个文件，就说明导入成功。&lt;/p&gt;

&lt;p&gt;导入之后，团队应当改从 AngusGit 拉代码。原来指向 GitHub 的本地仓库，执行一次 &lt;code&gt;git remote set-url origin &amp;lt;新地址&amp;gt;&lt;/code&gt; 就能把远程地址换成 AngusGit。不过新仓库的历史和源仓库对不上，在旧的本地副本里直接 &lt;code&gt;git push&lt;/code&gt; 会被拒绝，所以最稳妥的做法是每个人重新 clone 一份。&lt;/p&gt;

&lt;h2&gt;
  
  
  5. 要保留完整历史，或者源仓库是私有的
&lt;/h2&gt;

&lt;p&gt;遇到这两种情况，&lt;strong&gt;导入仓库&lt;/strong&gt; 就不够用了，要改走推送：先在自己的电脑上把源仓库完整克隆下来，再推到 AngusGit 上一个新建的空仓库。这样所有分支、标签和提交历史都会一起过去。&lt;/p&gt;

&lt;p&gt;下面命令里的目标仓库 &lt;code&gt;hello-world-history&lt;/code&gt; 只是示意名称，推送前要先在 AngusGit 上把它建成空仓库。不要推到已经导入的 &lt;code&gt;xlliu/hello-world-import&lt;/code&gt;：两边的历史对不上，推送会被拒绝。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;在 AngusGit 建一个空仓库&lt;/strong&gt;
按「新建仓库」的步骤建一个仓库，&lt;strong&gt;添加 README 文件&lt;/strong&gt;、&lt;strong&gt;添加 .gitignore&lt;/strong&gt;、&lt;strong&gt;选择许可证&lt;/strong&gt; 三项都不要勾选，这样建出来的才是空仓库。勾选任何一项，推送都会因为两边历史对不上而被拒绝。建好后复制这个空仓库的地址。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;把源仓库完整克隆到本地&lt;/strong&gt;
&lt;code&gt;git clone --bare&lt;/code&gt; 得到的是一个裸仓库：它只保存 Git 的数据，也就是全部分支、标签和提交历史，不生成可以直接编辑的工作区文件，正适合原样转推。源仓库是私有的，这一步会要求你登录源站。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;主分支不叫 main 时先改名&lt;/strong&gt;
AngusGit 空仓库的默认分支是 &lt;code&gt;main&lt;/code&gt;，而 Hello-World 的主分支叫 &lt;code&gt;master&lt;/code&gt;，所以先在裸仓库里执行 &lt;code&gt;git branch -m master main&lt;/code&gt; 改名。源仓库的主分支已经叫 &lt;code&gt;main&lt;/code&gt; 时，跳过这一步。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;推送所有分支和标签&lt;/strong&gt;
&lt;code&gt;--all&lt;/code&gt; 推送所有分支，&lt;code&gt;--tags&lt;/code&gt; 推送所有标签。示例用的是 SSH 地址；改用 HTTPS 地址也可以，这时密码一栏填 AngusGit 的访问令牌。令牌只保存在你自己的密码管理器里。
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;--bare&lt;/span&gt; https://github.com/octocat/Hello-World.git hello-world-history.git
&lt;span class="nb"&gt;cd &lt;/span&gt;hello-world-history.git
git branch &lt;span class="nt"&gt;-m&lt;/span&gt; master main
git push ssh://git@git.anguskit.com:2222/xlliu/hello-world-history.git &lt;span class="nt"&gt;--all&lt;/span&gt;
git push ssh://git@git.anguskit.com:2222/xlliu/hello-world-history.git &lt;span class="nt"&gt;--tags&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;推送完成后，打开 AngusGit 仓库的 &lt;strong&gt;分支&lt;/strong&gt; 页签，应该能看到源仓库的全部分支，提交记录也和源仓库一致。SSH 公钥和访问令牌按开头准备。&lt;/p&gt;

&lt;h2&gt;
  
  
  6. 一次导入很多个仓库
&lt;/h2&gt;

&lt;p&gt;要搬的仓库较多时，可以用 &lt;strong&gt;仓库&lt;/strong&gt; 页右上角的 &lt;strong&gt;批量导入&lt;/strong&gt;。打开后页面标题是 &lt;strong&gt;批量导入仓库&lt;/strong&gt;，有 &lt;strong&gt;手动输入&lt;/strong&gt;、&lt;strong&gt;待导入&lt;/strong&gt;、&lt;strong&gt;已导入&lt;/strong&gt; 三个页签。&lt;/p&gt;

&lt;p&gt;在 &lt;strong&gt;手动输入&lt;/strong&gt; 页签的 &lt;strong&gt;仓库地址列表&lt;/strong&gt; 里每行填一个地址，选好 &lt;strong&gt;所有者&lt;/strong&gt;，再点 &lt;strong&gt;添加到待导入列表&lt;/strong&gt;，一次最多 100 个。批量导入和单个导入相比有三点不同：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;仓库名称自动取地址的最后一段，例如 &lt;code&gt;https://github.com/octocat/Hello-World.git&lt;/code&gt; 会导入成 &lt;code&gt;Hello-World&lt;/code&gt;，列表里不能逐个改名。&lt;/li&gt;
&lt;li&gt;页面上没有 &lt;strong&gt;设为私有仓库&lt;/strong&gt; 开关，批量导入的仓库全部是公开的。需要私有时有两种办法：导入后到每个仓库的 &lt;strong&gt;设置 → 访问&lt;/strong&gt;，在 &lt;strong&gt;可见性&lt;/strong&gt; 里改成私有并点 &lt;strong&gt;保存可见性设置&lt;/strong&gt;；或者改用第 3 节的单个导入，&lt;code&gt;hello-world-import&lt;/code&gt; 就是单个导入时打开私有开关得到的。&lt;/li&gt;
&lt;li&gt;导入在后台排队进行，提交之后可以离开页面。进度在 &lt;strong&gt;待导入&lt;/strong&gt; 页签查看，每一项会依次显示 &lt;strong&gt;等待中&lt;/strong&gt;、&lt;strong&gt;处理中&lt;/strong&gt;，结束时显示 &lt;strong&gt;成功&lt;/strong&gt; 或 &lt;strong&gt;失败&lt;/strong&gt;。成功的项目出现在 &lt;strong&gt;已导入&lt;/strong&gt; 里；失败的不会自动重试，展开任务能看到原因，改好地址后重新添加即可。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;除了这三点，批量导入的规则和单个导入一样：只带默认分支的文件，源仓库也必须不登录就能读。已经存在的 &lt;code&gt;anguskit/AngusRepo&lt;/code&gt; 不要再放进列表。&lt;/p&gt;

&lt;h2&gt;
  
  
  7. 导入失败时
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;你看到的&lt;/th&gt;
&lt;th&gt;原因&lt;/th&gt;
&lt;th&gt;怎么处理&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;输入框下方 &lt;strong&gt;请输入正确的仓库URL&lt;/strong&gt;，&lt;strong&gt;开始导入&lt;/strong&gt; 点不了&lt;/td&gt;
&lt;td&gt;地址不是 &lt;code&gt;https://&lt;/code&gt;、&lt;code&gt;http://&lt;/code&gt; 或 &lt;code&gt;git@&lt;/code&gt; 开头，或者缺了路径&lt;/td&gt;
&lt;td&gt;从源站仓库页重新复制 HTTPS 克隆地址&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;源仓库URL「…」无效或无法正常访问&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;地址拼错；源仓库是私有的；或 AngusGit 服务器连不上源站&lt;/td&gt;
&lt;td&gt;先不登录在浏览器打开源仓库页，确认它是公开的；再在 AngusGit 服务器上执行 &lt;code&gt;git ls-remote &amp;lt;源地址&amp;gt;&lt;/code&gt;。私有源仓库按第 5 节推送&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;仓库「xlliu/hello-world-import」已存在&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;同一个所有者下已有同名仓库，例如这个仓库已经导入过一次&lt;/td&gt;
&lt;td&gt;换一个 &lt;strong&gt;仓库名称&lt;/strong&gt;。&lt;code&gt;anguskit/AngusRepo&lt;/code&gt; 同样已存在，不要用它当导入目标&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;克隆仓库异常：…&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;拉取过程中出错，例如网络中途断开&lt;/td&gt;
&lt;td&gt;看冒号后面的原因。网络问题先重试一次；仓库很大、反复中断时改用第 5 节&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;批量导入里某一项 &lt;strong&gt;失败&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;和单个导入的原因相同&lt;/td&gt;
&lt;td&gt;展开任务看这一项的错误信息，改好后重新添加到待导入列表&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  常见问题
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;导入之后，源仓库的新提交会同步过来吗？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;不会。导入只在你点击 &lt;strong&gt;开始导入&lt;/strong&gt; 的那一刻拉一次代码，之后源仓库的新提交不会自动过来。想拿到这些新提交，可以用一个新名字重新导入一次；或者改用第 5 节的推送方式，把源仓库连同新提交一起推到一个新建的空仓库。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;为什么导入后只剩一条提交？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;因为导入拿的是源仓库默认分支当时的文件，用这些文件在 AngusGit 上新建一个仓库并生成一条提交，原来的提交历史、其他分支和标签都不会带过来。以 Hello-World 为例，导入后只有这一条 &lt;code&gt;Initial commit from https://github.com/octocat/Hello-World.git&lt;/code&gt;。要保留历史，请用第 5 节。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitLab 和 Gitee 的仓库也是这样导入吗？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;是的。&lt;strong&gt;仓库 URL&lt;/strong&gt; 填 GitLab 或 Gitee 仓库页上的 HTTPS 克隆地址即可，步骤和规则完全一样。表单提示里列出的就是 Github、Gitlab、Gitee、SourceForge 这几家，GitBucket 等能用 Git 地址访问的平台也可以导入。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue 和合并请求能一起迁过来吗？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;不能。导入和推送都只搬 Git 仓库里的内容，Issue 和合并请求不在其中。进行中的合并请求，建议先在源站合并或关闭再迁移；还没解决的 Issue，到 AngusGit 的 &lt;strong&gt;问题&lt;/strong&gt; 里重新创建。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;源仓库用了 Git LFS 怎么办？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Git LFS 是 Git 的大文件存储扩展，AngusGit 不支持它。导入或推送之后，LFS 管理的大文件只剩下指针文件（一小段记录文件位置的文本），原来的内容不会过来。这类文件需要改成普通文件提交，或者放到制品库里。&lt;/p&gt;

&lt;h2&gt;
  
  
  小结
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;导入仓库&lt;/strong&gt; 适合只要代码当前版本的场景：源仓库必须公开，结果只有默认分支的文件和一条 &lt;code&gt;Initial commit from …&lt;/code&gt;，不带历史、其他分支和标签，也不包括 Issue、合并请求和 CI。以 Hello-World 为例，它被导入成私有的 &lt;code&gt;xlliu/hello-world-import&lt;/code&gt;，&lt;code&gt;main&lt;/code&gt; 上只有一个 &lt;code&gt;README&lt;/code&gt;。如果源仓库是私有的，或者要保留每一次提交、每一个分支和标签，就改走推送：先建一个空仓库，再用 &lt;code&gt;git clone --bare&lt;/code&gt; 加 &lt;code&gt;git push --all&lt;/code&gt; 和 &lt;code&gt;--tags&lt;/code&gt;。推送目标必须是这个新建的空仓库，不要推到已经导入的仓库上。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;结论：&lt;/strong&gt; &lt;strong&gt;导入仓库&lt;/strong&gt; 只会把公开源仓库默认分支当时的文件做成一条 &lt;code&gt;Initial commit from &amp;lt;源地址&amp;gt;&lt;/code&gt;（&lt;code&gt;xlliu/hello-world-import&lt;/code&gt; 就是把 &lt;code&gt;master&lt;/code&gt; 上的 &lt;code&gt;README&lt;/code&gt; 落到了 &lt;code&gt;main&lt;/code&gt;），要保留完整历史、全部分支和标签，就新建空仓库，再用 &lt;code&gt;git clone --bare&lt;/code&gt; 加 &lt;code&gt;git push --all&lt;/code&gt; / &lt;code&gt;--tags&lt;/code&gt; 推送。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  参考文章
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/docs/git/manual/04-repository" rel="noopener noreferrer"&gt;AngusGit 手册：仓库（新建、导入、批量导入）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/self-hosted-git-first-push-ssh-https" rel="noopener noreferrer"&gt;自建 Git 之后怎么推代码：配 SSH 或 HTTPS，完成第一次 push&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/clone-private-repository-https-access-token" rel="noopener noreferrer"&gt;HTTPS 克隆私有仓库：密码填访问令牌&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>angusgit</category>
      <category>github</category>
    </item>
    <item>
      <title>GitLab 私有化部署太吃内存：只留仓库和合并请求</title>
      <dc:creator>Wen Ping</dc:creator>
      <pubDate>Sat, 31 Oct 2026 04:08:48 +0000</pubDate>
      <link>https://dev.to/wenping/gitlab-si-you-hua-bu-shu-tai-chi-nei-cun-zhi-liu-cang-ku-he-he-bing-qing-qiu-48hh</link>
      <guid>https://dev.to/wenping/gitlab-si-you-hua-bu-shu-tai-chi-nei-cun-zhi-liu-cang-ku-he-he-bing-qing-qiu-48hh</guid>
      <description>&lt;p&gt;这篇讲内网 GitLab 占着大内存、团队却只用仓库和评审时，怎么把这三件事放到更轻的 AngusGit 上。看完你可以按团队建好仓库并连同历史迁入，配好合并方式和分支保护，构建继续留在 Jenkins，制品继续留在制品仓库。&lt;/p&gt;

&lt;p&gt;先登录 AngusGit。搬一个仓库时，在 &lt;strong&gt;设置 → 团队&lt;/strong&gt; 点 &lt;strong&gt;新建团队&lt;/strong&gt;，&lt;strong&gt;团队标识&lt;/strong&gt; 沿用 GitLab 组的英文名。侧栏 &lt;strong&gt;仓库 → 新建仓库&lt;/strong&gt;，所有者选这个团队，&lt;strong&gt;添加 README 文件&lt;/strong&gt;、&lt;strong&gt;添加 .gitignore&lt;/strong&gt;、&lt;strong&gt;选择许可证&lt;/strong&gt; 都不要勾，否则推送会因为历史对不上被拒绝。本机没有公钥时执行 &lt;code&gt;ssh-keygen -t ed25519 -C "you@example.com"&lt;/code&gt;，把 &lt;code&gt;cat ~/.ssh/id_ed25519.pub&lt;/code&gt; 的那一行登记到 &lt;strong&gt;我的 → SSH密钥&lt;/strong&gt;。然后执行（地址换成你的；主分支叫 &lt;code&gt;master&lt;/code&gt; 时先在裸仓库里 &lt;code&gt;git branch -m master main&lt;/code&gt;）：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;--bare&lt;/span&gt; https://gitlab.example.com/组/项目.git
&lt;span class="nb"&gt;cd &lt;/span&gt;项目.git
git push ssh://git@git.example.com:2222/团队/仓库.git &lt;span class="nt"&gt;--all&lt;/span&gt;
git push ssh://git@git.example.com:2222/团队/仓库.git &lt;span class="nt"&gt;--tags&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;用户名是 &lt;code&gt;git&lt;/code&gt;，端口是 2222。改用 HTTPS 时，到 &lt;strong&gt;我的 → 访问令牌&lt;/strong&gt; 生成一把勾了 &lt;code&gt;git:write&lt;/code&gt;、仓库权限为 &lt;strong&gt;WRITE&lt;/strong&gt; 的令牌，用户名填登录名，密码填令牌。验收用下面的命令，没有意外输出就是两边一致：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diff &lt;span class="se"&gt;\&lt;/span&gt;
  &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;git ls-remote &lt;span class="nt"&gt;--heads&lt;/span&gt; &lt;span class="nt"&gt;--tags&lt;/span&gt; https://gitlab.example.com/组/项目.git | &lt;span class="nb"&gt;sort&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;git ls-remote &lt;span class="nt"&gt;--heads&lt;/span&gt; &lt;span class="nt"&gt;--tags&lt;/span&gt; ssh://git@git.example.com:2222/团队/仓库.git | &lt;span class="nb"&gt;sort&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;仓库多时，把 GitLab 路径和「团队/仓库名」每行一对写进 &lt;code&gt;repos.txt&lt;/code&gt;，对每一行重复上面的克隆和推送。先手动成功推一个，并在系统凭据窗口里保存令牌，再跑循环。&lt;code&gt;master&lt;/code&gt; 改成 &lt;code&gt;main&lt;/code&gt; 时，这一对名字不同是预期的。&lt;/p&gt;

&lt;p&gt;拉取请求在 GitLab 里叫合并请求（Merge Request），和 AngusGit 页面上的拉取请求是同一件事。&lt;/p&gt;

&lt;p&gt;GitLab 占内存，是因为它的一体化安装包里带了数据库、缓存、后台任务、内置 CI、容器镜像仓库和监控等一整套组件，即使只用代码托管，这些进程也一直在后台运行。如果你们的流水线已经在 Jenkins 上跑、制品也有单独的仓库，那么用 2 核 4 GB 起步的 AngusGit 承接仓库、拉取请求和代码评审这三件事就够了。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl8jmw62w3myzopwae42v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl8jmw62w3myzopwae42v.png" alt="GitLab 里每天在用的只有三块；拆开后，构建和制品各自留在原系统" width="800" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. 先确认：团队真的只用这三件事吗
&lt;/h2&gt;

&lt;p&gt;动手之前，先花十分钟确认现有 GitLab 上哪些功能真的在用。可以对照下表逐项问一遍：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;功能&lt;/th&gt;
&lt;th&gt;在用吗&lt;/th&gt;
&lt;th&gt;拆分后放在哪&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;仓库托管、推拉代码&lt;/td&gt;
&lt;td&gt;几乎一定在用&lt;/td&gt;
&lt;td&gt;AngusGit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;合并请求、网页评审&lt;/td&gt;
&lt;td&gt;几乎一定在用&lt;/td&gt;
&lt;td&gt;AngusGit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Issue、里程碑&lt;/td&gt;
&lt;td&gt;看团队习惯&lt;/td&gt;
&lt;td&gt;AngusGit 也有，可以一起迁&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitLab CI&lt;/td&gt;
&lt;td&gt;不一定&lt;/td&gt;
&lt;td&gt;已经在 Jenkins 上的继续用 Jenkins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;容器镜像仓库、软件包仓库&lt;/td&gt;
&lt;td&gt;不一定&lt;/td&gt;
&lt;td&gt;制品仓库，例如 AngusRepo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Git LFS&lt;/td&gt;
&lt;td&gt;少数团队在用&lt;/td&gt;
&lt;td&gt;AngusGit 不支持，这类仓库先留在原处&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Git LFS 是把大文件存在仓库之外的 Git 扩展。如果 GitLab CI 和 Git LFS 这两行的答案都是「不用」，拆分会很顺利；如果团队重度依赖 GitLab CI，先看文末参考里的选型一文，评估流水线怎么处理。&lt;/p&gt;

&lt;h2&gt;
  
  
  2. 第一件：仓库
&lt;/h2&gt;

&lt;p&gt;AngusGit 上的仓库都挂在团队下面，地址形如 &lt;code&gt;ssh://git@git.example.com:2222/团队/仓库.git&lt;/code&gt;。所以搬仓库分三步：先建团队，再把仓库搬过来，最后让开发者改远程地址。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;按现在的分组建团队&lt;/strong&gt;
在 &lt;strong&gt;设置 → 团队&lt;/strong&gt; 里 &lt;strong&gt;新建团队&lt;/strong&gt;，一个团队对应 GitLab 上的一个组。&lt;strong&gt;团队标识&lt;/strong&gt; 会出现在仓库地址里，建议沿用 GitLab 组的英文名，这样大家改远程地址时更好记。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;把仓库连同历史搬过来&lt;/strong&gt;
先在 AngusGit 上新建同名的空仓库，然后用 &lt;code&gt;git clone --bare&lt;/code&gt; 从 GitLab 克隆一份完整副本（裸仓库只存 Git 数据，没有可编辑的工作区），再用 &lt;code&gt;git push --all&lt;/code&gt; 和 &lt;code&gt;git push --tags&lt;/code&gt; 推到 AngusGit。这样提交历史、全部分支和标签都会带过来。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;让开发者改远程地址&lt;/strong&gt;
每个人在本地仓库里执行 &lt;code&gt;git remote set-url origin&lt;/code&gt;，把远程地址换成新地址。用 SSH 的，先在 &lt;strong&gt;我的 → SSH密钥&lt;/strong&gt; 登记公钥；用 HTTPS 的，密码一栏填 &lt;strong&gt;我的 → 访问令牌&lt;/strong&gt; 里生成的令牌。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;命令、凭据和两边对比见开头。先手动成功推一个仓库，再对 &lt;code&gt;repos.txt&lt;/code&gt; 里的每一行重复同样的克隆和推送。&lt;/p&gt;

&lt;h2&gt;
  
  
  3. 第二件：合并请求
&lt;/h2&gt;

&lt;p&gt;在 AngusGit 上，合并请求叫 &lt;strong&gt;拉取请求&lt;/strong&gt;，用法和 GitLab 的 Merge Request 一样：从功能分支发起，合进 &lt;code&gt;main&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;需要提前定下来的是合并方式。打开仓库的 &lt;strong&gt;设置 → 拉取请求&lt;/strong&gt;，这里有三个开关：&lt;strong&gt;合并提交&lt;/strong&gt;、&lt;strong&gt;压缩合并&lt;/strong&gt;、&lt;strong&gt;变基合并&lt;/strong&gt;。只打开团队约定的那一种，合并框里就只会出现这一种，避免每个人按自己的习惯选。如果还没想好选哪种：想保留每个提交，选合并提交；希望一个拉取请求在 &lt;code&gt;main&lt;/code&gt; 上只留下一条提交，选压缩合并。三种方式的详细区别见文末参考里的合并方式一文。&lt;/p&gt;

&lt;h2&gt;
  
  
  4. 第三件：人工评审
&lt;/h2&gt;

&lt;p&gt;评审在侧栏的 &lt;strong&gt;评审&lt;/strong&gt; 里完成。评审人打开 &lt;strong&gt;待评审&lt;/strong&gt; 里的一条拉取请求，在 &lt;strong&gt;文件变更&lt;/strong&gt; 里逐个文件看差异，在需要讨论的行上留言，最后点 &lt;strong&gt;提交评审&lt;/strong&gt;，选择 &lt;strong&gt;批准&lt;/strong&gt;、&lt;strong&gt;请求更改&lt;/strong&gt; 或 &lt;strong&gt;评论&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8mrocbusz1ocygodkp96.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8mrocbusz1ocygodkp96.png" alt="xlliu/test-cicd-example #1 的文件变更页：左侧是文件树，右侧是 index.js 的差异" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;以还开着的 &lt;a href="https://git.anguskit.com/review/xlliu/test-cicd-example/1/382507197532471397" rel="noopener noreferrer"&gt;xlliu/test-cicd-example #1&lt;/a&gt; 为例，它的标题是「SC-02/SC-04 PR trigger verification」，状态是 &lt;strong&gt;待评审&lt;/strong&gt;。页面左侧是文件树，两个文件都还没有标记为已查看；右侧展开的是 &lt;code&gt;apps/service-a/src/index.js&lt;/code&gt;，新增了两行，目前还没有行评。点右上角的 &lt;strong&gt;提交评审&lt;/strong&gt; 会弹出一个对话框，标题同样是「提交评审」，说明文字是「选择你的评审决定并添加评论」，下面有 &lt;strong&gt;批准&lt;/strong&gt;、&lt;strong&gt;请求更改&lt;/strong&gt;、&lt;strong&gt;评论&lt;/strong&gt; 三个选项。选好结论、再点对话框里的 &lt;strong&gt;提交评审&lt;/strong&gt;，评审才算提交；只打开对话框，状态仍然是 &lt;strong&gt;待评审&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;到这一步，评审人批不批准还只能靠自觉。要把评审变成必须遵守的规定，需要打开分支保护，也就是给分支加上「禁止直接推送、合并前必须满足条件」的规则：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;保护主分支&lt;/strong&gt;
打开仓库的 &lt;strong&gt;设置 → 分支保护&lt;/strong&gt;，给 &lt;code&gt;main&lt;/code&gt; 添加一条规则：打开 &lt;strong&gt;需要代码评审&lt;/strong&gt;，&lt;strong&gt;必需的评审人数&lt;/strong&gt; 填 1，并打开 &lt;strong&gt;对管理员同样生效&lt;/strong&gt;。保存之后，直接推送 &lt;code&gt;main&lt;/code&gt; 会被拒绝，改动必须通过拉取请求、拿到批准后才能合并。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;按目录指定评审人（可选）&lt;/strong&gt;
如果不同目录由不同的人负责，就在仓库里加一个 CODEOWNERS 文件，写明每个目录的负责人。之后创建拉取请求时，系统会自动把对应的人加为评审人。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;配好之后，团队每天的流程就固定为：开分支、推送、发起拉取请求、评审人批准、合并。每一项的具体设置见文末参考里的分支保护和 CODEOWNERS 两篇。&lt;/p&gt;

&lt;h2&gt;
  
  
  5. 不放进来的功能，交给谁
&lt;/h2&gt;

&lt;p&gt;仓库、拉取请求和评审之外的功能，继续交给各自的专职系统，对应关系如下：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;功能&lt;/th&gt;
&lt;th&gt;原有系统归属&lt;/th&gt;
&lt;th&gt;拆分后怎么处理&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;构建和测试&lt;/td&gt;
&lt;td&gt;Jenkins&lt;/td&gt;
&lt;td&gt;继续用 Jenkins，触发方式见表后说明&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;镜像、软件包&lt;/td&gt;
&lt;td&gt;制品仓库&lt;/td&gt;
&lt;td&gt;放在制品仓库里，例如 AngusRepo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;大文件&lt;/td&gt;
&lt;td&gt;Git LFS / 存储&lt;/td&gt;
&lt;td&gt;AngusGit 不支持 Git LFS，大文件放到制品仓库或 Release 附件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;统一登录&lt;/td&gt;
&lt;td&gt;用户中心&lt;/td&gt;
&lt;td&gt;用户和登录由 GM 管理；对接 LDAP 在 GM 企业版上配置&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;构建这一行要注意触发方式。Webhook 是代码有变动时自动通知外部系统的机制，而 AngusGit 的推送 Webhook 只在 &lt;code&gt;git push&lt;/code&gt; 时发出，所以在网页上合并拉取请求之后，建议让 Jenkins 配合 Poll SCM（轮询 SCM，定时检查分支有没有新提交）来发现新代码。&lt;/p&gt;

&lt;p&gt;这样拆分之后，每个系统各管一件事，出了问题也更容易定位是哪一边。&lt;/p&gt;

&lt;h2&gt;
  
  
  常见问题
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;拆出来之后，原来的 GitLab 怎么办？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;先不要删。把 GitLab 改为只读，保留一段时间，方便回查旧的合并请求和讨论记录；确认没有遗漏后再归档。迁移期间怎么双跑（新旧平台并行一段时间，期间只让一边可写）、出了问题怎么回滚，见文末参考里的迁移清单一文。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;以后又需要内置流水线了怎么办？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AngusGit 的团队版和企业版带内置流水线，换用对应的安装包即可，已有的仓库和拉取请求数据不受影响。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;社区版人数不够怎么办？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;社区版最多 10 个用户，超过时换用团队版或企业版，仓库、拉取请求和评审的用法都不变。&lt;/p&gt;

&lt;h2&gt;
  
  
  小结
&lt;/h2&gt;

&lt;p&gt;GitLab 私有化部署吃内存，很多时候是因为一整套组件都在运行，而团队只用其中三件事。把仓库、拉取请求和人工评审搬到 AngusGit 上：仓库按团队建好，连同历史一起搬过来；合并方式在 &lt;strong&gt;设置 → 拉取请求&lt;/strong&gt; 里统一；评审在 &lt;strong&gt;评审&lt;/strong&gt; 里完成，再用分支保护和 CODEOWNERS 把它变成规定。构建继续交给 Jenkins，镜像和软件包放到制品仓库；以后需要内置流水线时，换用团队版或企业版即可。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;结论：&lt;/strong&gt; 只保留仓库、拉取请求和代码评审三件事时，用 2 核 4 GB 起步的 AngusGit 加 GM 就能替代重型 GitLab，构建和制品分别留给 Jenkins 和制品仓库。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  参考文章
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/docs/git/manual/06-pull-request" rel="noopener noreferrer"&gt;AngusGit 手册：拉取请求&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/docs/git/manual/07-code-review" rel="noopener noreferrer"&gt;AngusGit 手册：代码评审&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/self-hosted-git-platform-selection-angusgit" rel="noopener noreferrer"&gt;私有化 Git 怎么选：用一套仓库和合并请求替换 GitLab&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/move-gitlab-repository-to-angusgit" rel="noopener noreferrer"&gt;从 GitLab 搬仓库到 AngusGit：公开项目直接导入，私有和内部项目推送&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/pull-request-merge-methods-squash-rebase" rel="noopener noreferrer"&gt;合并请求怎么合入：在保留提交、压缩和变基里选一种&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/code-review-in-pull-request" rel="noopener noreferrer"&gt;在合并请求里做完代码评审：看 Diff、写行评、通过或打回&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/protect-git-main-branch-pull-request" rel="noopener noreferrer"&gt;怎么保护 Git 主分支：先开分支保护，再用合并请求合代码&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/codeowners-auto-assign-reviewers" rel="noopener noreferrer"&gt;谁来审这段代码：用 CODEOWNERS 按目录自动指派评审人&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/migrate-gitlab-to-angusgit-checklist" rel="noopener noreferrer"&gt;从 GitLab 迁到 AngusGit：迁移清单、双跑和回滚&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>angusgit</category>
      <category>gitlab</category>
    </item>
    <item>
      <title>内网 Git 要留哪些操作记录：在 AngusGit 里查成员、保护和合并</title>
      <dc:creator>Wen Ping</dc:creator>
      <pubDate>Sat, 31 Oct 2026 04:08:12 +0000</pubDate>
      <link>https://dev.to/wenping/nei-wang-git-yao-liu-na-xie-cao-zuo-ji-lu-zai-angusgit-li-cha-cheng-yuan-bao-hu-he-he-bing-1cp2</link>
      <guid>https://dev.to/wenping/nei-wang-git-yao-liu-na-xie-cao-zuo-ji-lu-zai-angusgit-li-cha-cheng-yuan-bao-hu-he-he-bing-1cp2</guid>
      <description>&lt;p&gt;这篇讲安全检查或出问题后，怎么查谁加了成员、谁改了分支保护、谁合并了代码。看完你可以在侧栏 &lt;strong&gt;动态&lt;/strong&gt; 里按活动类型筛出「谁、什么时候、做了什么」；合并方式和代码作者要回到 Git 历史里看，动态里没有的操作则去查数据库备份和服务器日志。&lt;/p&gt;

&lt;h2&gt;
  
  
  1. 常被问到的问题，各在哪里查
&lt;/h2&gt;

&lt;p&gt;动态里的记录按活动类型分类。筛选器里的类型名称是 &lt;strong&gt;提交&lt;/strong&gt;、&lt;strong&gt;拉取请求&lt;/strong&gt;、&lt;strong&gt;合并&lt;/strong&gt;、&lt;strong&gt;团队&lt;/strong&gt;、&lt;strong&gt;配置&lt;/strong&gt;、&lt;strong&gt;分支&lt;/strong&gt;；其中推送代码这一类，在页面上方的统计卡上写作「代码提交」。下表列出常见问题该选哪种活动类型，以及记录的文案：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;问题&lt;/th&gt;
&lt;th&gt;在动态里选的活动类型&lt;/th&gt;
&lt;th&gt;记录长什么样&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;谁把某人加进了团队&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;团队&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;「添加了团队成员」；邀请还没被接受时是「邀请了成员 名字」&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;谁把某人加进了某个仓库&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;团队&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;「添加了协作者 用户名」，例如「添加了协作者 xlliu」&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;谁改了权限或角色&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;团队&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;仓库协作者：「更新了协作者 用户名 的权限」；团队：「更新了团队成员角色」&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;谁移除了成员或协作者&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;团队&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;「移除了团队成员」「移除了成员」「移除了协作者 用户名」&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;谁改了或删了分支保护&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;配置&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;「设置了分支保护」或「删除了分支保护」，后面紧跟分支名&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;谁改了仓库设置或可见性&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;配置&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;「更新了仓库设置」「更新了仓库可见性」&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;谁加了、改了或删了 Webhook&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;配置&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;「创建了Webhook」「更新了Webhook」「删除了Webhook」&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;谁合并了某个拉取请求&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;拉取请求&lt;/strong&gt;（拉取请求页本身也能查）&lt;/td&gt;
&lt;td&gt;「合并了 Pull Request #编号」&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;谁往仓库推了代码&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;提交&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;一个提交是「推送了提交到」，多个是「推送了 N 个提交到」&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;谁删了某个分支&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;分支&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;「删除了分支」&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;某人最近从哪登录过&lt;/td&gt;
&lt;td&gt;不在动态里&lt;/td&gt;
&lt;td&gt;AngusGM &lt;strong&gt;用户详情 → 登录历史&lt;/strong&gt;，最近 10 次登录&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;读这张表时有几点要注意：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;筛选器里的 &lt;strong&gt;合并&lt;/strong&gt; 只记直接把一个分支合并到另一个分支的操作，文案是「将分支 A 合并到 B」，目前是 0 条。通过拉取请求合并的记录，要到 &lt;strong&gt;拉取请求&lt;/strong&gt; 类型下找。&lt;/li&gt;
&lt;li&gt;分支保护的文案和分支名之间没有空格，例如已有的一条记录是「设置了分支保护mcp-round1-locked」。&lt;/li&gt;
&lt;li&gt;有的推送记录没有用户名，页面上显示「未知用户」。&lt;/li&gt;
&lt;li&gt;有些文案在系统里存在，但示例实例里还没出现过：26 条团队记录里没有邀请和移除，70 条配置记录里没有删除分支保护，也没有 Webhook 相关的记录。分支类型下目前看到的是「创建了分支」后面接分支名。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. 在「动态」里查一条记录
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;打开动态&lt;/strong&gt;
在侧栏点 &lt;strong&gt;动态&lt;/strong&gt;，页面说明是「查看提交、评审、议题和流水线等操作记录。」，列表按时间倒序排列，最新的在最上面。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;用筛选器缩小范围&lt;/strong&gt;
用顶部的筛选器缩小范围：&lt;strong&gt;活动类型&lt;/strong&gt; 按上表选对应的一类；仓库选要查的那个；&lt;strong&gt;用户&lt;/strong&gt; 选怀疑的操作人，不确定就留空；&lt;strong&gt;时间范围&lt;/strong&gt; 可选 &lt;strong&gt;本周&lt;/strong&gt;、&lt;strong&gt;本月&lt;/strong&gt;、&lt;strong&gt;本季度&lt;/strong&gt;、&lt;strong&gt;本年&lt;/strong&gt; 和 &lt;strong&gt;全部&lt;/strong&gt;。仓库下拉一次只列出 20 个名字，要找的仓库不在里面时，先在下拉里搜索仓库名再选。也可以直接在搜索框里输入用户、仓库、分支或描述里的关键字。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;看记录&lt;/strong&gt;
配置、团队、拉取请求这几类记录，都写明了是谁、在哪个仓库、做了什么、什么时候做的。以查分支保护为例：活动类型选 &lt;strong&gt;配置&lt;/strong&gt;，在搜索框里输入 &lt;code&gt;分支保护&lt;/code&gt;，时间范围选 &lt;strong&gt;本月&lt;/strong&gt;，结果如下图。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiorrf2kgl8fhdcng5bad.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiorrf2kgl8fhdcng5bad.png" alt="活动类型选配置并搜索「分支保护」，结果只有 e2e-git01-defaults 上的一条记录" width="799" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;图中这条记录的意思是：Angus 在仓库 &lt;code&gt;e2e-git01-defaults&lt;/code&gt; 上给分支 &lt;code&gt;mcp-round1-locked&lt;/code&gt; 设置了保护。记录正文是「设置了分支保护mcp-round1-locked」，后面的链接文字是 &lt;code&gt;branch_protection&lt;/code&gt;，状态是 &lt;strong&gt;成功&lt;/strong&gt;，时间显示为「4 天前」，对应 2026-10-22 16:36:09。如果有人删除了分支保护，文案会是「删除了分支保护」紧跟分支名；在这个筛选条件下，本月没有删除记录。&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Git 历史能补上的信息
&lt;/h2&gt;

&lt;p&gt;动态只告诉你谁合并了拉取请求，不说明用的是哪种合并方式。这时可以回到代码本身来查：Git 历史里每条提交都记录了作者（写代码的人）和提交者（把这条提交写进仓库的人），而不同的合并方式，会在 &lt;code&gt;main&lt;/code&gt; 的历史里留下不同的痕迹：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;合并方式&lt;/th&gt;
&lt;th&gt;提交数量变化&lt;/th&gt;
&lt;th&gt;在 &lt;code&gt;main&lt;/code&gt; 的历史里看到的&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;合并提交&lt;/td&gt;
&lt;td&gt;新增一条双父提交（快进除外）&lt;/td&gt;
&lt;td&gt;说明是 &lt;code&gt;Merge pull request #N from …&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;压缩合并&lt;/td&gt;
&lt;td&gt;多条压成一条普通提交&lt;/td&gt;
&lt;td&gt;作者是拉取请求的作者，提交者是点合并的人&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;变基合并&lt;/td&gt;
&lt;td&gt;逐条重放&lt;/td&gt;
&lt;td&gt;每条提交被重新应用到 &lt;code&gt;main&lt;/code&gt; 上，提交哈希与原来不同&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;表里的「双父提交」是指有两个父提交的合并提交，它把 &lt;code&gt;main&lt;/code&gt; 和源分支连在一起。「快进」是例外情况：可以快进时，Git 直接把源分支的提交接在 &lt;code&gt;main&lt;/code&gt; 后面，不会新增这条合并提交。「逐条重放」是指把源分支上的提交一条一条重新应用到 &lt;code&gt;main&lt;/code&gt; 上，所以提交哈希会变。&lt;/p&gt;

&lt;p&gt;要查某个文件里的代码是谁写的、从哪个拉取请求进来的，可以运行：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git log &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'%h %an %cn %cd %s'&lt;/span&gt; &lt;span class="nt"&gt;--date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;iso &lt;span class="nt"&gt;--&lt;/span&gt; path/to/file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;输出里 &lt;code&gt;%an&lt;/code&gt; 是作者，&lt;code&gt;%cn&lt;/code&gt; 是提交者，&lt;code&gt;%cd&lt;/code&gt; 是提交时间。如果用的是压缩合并，作者就是写代码的人，提交者就是点合并的人；提交说明里通常带着拉取请求的标题，拿这个标题回到 AngusGit 就能找到对应的拉取请求，再去看评审记录。&lt;/p&gt;

&lt;p&gt;拉取请求页本身也是一份记录：谁发起、谁评审、评审结论是什么、讨论过什么，都保存在那条拉取请求里。&lt;/p&gt;

&lt;h2&gt;
  
  
  4. 目前没有记录的地方
&lt;/h2&gt;

&lt;p&gt;下面这些情况在动态里查不到。安全检查前要先弄清楚，并准备好替代材料：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;情况&lt;/th&gt;
&lt;th&gt;现状&lt;/th&gt;
&lt;th&gt;建议&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;改了分支保护的哪一项&lt;/td&gt;
&lt;td&gt;只记「设置了分支保护」，不记改前改后的具体规则&lt;/td&gt;
&lt;td&gt;分支保护规则变更走内部审批单，写清改了什么&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;操作来源 IP&lt;/td&gt;
&lt;td&gt;动态记录里没有 IP&lt;/td&gt;
&lt;td&gt;登录看 AngusGM &lt;strong&gt;登录历史&lt;/strong&gt;；其他访问看代理日志（见表后）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;强制推送&lt;/td&gt;
&lt;td&gt;和普通推送一样记为「推送了提交到」或「推送了 N 个提交到」，不单独标出&lt;/td&gt;
&lt;td&gt;在分支保护里关闭 &lt;strong&gt;允许强制推送&lt;/strong&gt;，从源头上禁止&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;被拒绝的推送&lt;/td&gt;
&lt;td&gt;被分支保护拒绝的推送不进动态，只在 AngusGit 服务器日志里&lt;/td&gt;
&lt;td&gt;需要时保留服务器日志&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;导出&lt;/td&gt;
&lt;td&gt;动态不能导出&lt;/td&gt;
&lt;td&gt;定期备份 AngusGit 的数据库&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;保留时长&lt;/td&gt;
&lt;td&gt;动态目前没有自动清理设置&lt;/td&gt;
&lt;td&gt;按公司要求定期备份；需要清理时由运维按备份策略处理&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;合并人和合并时间的展示&lt;/td&gt;
&lt;td&gt;拉取请求页上不显示合并人和合并时间&lt;/td&gt;
&lt;td&gt;看动态 &lt;strong&gt;拉取请求&lt;/strong&gt; 类型里的「合并了 Pull Request #编号」，或看 Git 历史&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;关于来源 IP：登录相关的来源可以看 AngusGM 的 &lt;strong&gt;登录历史&lt;/strong&gt;；如果需要更细的访问来源，要保留反向代理或负载均衡的访问日志。查合并人和合并时间时，Git 历史里的提交者和提交时间也能作为依据。&lt;/p&gt;

&lt;p&gt;还有一处容易混淆：AngusGM 的 &lt;strong&gt;管理 → 日志&lt;/strong&gt; 里也有 &lt;strong&gt;审计日志&lt;/strong&gt;，但它记录的是 AngusGM 平台自己的用户操作和系统日志，不包含 Git 仓库里的操作。查 Git 相关的问题，要看 AngusGit 的 &lt;strong&gt;动态&lt;/strong&gt;。&lt;/p&gt;

&lt;h2&gt;
  
  
  5. 平时就做好这几件事
&lt;/h2&gt;

&lt;p&gt;事后追查最怕的是记录本来就不存在。下面几件事建议在仓库上线时就做好：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;分支保护里打开 对管理员同样生效，关闭 允许强制推送 和 允许删除。&lt;/strong&gt; 这样历史不会被改写，主分支不会被删除，管理员也不能绕过评审。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;权限尽量给到团队，少给个人协作者。&lt;/strong&gt; 团队成员的加入和移除都记在 &lt;strong&gt;团队&lt;/strong&gt; 类型下，比逐个翻查每个仓库的协作者清楚得多。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;定期备份数据库，保留服务器日志。&lt;/strong&gt; 动态存在数据库里且不能导出；被拒绝的推送只出现在服务器日志里。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;重要的设置变更走审批单。&lt;/strong&gt; 动态只记录「谁在什么时候改了」，审批单能补上「为什么改、改成了什么」。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8uxw0wgpzmy98xu70rs4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8uxw0wgpzmy98xu70rs4.png" alt="动态回答谁和何时，Git 历史补作者和评审，规则明细和 IP 要另外留存" width="800" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  常见问题
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;装了 AngusGit，就满足等保对代码仓库的要求了吗？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;不能这么说。AngusGit 的动态只能提供一部分操作记录，而等保（网络安全等级保护）还关心身份鉴别、访问控制、日志留存期限、备份恢复等很多方面，需要结合你们的整体方案和测评机构的要求来判断。本文第 4 节列出的缺口，就是准备材料时要重点补上的地方。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;拉取请求被合并后又被回退，能查到吗？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;能。回退本身就是一次新的提交或拉取请求，会以推送或合并的形式出现在动态里；Git 历史里也能看到那条 &lt;code&gt;Revert …&lt;/code&gt; 提交。&lt;/p&gt;

&lt;h2&gt;
  
  
  小结
&lt;/h2&gt;

&lt;p&gt;查 Git 上的操作记录，先去 AngusGit 的 &lt;strong&gt;动态&lt;/strong&gt;：成员和权限看 &lt;strong&gt;团队&lt;/strong&gt;，分支保护、仓库设置、可见性和 Webhook 看 &lt;strong&gt;配置&lt;/strong&gt;，拉取请求的合并看 &lt;strong&gt;拉取请求&lt;/strong&gt;，推送看 &lt;strong&gt;提交&lt;/strong&gt;；筛选器上的 &lt;strong&gt;合并&lt;/strong&gt; 只记直接把一个分支合并到另一个分支的操作，目前没有这类记录。合并方式、作者和提交者要回到 Git 历史里查，评审结论在拉取请求页里。动态不记改前改后的规则、不记 IP、不能导出，被拒绝的推送也不在里面，这些缺口要靠变更审批单、数据库备份、服务器日志和反向代理日志补上；登录记录则看 AngusGM 的 &lt;strong&gt;登录历史&lt;/strong&gt;。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;结论：&lt;/strong&gt; 成员变更看 &lt;strong&gt;团队&lt;/strong&gt;，分支保护看 &lt;strong&gt;配置&lt;/strong&gt;，拉取请求的合并看 &lt;strong&gt;拉取请求&lt;/strong&gt;；改前改后的具体规则、来源 IP 和被拒绝的推送，需要用审批单、反向代理日志和服务器日志另外留存。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  参考文章
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/docs/git/manual/03-org-permissions" rel="noopener noreferrer"&gt;AngusGit 手册：组织与权限&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/docs/git/manual/05-branch-merge" rel="noopener noreferrer"&gt;AngusGit 手册：分支与合并（分支保护）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/protect-git-main-branch-pull-request" rel="noopener noreferrer"&gt;怎么保护 Git 主分支：先开分支保护，再用合并请求合代码&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/pull-request-merge-methods-squash-rebase" rel="noopener noreferrer"&gt;合并请求怎么合入：在保留提交、压缩和变基里选一种&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>angusgit</category>
      <category>selfhosted</category>
    </item>
    <item>
      <title>流水线运行器怎么接到构建机：在那台机器上执行接入命令</title>
      <dc:creator>Wen Ping</dc:creator>
      <pubDate>Sat, 31 Oct 2026 04:07:35 +0000</pubDate>
      <link>https://dev.to/wenping/liu-shui-xian-yun-xing-qi-zen-yao-jie-dao-gou-jian-ji-zai-na-tai-ji-qi-shang-zhi-xing-jie-ru-ming-ling-4d8p</link>
      <guid>https://dev.to/wenping/liu-shui-xian-yun-xing-qi-zen-yao-jie-dao-gou-jian-ji-zai-na-tai-ji-qi-shang-zhi-xing-jie-ru-ming-ling-4d8p</guid>
      <description>&lt;p&gt;这篇讲流水线 YAML 写好了、任务却没人执行时，怎么把运行器接到构建机上。看完你可以在控制台新增 Runner（装在构建机上、替 AngusGit 执行任务的程序），生成 1 小时有效的注册令牌，按 Docker 或 Zip 把接入命令复制到构建机上执行，并确认它心跳上线。&lt;/p&gt;

&lt;p&gt;流水线和运行器在团队版和企业版中提供；社区版没有 &lt;strong&gt;CI/CD&lt;/strong&gt; 菜单，本文的操作不适用。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F09ta2loeu691v9mqbcvz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F09ta2loeu691v9mqbcvz.png" alt="控制面 git.anguskit.com 和构建机上的运行器之间交换心跳、任务和日志" width="800" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. 准备一台构建机
&lt;/h2&gt;

&lt;p&gt;接入之前，先确认网络、容器环境和宿主机环境这三件事。Docker 方式会在这台机器上为每个任务拉起容器（按镜像临时启动的隔离环境）；Zip 方式则让任务直接在宿主机（运行器进程所在的那台机器）上执行。&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;检查项&lt;/th&gt;
&lt;th&gt;适用方式&lt;/th&gt;
&lt;th&gt;要求&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;网络连通&lt;/td&gt;
&lt;td&gt;全部方式&lt;/td&gt;
&lt;td&gt;构建机主动连 AngusGit（HTTPS / WebSocket 长连接），AngusGit 不需要反连构建机&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;容器环境&lt;/td&gt;
&lt;td&gt;Docker 方式&lt;/td&gt;
&lt;td&gt;机器装好 Docker，当前用户有权限执行 &lt;code&gt;docker&lt;/code&gt; 并挂载 &lt;code&gt;/var/run/docker.sock&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;宿主机环境&lt;/td&gt;
&lt;td&gt;Zip 方式&lt;/td&gt;
&lt;td&gt;机器装好 JDK 21、&lt;code&gt;curl&lt;/code&gt; 和 &lt;code&gt;unzip&lt;/code&gt;，当前用户有 &lt;code&gt;sudo&lt;/code&gt; 权限（安装到 &lt;code&gt;/opt/AngusRunnerAgent&lt;/code&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;在构建机上用 &lt;code&gt;curl -I https://&amp;lt;你的 AngusGit 地址&amp;gt;&lt;/code&gt; 试一下，能返回 HTTP 状态码，说明网络是通的。&lt;/p&gt;

&lt;h2&gt;
  
  
  2. 在控制台新增运行器
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;打开接入页&lt;/strong&gt;
侧栏进入 &lt;strong&gt;CI/CD → 运行器&lt;/strong&gt;，点右上角 &lt;strong&gt;新增 Runner&lt;/strong&gt;，页面切到 &lt;strong&gt;接入新 Runner&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;确认控制面地址&lt;/strong&gt;
&lt;strong&gt;控制面地址&lt;/strong&gt; 默认取当前浏览器打开的站点地址。页面提示「若 Runner 所在网络访问不到，请改为控制面对外可达的 URL」。例如你在办公网用域名访问 AngusGit，而构建机在机房只能用内网 IP 访问，这里就要改成构建机能访问的那个地址。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;填名称和标签&lt;/strong&gt;
&lt;strong&gt;Runner 名称（可选）&lt;/strong&gt; 写清是哪台机器，例如 &lt;code&gt;build-linux-01&lt;/code&gt;。&lt;strong&gt;标签（可选）&lt;/strong&gt; 用逗号分隔，例如 &lt;code&gt;linux,docker&lt;/code&gt;。流水线里的任务会按标签挑运行器，这里的标签要和任务里写的对得上。不确定时先留空，用每种接入方式的默认标签。&lt;strong&gt;最大并发 Job 数&lt;/strong&gt; 是这台机器同时最多跑几个任务（Job 就是流水线里的一组步骤），默认 4，可填 1 到 64，按机器的 CPU 和内存决定。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;生成注册令牌&lt;/strong&gt;
点 &lt;strong&gt;注册令牌&lt;/strong&gt; 旁的 &lt;strong&gt;生成令牌&lt;/strong&gt;。页面提示「已签发短期注册令牌（明文仅显示一次）」，下方显示「令牌有效期至 …」，有效期 1 小时。令牌会自动填进下面的命令里。没有令牌时点 &lt;strong&gt;复制命令&lt;/strong&gt;，会提示「请先生成或粘贴注册令牌后再复制命令」。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;下面这张是还没点 &lt;strong&gt;生成令牌&lt;/strong&gt; 时的页面。控制面地址是 &lt;code&gt;https://git.anguskit.com&lt;/code&gt;，名称 &lt;code&gt;build-linux-01&lt;/code&gt;，标签 &lt;code&gt;linux,docker&lt;/code&gt;，最大并发 Job 数 4。黄色提示是「尚未填写令牌」，&lt;strong&gt;复制命令&lt;/strong&gt; 是灰的。三个页签是 &lt;strong&gt;Zip 安装&lt;/strong&gt;、&lt;strong&gt;Docker 一键拉起&lt;/strong&gt;、&lt;strong&gt;一键安装脚本&lt;/strong&gt;，默认停在 &lt;strong&gt;Docker 一键拉起&lt;/strong&gt;。页签上没有「推荐」两个字，推荐用 Docker 是本文的建议。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxc86obusqku42awhu3wi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxc86obusqku42awhu3wi.png" alt="接入新 Runner：名称 build-linux-01，标签 linux,docker，Docker 一键拉起命令里令牌还是占位符" width="799" height="493"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. 选一种接入方式，复制命令
&lt;/h2&gt;

&lt;p&gt;三个页签生成的命令，都已经带上了你填的地址、名称、标签、并发数和令牌。&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker 一键拉起（推荐）
&lt;/h3&gt;

&lt;p&gt;页签提示「从 AngusRepo 公开仓库拉取 repo.anguskit.com/docker-public/angus-runner-agent」。命令形如：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;always &lt;span class="nt"&gt;--name&lt;/span&gt; build-linux-01 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; /opt/AngusRunnerAgent/logs:/opt/AngusRunnerAgent/logs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; /opt/AngusRunnerAgent/data:/opt/AngusRunnerAgent/data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; /var/run/docker.sock:/var/run/docker.sock &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;RUNNER_SERVER_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;wss://git.anguskit.com/ws/runner &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;RUNNER_REGISTER_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;注册令牌&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;RUNNER_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;build-linux-01 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;RUNNER_LABELS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;linux,docker &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;RUNNER_MAX_CONCURRENCY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4 &lt;span class="se"&gt;\&lt;/span&gt;
  repo.anguskit.com/docker-public/angus-runner-agent:1.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;几个要点：&lt;code&gt;--restart=always&lt;/code&gt; 让运行器在机器重启后自动恢复；挂载 &lt;code&gt;/var/run/docker.sock&lt;/code&gt; 是为了让运行器能在这台机器上为每个任务启动容器；标签留空时默认是 &lt;code&gt;linux,docker&lt;/code&gt;。&lt;/p&gt;

&lt;h3&gt;
  
  
  Zip 安装
&lt;/h3&gt;

&lt;p&gt;页签提示「从 AngusRepo 下载 Zip，安装到 /opt/AngusRunnerAgent」。命令会依次下载并解压 &lt;code&gt;AngusRunnerAgent-1.0.0.zip&lt;/code&gt;，复制到 &lt;code&gt;/opt/AngusRunnerAgent&lt;/code&gt;，设置 &lt;code&gt;RUNNER_SERVER_URL&lt;/code&gt;、&lt;code&gt;RUNNER_REGISTER_TOKEN&lt;/code&gt; 等环境变量，然后执行 &lt;code&gt;./doctor-native.sh&lt;/code&gt; 检查本机环境，最后执行 &lt;code&gt;./startup-agent.sh&lt;/code&gt; 启动。&lt;/p&gt;

&lt;p&gt;这种方式下，任务直接在宿主机上执行，用的是机器上装好的 JDK、Node.js 等工具。页签提示「NATIVE 流水线不要加 docker 标签」：带了 &lt;code&gt;docker&lt;/code&gt; 标签的运行器会被当作 Docker 运行器，领不到宿主机任务。&lt;/p&gt;

&lt;h3&gt;
  
  
  一键安装脚本
&lt;/h3&gt;

&lt;p&gt;命令形如：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://git.anguskit.com/runner/install.sh | bash &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; https://git.anguskit.com &lt;span class="nt"&gt;--token&lt;/span&gt; &amp;lt;注册令牌&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; build-linux-01 &lt;span class="nt"&gt;--labels&lt;/span&gt; linux,docker &lt;span class="nt"&gt;--max-concurrency&lt;/span&gt; 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;页签提示「从控制面下载 /runner/install.sh，在目标机用 Docker 拉起公开镜像」。效果和 Docker 方式相同，适合不想手动拼 &lt;code&gt;docker run&lt;/code&gt; 参数的情况。&lt;/p&gt;

&lt;p&gt;两种 Docker 方式和 Zip 方式分别适合什么任务，见文末参考里的「Docker 还是宿主机」一文。&lt;/p&gt;

&lt;h2&gt;
  
  
  4. 在构建机上执行，等它上线
&lt;/h2&gt;

&lt;p&gt;把复制的命令粘贴到构建机的终端里执行。然后回到控制台，点 &lt;strong&gt;完成并等待上线&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;页面显示「正在等待 “build-linux-01” 心跳上线…」，每隔几秒检查一次。运行器连上之后，提示「Runner “build-linux-01” 已上线」，列表里出现这台机器，状态是 &lt;strong&gt;在线&lt;/strong&gt; 或 &lt;strong&gt;空闲&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;接入成功后，可以到 &lt;a href="https://git.anguskit.com/cicd/compute" rel="noopener noreferrer"&gt;CI/CD → 运行器&lt;/a&gt; 对照列表。当前实例已经有两台空闲的运行器：&lt;code&gt;dev-1&lt;/code&gt; 运行模式 NATIVE，执行器 SHELL，标签 &lt;code&gt;linux&lt;/code&gt;、&lt;code&gt;native&lt;/code&gt;、&lt;code&gt;dev&lt;/code&gt;，并发 0/2；&lt;code&gt;priv-1&lt;/code&gt; 运行模式 DOCKER，执行器 SHELL，标签 &lt;code&gt;linux&lt;/code&gt;、&lt;code&gt;docker&lt;/code&gt;，并发 0/2。按上面命令接入的 &lt;code&gt;build-linux-01&lt;/code&gt; 也会出现在这张表里，状态是 &lt;strong&gt;空闲&lt;/strong&gt;，最近心跳是「刚刚」。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmmu9an9yowdo1g9v6zqm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmmu9an9yowdo1g9v6zqm.png" alt="运行器列表：dev-1 与 priv-1 都是空闲，最近心跳为刚刚" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;等了 90 秒还没上线，页面会提示「等待心跳超时，请稍后刷新列表确认」。这不一定是失败，运行器可能还在拉镜像。过一两分钟刷新列表；仍然没有，按第 6 节排查。&lt;/p&gt;

&lt;h2&gt;
  
  
  5. 接好之后
&lt;/h2&gt;

&lt;p&gt;上线之后，日常只要盯状态、改标签或暂时停用，一般不用重装。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;运行器的状态。&lt;/strong&gt; 列表里有 &lt;strong&gt;在线&lt;/strong&gt;、&lt;strong&gt;空闲&lt;/strong&gt;、&lt;strong&gt;忙碌&lt;/strong&gt;、&lt;strong&gt;离线&lt;/strong&gt;、&lt;strong&gt;已禁用&lt;/strong&gt; 几种。运行器每 15 秒发一次心跳；超过 90 秒没有心跳、连接也断了，就会变成 &lt;strong&gt;离线&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;改名称、标签和并发。&lt;/strong&gt; 在列表里打开 &lt;strong&gt;编辑运行器&lt;/strong&gt; 修改，不用重新安装。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;重装或换机器。&lt;/strong&gt; 打开运行器详情的 &lt;strong&gt;安装运行器&lt;/strong&gt; 页签，在 &lt;strong&gt;安装 / 重装命令&lt;/strong&gt; 里重新生成命令。页面提示「重装到新主机时请重新生成或粘贴注册令牌」，因为原来那把注册令牌已经在第一次接入时用掉了。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;暂时不用这台机器。&lt;/strong&gt; 点 &lt;strong&gt;禁用&lt;/strong&gt;，它就不再领取新任务；要恢复时点 &lt;strong&gt;启用&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;私有化部署时，运维也可以在服务端配置一把共享注册令牌（&lt;code&gt;CICD_RUNNER_REGISTER_TOKEN&lt;/code&gt;），接入时直接粘贴。这把令牌的默认值是 &lt;code&gt;change-me-runner-register-token&lt;/code&gt;，上线前一定要改掉；否则任何知道默认值、又能访问 AngusGit 的机器，都能把自己注册成运行器来领取任务。&lt;/p&gt;

&lt;h2&gt;
  
  
  6. 接入失败时
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;你看到的&lt;/th&gt;
&lt;th&gt;原因&lt;/th&gt;
&lt;th&gt;怎么处理&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;「等待心跳超时，请稍后刷新列表确认」，刷新后仍没有&lt;/td&gt;
&lt;td&gt;构建机访问不到 &lt;strong&gt;控制面地址&lt;/strong&gt;；或者注册令牌已过期&lt;/td&gt;
&lt;td&gt;在构建机上 &lt;code&gt;curl -I &amp;lt;控制面地址&amp;gt;&lt;/code&gt; 检查网络；超过 1 小时的令牌重新生成，再复制一次命令&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker 方式执行后，容器很快退出&lt;/td&gt;
&lt;td&gt;令牌不对，或者连不上控制面&lt;/td&gt;
&lt;td&gt;执行 &lt;code&gt;docker logs build-linux-01&lt;/code&gt; 看最后几行报错&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker 方式拉镜像失败&lt;/td&gt;
&lt;td&gt;构建机访问不到 &lt;code&gt;repo.anguskit.com&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;让构建机能访问外网，或者把镜像提前导入到内网镜像仓库，再把命令里的镜像地址改成内网地址&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zip 方式 &lt;code&gt;doctor-native.sh&lt;/code&gt; 报错&lt;/td&gt;
&lt;td&gt;缺 JDK 21 或其他依赖&lt;/td&gt;
&lt;td&gt;按它打印的提示补装，再执行 &lt;code&gt;./startup-agent.sh&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;运行器上线了，但任务一直 &lt;strong&gt;等待中&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;任务要求的执行方式或标签和这台运行器对不上&lt;/td&gt;
&lt;td&gt;看运行详情里任务的等待原因，排查方法见文末参考里的「运行器不接任务」一文&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;运行器进程自己的日志，在运行器详情的 &lt;strong&gt;运行日志&lt;/strong&gt; 页签里能看到，页面说明是「Runner 进程日志，包含注册、心跳、接单、拉镜像和步骤执行记录」。旧版本运行器不上报这部分日志时，显示「暂无进程日志（需 Runner 升级到支持上报的版本）」，这时到构建机上看 &lt;code&gt;/opt/AngusRunnerAgent/logs&lt;/code&gt; 目录。&lt;/p&gt;

&lt;h2&gt;
  
  
  常见问题
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;一台机器能装几个运行器？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;通常一个就够了，用 &lt;strong&gt;最大并发 Job 数&lt;/strong&gt; 控制同时跑几个任务。需要在同一台机器上同时提供 Docker 和宿主机两种执行方式时，可以装两个：一个用 Docker 方式、带 &lt;code&gt;docker&lt;/code&gt; 标签，一个用 Zip 方式、不带 &lt;code&gt;docker&lt;/code&gt; 标签，名称要区分开。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;注册令牌过期了，已经接入的运行器会掉线吗？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;不会。控制台生成的注册令牌是一次性的，只在第一次接入时使用。接入成功后，AngusGit 会给这台运行器下发一把长期身份令牌，运行器把它保存在本机，以后重启、断线重连都用这把。要再接入一台新机器时，重新生成一把注册令牌即可。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;运行器要开放端口吗？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;不需要。是运行器主动连 AngusGit，构建机上不需要对外开放任何端口。&lt;/p&gt;

&lt;h2&gt;
  
  
  小结
&lt;/h2&gt;

&lt;p&gt;运行器是装在构建机上、替 AngusGit 执行流水线任务的程序。在 &lt;strong&gt;CI/CD → 运行器 → 新增 Runner&lt;/strong&gt; 里确认构建机能访问的 &lt;strong&gt;控制面地址&lt;/strong&gt;，填名称、标签和并发数，点 &lt;strong&gt;生成令牌&lt;/strong&gt;，选一种接入方式复制命令，到构建机上执行，再点 &lt;strong&gt;完成并等待上线&lt;/strong&gt;。有 Docker 的机器优先用 Docker 方式，要在宿主机上直接构建就用 Zip 方式并装好 JDK 21。私有化部署时，共享注册令牌的默认值必须改掉。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;结论：&lt;/strong&gt; 在 &lt;strong&gt;CI/CD → 运行器 → 新增 Runner&lt;/strong&gt; 填好构建机可达的 &lt;strong&gt;控制面地址&lt;/strong&gt;、标签并点 &lt;strong&gt;生成令牌&lt;/strong&gt;，复制命令到构建机执行后，列表出现 &lt;strong&gt;在线 / 空闲&lt;/strong&gt; 状态即接入完成。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  参考文章
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/docs/git/manual/12-runner-management" rel="noopener noreferrer"&gt;AngusGit 手册：运行器管理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/docs/git/manual/10-pipeline-basics" rel="noopener noreferrer"&gt;AngusGit 手册：流水线基础&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/pipeline-runner-docker-vs-native" rel="noopener noreferrer"&gt;流水线任务在哪跑：Docker 容器还是宿主机&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/pipeline-runner-not-picking-up-jobs" rel="noopener noreferrer"&gt;运行器不接任务：标签、在线和并发对不上&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>angusgit</category>
      <category>runner</category>
    </item>
    <item>
      <title>谁来审这段代码：用 CODEOWNERS 按目录自动指派评审人</title>
      <dc:creator>Wen Ping</dc:creator>
      <pubDate>Sat, 31 Oct 2026 04:06:59 +0000</pubDate>
      <link>https://dev.to/wenping/shui-lai-shen-zhe-duan-dai-ma-yong-codeowners-an-mu-lu-zi-dong-zhi-pai-ping-shen-ren-2lfc</link>
      <guid>https://dev.to/wenping/shui-lai-shen-zhe-duan-dai-ma-yong-codeowners-an-mu-lu-zi-dong-zhi-pai-ping-shen-ren-2lfc</guid>
      <description>&lt;p&gt;这篇讲怎么按目录自动指定谁来评审。看完你可以写一份 CODEOWNERS 文件，按路径写明每段代码由谁负责，打开 &lt;strong&gt;启用 CODEOWNERS&lt;/strong&gt; 之后，新建拉取请求就会按改动路径把这些人加为评审人。&lt;/p&gt;

&lt;p&gt;下文以仓库 &lt;code&gt;xlliu/test-pr-example&lt;/code&gt; 对照。这个仓库已经打开 &lt;strong&gt;需要代码评审&lt;/strong&gt; 和 &lt;strong&gt;需要所有评审人通过&lt;/strong&gt;（被指派的人都批准才能合并），但 &lt;strong&gt;启用 CODEOWNERS&lt;/strong&gt; 还是关的。它设置了两位默认评审人，也就是在仓库设置里固定指定、每条新拉取请求都会带上的人：一位显示名是 Angus、登录名是 &lt;code&gt;xlliu&lt;/code&gt;，另一位是 &lt;code&gt;admin&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7q2ammr19sjfcw6dvnbp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7q2ammr19sjfcw6dvnbp.png" alt="同一文件命中多行时用最后一行；这个仓库还没放 CODEOWNERS" width="800" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. 现在这条拉取请求是怎么来的
&lt;/h2&gt;

&lt;p&gt;还开着的 #9 标题是 &lt;code&gt;docs: 说明空购物车不计算折扣&lt;/code&gt;，从 &lt;code&gt;feature/link-empty-cart&lt;/code&gt; 合向 &lt;code&gt;main&lt;/code&gt;，作者是 Angus，正文写了 &lt;code&gt;Closes #4&lt;/code&gt;，只改了 1 个文件：&lt;code&gt;docs/empty-cart-note.md&lt;/code&gt;（&lt;code&gt;+3 -0&lt;/code&gt;）。&lt;/p&gt;

&lt;p&gt;评审人是 Angus 和 admin，两人都是 &lt;strong&gt;待评审&lt;/strong&gt;。合并卡片写着「此拉取请求尚未准备好合并」，下面是「暂无状态检查」和「仍有评审未完成，需等待评审通过后才能合并」，所以 &lt;strong&gt;合并拉取请求&lt;/strong&gt; 是灰的。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1gix7ttgv6x91bq7e4om.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1gix7ttgv6x91bq7e4om.png" alt="#9：Angus 和 admin 都待评审，合并按钮不可用" width="800" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;所以这两位来自仓库设置里的默认评审人，不是按文件路径算出来的；&lt;code&gt;main&lt;/code&gt; 上目前也没有 &lt;code&gt;.github/CODEOWNERS&lt;/code&gt;、根目录 &lt;code&gt;CODEOWNERS&lt;/code&gt; 或 &lt;code&gt;docs/CODEOWNERS&lt;/code&gt;。&lt;/p&gt;

&lt;h2&gt;
  
  
  2. CODEOWNERS 实际会做什么
&lt;/h2&gt;

&lt;p&gt;CODEOWNERS 只在&lt;strong&gt;新建拉取请求&lt;/strong&gt;时算一次，而且仓库要先打开 &lt;strong&gt;需要代码评审&lt;/strong&gt;；创建之后再往源分支推提交，不会按新文件补评审人。&lt;/p&gt;

&lt;p&gt;指派时会做两件事，再去重：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;放上设置里的默认评审人。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;启用 CODEOWNERS&lt;/strong&gt; 开着时，再读目标分支上的文件，按这次改动的路径追加人。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;所以默认名单里已经有的人，文件里再写一次也不会变成两行评审。这个仓库的默认名单已经是 &lt;code&gt;xlliu&lt;/code&gt; 和 &lt;code&gt;admin&lt;/code&gt; 两个人。文件里如果也只写这两位，打开开关后新建的拉取请求，评审人还是这两位。&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;能做&lt;/th&gt;
&lt;th&gt;做不到&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;创建拉取请求时，把默认评审人和路径匹配到的人并在一起&lt;/td&gt;
&lt;td&gt;拉取请求创建后再推送，不会重新算、也不会补人&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;只认 &lt;code&gt;@登录名&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;团队名（中间有 &lt;code&gt;/&lt;/code&gt;）和邮箱会被跳过&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;配合 &lt;strong&gt;需要所有评审人通过&lt;/strong&gt;，被指派的人都批准才能合并&lt;/td&gt;
&lt;td&gt;只开 CODEOWNERS、不开这一项时，CODEOWNERS 自己不拦合并&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;作者自己被指派时，不再给自己发「请求评审」通知。其他评审人会收到。&lt;/p&gt;

&lt;h2&gt;
  
  
  3. 文件怎么写
&lt;/h2&gt;

&lt;p&gt;CODEOWNERS 文件可以放在三个位置，按下面的顺序找，用找到的第一个：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;.github/CODEOWNERS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;仓库根目录的 &lt;code&gt;CODEOWNERS&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docs/CODEOWNERS&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;读的是拉取请求&lt;strong&gt;目标分支&lt;/strong&gt;上的文件。要合进 &lt;code&gt;main&lt;/code&gt;，就得先把文件合进 &lt;code&gt;main&lt;/code&gt;。只改在自己的分支上，对这条拉取请求不起作用。&lt;/p&gt;

&lt;p&gt;下面这份是给这个仓库准备的示例，&lt;strong&gt;还没有提交&lt;/strong&gt;。登录名用 &lt;code&gt;@xlliu&lt;/code&gt;，不要写显示名 &lt;code&gt;@Angus&lt;/code&gt;。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 没被后面规则盖住的文件
*                  @xlliu

# 根目录下的 service 目录
/service/          @admin

# 任意目录的 SQL，写在后面，盖住前面的规则
*.sql              @xlliu @admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;#&lt;/code&gt; 开头和行尾的注释会被拿掉，空行忽略。路径和人之间用空格分开。&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;写法&lt;/th&gt;
&lt;th&gt;匹配&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;所有文件。单独一行时做兜底&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/service/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;从仓库根目录算的 &lt;code&gt;service&lt;/code&gt; 目录，以及里面的文件。开头的 &lt;code&gt;/&lt;/code&gt; 表示锚定到根&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*.sql&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;任意一层目录里、以 &lt;code&gt;.sql&lt;/code&gt; 结尾的文件&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/docs/**/*.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;docs&lt;/code&gt; 下任意层级的 &lt;code&gt;.md&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;?&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;一个不是斜杠的字符。&lt;code&gt;v?.txt&lt;/code&gt; 能对上 &lt;code&gt;v1.txt&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;同一个文件命中多行时，用&lt;strong&gt;最后一行&lt;/strong&gt;。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docs/empty-cart-note.md&lt;/code&gt; 只命中 &lt;code&gt;*&lt;/code&gt;，人是 &lt;code&gt;@xlliu&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;service/Foo.java&lt;/code&gt; 先命中 &lt;code&gt;*&lt;/code&gt;，再命中 &lt;code&gt;/service/&lt;/code&gt;，留下 &lt;code&gt;@admin&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;service/db/init.sql&lt;/code&gt; 三行都命中，最后一行是 &lt;code&gt;*.sql&lt;/code&gt;，留下 &lt;code&gt;@xlliu&lt;/code&gt; 和 &lt;code&gt;@admin&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以 &lt;code&gt;!&lt;/code&gt; 开头不会当成排除。方括号不会当成字符集，只按字面量里的括号去匹配。&lt;/p&gt;

&lt;h2&gt;
  
  
  4. 开关在哪
&lt;/h2&gt;

&lt;p&gt;入口是 &lt;strong&gt;仓库设置 → 拉取请求&lt;/strong&gt;，地址带 &lt;code&gt;?tab=settings&amp;amp;section=pullrequests&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0vjwg4621cxnh1pdoqyk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0vjwg4621cxnh1pdoqyk.png" alt="需要所有评审人通过开着，启用 CODEOWNERS 关着" width="800" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这次看到的是：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;项&lt;/th&gt;
&lt;th&gt;状态&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;需要代码评审&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;开。说明是「合并前必须经过代码评审，开启后针对所有分支都生效。」&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;需要的评审人数&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;评审人&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;已选择 2 个：Angus、admin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;需要解决所有评论&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;关&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;需要所有评审人通过&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;开。说明是「合并前所有指派的评审人都必须通过评审。」&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;启用 CODEOWNERS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;关。说明是「根据 .github/CODEOWNERS 文件按代码路径自动指派评审人。」&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;评审通过后自动合并&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;开。还要这条拉取请求自己也打开自动合并才会合。#9 的合并按钮仍是灰的&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;自动指派评审人（负载均衡）&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;关&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;评审超时提醒（小时）&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0&lt;/code&gt;，表示关闭&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;打开 &lt;strong&gt;需要代码评审&lt;/strong&gt; 时，人数至少为 1，并且至少选一位评审人，否则保存会提示「启用代码评审时，必须设置评审人数并选择至少一个评审人」。保存成功的提示是「拉取请求设置已保存」。页面上已经是这些值时，打开设置只是查看，不必再保存。&lt;/p&gt;

&lt;p&gt;负载均衡开着、而且候选人数多于 &lt;strong&gt;需要的评审人数&lt;/strong&gt; 时，只留下未完成评审最少的那几位，人数等于所需人数。代码所有者可能被挑掉。这个仓库它是关的，两位默认评审人都会留下。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;需要所有评审人通过&lt;/strong&gt; 看的是已经指派的每一个人，包括默认评审人和 CODEOWNERS 追加的人。人数设成 1 时，如果没开「所有人通过」，一位批准就够人数。这个仓库开了「所有人通过」，所以 #9 要等 Angus 和 admin 都批准。页面上现在的红字是「仍有评审未完成，需等待评审通过后才能合并」。合并请求若仍然发出去，接口返回的是「需要所有评审人通过后才能合并，当前仍有「1」个评审人未通过」。#9 还开着，等两位评审人都通过再合并。&lt;/p&gt;

&lt;h2&gt;
  
  
  5. 和分支保护
&lt;/h2&gt;

&lt;p&gt;仓库设置里的 &lt;strong&gt;需要代码评审&lt;/strong&gt; 管的是拉取请求能不能合，并不阻止直接往 &lt;code&gt;main&lt;/code&gt; 推。这个仓库的 &lt;strong&gt;设置 → 分支保护&lt;/strong&gt; 写着「尚未设置分支保护规则」；要拦住直接推送、让改动必须走拉取请求，再点 &lt;strong&gt;添加规则&lt;/strong&gt;，给 &lt;code&gt;main&lt;/code&gt; 打开需要评审，并对管理员同样生效。做法见文末的分支保护一文。&lt;/p&gt;

&lt;p&gt;合并时如果目标分支的保护规则里填了评审人数，用保护规则上的人数；没填才用仓库设置里的 &lt;strong&gt;需要的评审人数&lt;/strong&gt;。&lt;strong&gt;需要所有评审人通过&lt;/strong&gt; 仍看仓库设置。&lt;/p&gt;

&lt;h2&gt;
  
  
  6. 没有按路径指派时
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;你看到的&lt;/th&gt;
&lt;th&gt;原因&lt;/th&gt;
&lt;th&gt;怎么处理&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;只有默认评审人&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;启用 CODEOWNERS&lt;/strong&gt; 关着，或目标分支上三个位置都没有文件。#9 就是前一种&lt;/td&gt;
&lt;td&gt;把文件合进目标分支，打开开关，保存后再新建一条拉取请求&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;文件里写了人，评审列表里没有&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@&lt;/code&gt; 后面不是登录名，或写了团队名、邮箱&lt;/td&gt;
&lt;td&gt;改成 &lt;code&gt;@xlliu&lt;/code&gt; 这种登录名。显示名 Angus 对不上&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;指派的不是预想的那一行&lt;/td&gt;
&lt;td&gt;同一个文件命中了多行，生效的是最后一行&lt;/td&gt;
&lt;td&gt;把更具体的规则放到后面，或把几个人写在同一行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;创建后又改了别的目录，新人没出现&lt;/td&gt;
&lt;td&gt;只在创建时算一次&lt;/td&gt;
&lt;td&gt;由作者或管理员手工把这个人加为评审人&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;人还没批准就被合并了&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;需要所有评审人通过&lt;/strong&gt; 关着，而且批准人数已经达到所需人数&lt;/td&gt;
&lt;td&gt;打开「所有人通过」。这个仓库已经开着&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  常见问题
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CODEOWNERS 里的人没有仓库权限会怎样？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;他仍会被加进评审人。要打开拉取请求并提交评审，需要这个仓库的读取权限。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;社区版能用吗？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;能。&lt;strong&gt;需要代码评审&lt;/strong&gt;、&lt;strong&gt;启用 CODEOWNERS&lt;/strong&gt; 和 &lt;strong&gt;需要所有评审人通过&lt;/strong&gt; 都在仓库 &lt;strong&gt;设置 → 拉取请求&lt;/strong&gt; 里。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;两个人里任意一个批准就行，能做到吗？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;把两个人写在同一行，关掉 &lt;strong&gt;需要所有评审人通过&lt;/strong&gt;，&lt;strong&gt;需要的评审人数&lt;/strong&gt; 填 &lt;code&gt;1&lt;/code&gt;，并保持负载均衡关闭。这样一位批准就够人数。代价是：任何一位已指派的人批准都算数，不限是不是代码所有者。这个仓库现在开着「所有人通过」，所以不是这种算法。&lt;/p&gt;

&lt;h2&gt;
  
  
  小结
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;xlliu/test-pr-example&lt;/code&gt; 没有 CODEOWNERS 文件，&lt;strong&gt;启用 CODEOWNERS&lt;/strong&gt; 是关的。#9 的评审人 Angus 和 admin 来自默认名单。两人都还是待评审，合并按钮不可用。&lt;/p&gt;

&lt;p&gt;要按目录加人：把 &lt;code&gt;.github/CODEOWNERS&lt;/code&gt; 合进目标分支，规则用 &lt;code&gt;@登录名&lt;/code&gt;，同一文件以最后一行为准；然后打开 &lt;strong&gt;启用 CODEOWNERS&lt;/strong&gt; 并保存。新建的拉取请求会把文件里匹配到的人和默认评审人并在一起。这个仓库已经打开 &lt;strong&gt;需要所有评审人通过&lt;/strong&gt;，负载均衡是关的，被指派的人都要批准。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;结论：&lt;/strong&gt; 默认评审人在创建拉取请求时就会指派。CODEOWNERS 要文件先在目标分支上，并且打开启用开关，才会按路径把登录名并进去。需要所有评审人通过开着时，这些人都批准才能合并。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  参考文章
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/docs/git/manual/07-code-review" rel="noopener noreferrer"&gt;AngusGit 手册：代码评审&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/docs/git/manual/06-pull-request" rel="noopener noreferrer"&gt;AngusGit 手册：拉取请求&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/protect-git-main-branch-pull-request" rel="noopener noreferrer"&gt;怎么保护 Git 主分支：先开分支保护，再用合并请求合代码&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/code-review-in-pull-request" rel="noopener noreferrer"&gt;在合并请求里做完代码评审：看 Diff、写行评、通过或打回&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>angusgit</category>
      <category>pullrequest</category>
    </item>
    <item>
      <title>在合并请求里做完代码评审：看 Diff、写行评、通过或打回</title>
      <dc:creator>Wen Ping</dc:creator>
      <pubDate>Sat, 31 Oct 2026 04:06:22 +0000</pubDate>
      <link>https://dev.to/wenping/zai-he-bing-qing-qiu-li-zuo-wan-dai-ma-ping-shen-kan-diff-xie-xing-ping-tong-guo-huo-da-hui-1032</link>
      <guid>https://dev.to/wenping/zai-he-bing-qing-qiu-li-zuo-wan-dai-ma-ping-shen-kan-diff-xie-xing-ping-tong-guo-huo-da-hui-1032</guid>
      <description>&lt;p&gt;这篇讲同事发来拉取请求、请你看代码时，从哪里下手。看完你可以在侧栏 &lt;strong&gt;评审&lt;/strong&gt; 页打开差异（Diff，即改动前后的对比），对具体的行写意见，必要时用 &lt;code&gt;suggestion&lt;/code&gt; 给出替换代码，再提交 &lt;strong&gt;批准&lt;/strong&gt;、&lt;strong&gt;请求更改&lt;/strong&gt; 或 &lt;strong&gt;评论&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;拉取请求（Pull Request，也叫合并请求）是把一个分支上的改动申请合进另一个分支的请求。AngusGit 界面上叫它 &lt;strong&gt;拉取请求&lt;/strong&gt;，和 GitLab 的 Merge Request、GitHub 的 Pull Request 是同一件事。要注意的是，行评和建议都在侧栏的 &lt;strong&gt;评审&lt;/strong&gt; 页里写，拉取请求页本身不提供这个入口。如果想让仓库强制要求「必须评审才能合并」，可以看文末参考文章里的分支保护一文。&lt;/p&gt;

&lt;p&gt;下文以还开着的 &lt;a href="https://git.anguskit.com/pulls/xlliu/test-cicd-example/1" rel="noopener noreferrer"&gt;xlliu/test-cicd-example #1&lt;/a&gt; 为例，它的标题是「SC-02/SC-04 PR trigger verification」，从源分支 &lt;code&gt;verify-pr-sc0204&lt;/code&gt; 合向目标分支 &lt;code&gt;main&lt;/code&gt;。Angus 在这条请求上的评审仍是 &lt;strong&gt;待评审&lt;/strong&gt;，在 &lt;strong&gt;文件变更&lt;/strong&gt; 里可以展开差异。如果想回看一次完整的评审对话，可以打开两条已经合并的例子：&lt;a href="https://git.anguskit.com/pulls/xlliu/test-pr-example/1" rel="noopener noreferrer"&gt;xlliu/test-pr-example 的 #1&lt;/a&gt; 和 &lt;a href="https://git.anguskit.com/pulls/xlliu/test-review-example/1" rel="noopener noreferrer"&gt;xlliu/test-review-example 的 #1&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqotj3srhg82wp1phz6tl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqotj3srhg82wp1phz6tl.png" alt="一次评审的来回：批准、请求更改、评论" width="799" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. 三种结论分别意味着什么
&lt;/h2&gt;

&lt;p&gt;评审的最后一步，是在三种结论里选一个。先弄清它们的区别，看代码时就知道自己要找什么。&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;结论&lt;/th&gt;
&lt;th&gt;意思&lt;/th&gt;
&lt;th&gt;对合并的影响&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;批准&lt;/td&gt;
&lt;td&gt;我看过了，同意合进去&lt;/td&gt;
&lt;td&gt;计入批准人数，人数够了合并卡才放行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;请求更改&lt;/td&gt;
&lt;td&gt;这些问题要先改完&lt;/td&gt;
&lt;td&gt;作者改完并重新获得批准前，合并卡显示还不能合并&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;评论&lt;/td&gt;
&lt;td&gt;有疑问或建议，但不挡合并&lt;/td&gt;
&lt;td&gt;不计入批准，也不阻止合并&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;拿不准时选 &lt;strong&gt;评论&lt;/strong&gt;，把疑问写清楚，等作者回答后再决定批准还是请求更改。如果发现某处问题会导致线上出错，就要选 &lt;strong&gt;请求更改&lt;/strong&gt;，因为批准之后再补一句「记得改」，并不能拦住合并。&lt;/p&gt;

&lt;h2&gt;
  
  
  2. 打开要评审的那一条
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;进入评审列表&lt;/strong&gt;
在侧栏点 &lt;strong&gt;评审&lt;/strong&gt;，进入页头为 &lt;strong&gt;代码评审&lt;/strong&gt; 的页面。默认停在 &lt;strong&gt;待评审&lt;/strong&gt; 页签，这里列的是还等你处理的请求，卡片上标着 &lt;strong&gt;已逾期&lt;/strong&gt; 的可以优先处理。如果同一条拉取请求指派了多位评审人，列表里会按评审人各出现一行。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;找到要评审的请求&lt;/strong&gt;
请求多的时候，可以在「搜索评审...」框里按标题搜，或者把仓库筛选从 &lt;strong&gt;所有仓库&lt;/strong&gt; 换成具体的仓库。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;打开评审详情&lt;/strong&gt;
点卡片进入评审详情。顶部的统计会显示这次改动包含几个提交、几个文件，以及新增和删除的行数。随时可以点 &lt;strong&gt;查看拉取请求&lt;/strong&gt; 回到这条请求的原页面。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;以 Angus 的评审列表为例：&lt;strong&gt;待评审&lt;/strong&gt; 是 6，&lt;strong&gt;已评审&lt;/strong&gt; 是 18，顶部有搜索框、&lt;strong&gt;所有仓库&lt;/strong&gt; 和 &lt;strong&gt;选择评审人&lt;/strong&gt; 三个筛选。列表里有三条请求，分别是 &lt;code&gt;xlliu/test-pr-example&lt;/code&gt; 的「docs: 说明空购物车不计算折扣 #9」（描述里写的是 &lt;code&gt;Closes #4&lt;/code&gt;）、&lt;code&gt;anguskit/Markdown&lt;/code&gt; 的「docs: cross-repo review list count fixture (round 25) #2」和 &lt;code&gt;xlliu/test-cicd-example&lt;/code&gt; 的「SC-02/SC-04 PR trigger verification #1」，后两条都标着 &lt;strong&gt;已逾期&lt;/strong&gt;。因为每条请求都指派了两位评审人（例如 #9 是 Angus 和 admin），所以它们在列表里各占两行。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4jkuueq8u8gyuk10h80s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4jkuueq8u8gyuk10h80s.png" alt="评审待办：待评审 6，已评审 18" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. 先看作者想做什么
&lt;/h2&gt;

&lt;p&gt;打开评审详情后，先别急着看代码，而是在 &lt;strong&gt;讨论&lt;/strong&gt; 页签读作者写的描述：这次要解决什么问题、改动范围有多大、有没有特别希望你看的地方。不清楚改动目的就直接看差异，很容易只挑出格式问题，却漏掉真正的逻辑错误。&lt;/p&gt;

&lt;p&gt;同一页上还有 &lt;strong&gt;状态检查&lt;/strong&gt;，也就是流水线等自动检查写回来的结果，每一项显示为 &lt;strong&gt;成功&lt;/strong&gt;、&lt;strong&gt;失败&lt;/strong&gt; 或 &lt;strong&gt;进行中&lt;/strong&gt;。如果有一项 &lt;strong&gt;失败&lt;/strong&gt;，先看失败的是哪一项：要是和你要评审的代码有关，例如编译不过或测试没通过，可以直接请求更改，请作者先让检查通过。&lt;/p&gt;

&lt;h2&gt;
  
  
  4. 逐个文件看差异，对具体的行写意见
&lt;/h2&gt;

&lt;p&gt;弄清改动目的之后，切到 &lt;strong&gt;文件变更&lt;/strong&gt; 页签开始看代码。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;按文件树一个个看&lt;/strong&gt;
左侧是 &lt;strong&gt;文件树&lt;/strong&gt;。每看完一个文件，就在树上勾选它，标记为 &lt;strong&gt;已查看&lt;/strong&gt;；文件多的时候，这样能避免漏掉被重命名或删除的文件。大文件默认只显示前面一部分，点 &lt;strong&gt;展开完整 Diff&lt;/strong&gt; 可以看到全部内容。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;选一种看得顺的视图&lt;/strong&gt;
&lt;strong&gt;统一视图&lt;/strong&gt; 把删除的行和新增的行上下排在一起，适合看小改动；&lt;strong&gt;分栏视图&lt;/strong&gt; 左边是修改前、右边是修改后，适合看被大段重写的函数。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;在要讨论的行上留言&lt;/strong&gt;
把鼠标移到要讨论的那一行，点 &lt;strong&gt;对该行添加评论&lt;/strong&gt;，写下意见后点 &lt;strong&gt;评论&lt;/strong&gt;。这时评论还是 &lt;strong&gt;草稿&lt;/strong&gt;，作者看不到，所以你可以继续看其他文件、写其他行评，最后一次性发出。如果需要和同事讨论某一行，可以点 &lt;strong&gt;复制行链接&lt;/strong&gt; 发给他。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqwlu8ix3nbg4k0e15bct.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqwlu8ix3nbg4k0e15bct.png" alt="文件变更：index.js 第 9 行的草稿评论" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;上图以 &lt;code&gt;xlliu/test-cicd-example&lt;/code&gt; #1 的评审页为例：在 &lt;strong&gt;文件变更&lt;/strong&gt; 的 &lt;strong&gt;统一视图&lt;/strong&gt; 里，对 &lt;code&gt;apps/service-a/src/index.js&lt;/code&gt; 新增的第 9 行写了一条评论。这条评论标着 &lt;strong&gt;草稿&lt;/strong&gt;，作者还看不到；左侧文件树上的两个文件也都还没有标记为已查看。&lt;/p&gt;

&lt;p&gt;写行评时注意下面三点，作者能更快改对：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;说清楚问题是什么，以及为什么是问题。「这里会空指针」比「这里有问题」有用得多。&lt;/li&gt;
&lt;li&gt;分清必须改的和只是建议的。如果只是个人偏好的写法，可以在开头注明「可选」，作者不改也不影响你批准。&lt;/li&gt;
&lt;li&gt;能给出代码时就直接给出，写法见下一节。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. 直接给出替换代码
&lt;/h2&gt;

&lt;p&gt;有些意见用文字很难说清，这时不如直接写出改好的代码。做法是在行评里放一个 &lt;code&gt;suggestion&lt;/code&gt; 代码块，块里写的就是你希望这一行变成的样子：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;这里没有处理 raw 为空的情况，建议先判断一下：

```suggestion
if (raw == null || raw.isBlank()) return false;
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;发出之后，这条评论会显示为 &lt;strong&gt;建议的变更&lt;/strong&gt;。作者或有写权限的人点 &lt;strong&gt;应用建议&lt;/strong&gt; 后，AngusGit 会用块里的内容替换你评论的那一行（块里可以写多行），并在源分支上生成一个新提交，成功后显示 &lt;strong&gt;已应用&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;使用建议时注意三点：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;建议是按行号替换的。如果你写完建议后作者又改过这个文件，行号可能已经错开，所以应用前先刷新差异，确认那一行还是你要改的那一行。&lt;/li&gt;
&lt;li&gt;一条评论只识别第一个 &lt;code&gt;suggestion&lt;/code&gt; 块。要改几处不相邻的代码，就分成几条行评来写。&lt;/li&gt;
&lt;li&gt;应用建议会直接往作者的分支上加提交。即使评审人自己有写权限，也最好先和作者说一声再点，免得作者本地还有没推送的改动，之后推送时产生冲突。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;另外，如果拉取请求的源分支在 Fork 仓库（从别人的仓库复制出来的个人副本）里，或者要改的是二进制文件，就不能应用建议。这时把代码写在普通评论里，请作者自己修改。&lt;/p&gt;

&lt;h2&gt;
  
  
  6. 提交结论
&lt;/h2&gt;

&lt;p&gt;所有文件看完之后，点顶部的 &lt;strong&gt;提交评审&lt;/strong&gt;，弹出的对话框说明是「选择你的评审决定并添加评论」。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;选择决定&lt;/strong&gt;
在 &lt;strong&gt;批准&lt;/strong&gt;、&lt;strong&gt;请求更改&lt;/strong&gt;、&lt;strong&gt;评论&lt;/strong&gt; 中选一个，含义见第 1 节。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;写一段总评&lt;/strong&gt;
在 &lt;strong&gt;评论&lt;/strong&gt; 框里写给作者的整体结论。批准时一句话就够，例如「逻辑没问题，可以合」；请求更改时，列出必须改的几处，这样作者不用自己从十几条行评里分辨哪些是必须改的。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;发出&lt;/strong&gt;
按钮上的 &lt;code&gt;+N&lt;/code&gt; 表示会把你之前写的 N 条草稿行评一起发出。确认无误后点 &lt;strong&gt;提交评审&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;提交之后，你的结论会出现在拉取请求 &lt;strong&gt;对话&lt;/strong&gt; 页的审查区，作者也能在文件差异上看到你的每一条行评。同时，这条请求会从你的 &lt;strong&gt;待评审&lt;/strong&gt; 移到 &lt;strong&gt;已评审&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp9e2zrcybrzp6i6r3ndb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp9e2zrcybrzp6i6r3ndb.png" alt="提交评审：选中请求更改，按钮带 +1" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;上图的对话框里选中了 &lt;strong&gt;请求更改&lt;/strong&gt;，按钮上的 &lt;code&gt;+1&lt;/code&gt; 就是前面那条草稿行评，点 &lt;strong&gt;提交评审&lt;/strong&gt; 时会一起发出；总评可以留空。在点 &lt;strong&gt;提交评审&lt;/strong&gt; 之前，这条评审的状态仍是 &lt;strong&gt;待评审&lt;/strong&gt;。&lt;/p&gt;

&lt;h2&gt;
  
  
  7. 作者改完之后，再审一遍
&lt;/h2&gt;

&lt;p&gt;作者收到 &lt;strong&gt;请求更改&lt;/strong&gt; 后，在本地按意见修改，再推送到同一个源分支。不需要新建拉取请求，新提交会自动出现在原来这一条里。&lt;/p&gt;

&lt;p&gt;某条讨论按意见改好之后，作者或评审人可以点 &lt;strong&gt;标记为已解决&lt;/strong&gt;。如果仓库要求合并前解决所有评论，没有标记的讨论会一直卡在合并条件里。&lt;/p&gt;

&lt;p&gt;作者推送之后，回到这条评审再看一遍：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;在 &lt;strong&gt;提交&lt;/strong&gt; 页签找到作者新推的几个提交，点 &lt;strong&gt;查看变更&lt;/strong&gt;，只看这次新改的内容，不必把整个差异从头再看。&lt;/li&gt;
&lt;li&gt;确认你提的问题都改好了；还有疑问的地方，继续写行评。&lt;/li&gt;
&lt;li&gt;再次点 &lt;strong&gt;提交评审&lt;/strong&gt;，这次通常选 &lt;strong&gt;批准&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;之前给过 &lt;strong&gt;批准&lt;/strong&gt; 的人，在作者推送新提交后，会看到自己的批准被标成 &lt;strong&gt;需重新评审&lt;/strong&gt;。如果目标分支开了分支保护（针对某个分支设置的合并与推送规则），这些旧批准就不再计入人数，必须有人对最新代码重新批准，合并卡才会放行。这样能保证被批准的始终是最新那一版代码。&lt;/p&gt;

&lt;h2&gt;
  
  
  8. 合并在拉取请求页完成，没空时可以委托
&lt;/h2&gt;

&lt;p&gt;评审页只负责给出结论，不负责合并代码。等批准人数达到仓库要求、状态检查通过、也没有冲突时，由作者或维护者回到拉取请求的 &lt;strong&gt;对话&lt;/strong&gt; 页点 &lt;strong&gt;合并拉取请求&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;如果你暂时没空评审，可以在待评审状态下点 &lt;strong&gt;委托&lt;/strong&gt;，在 &lt;strong&gt;委托给&lt;/strong&gt; 里选另一位成员，再点 &lt;strong&gt;委托&lt;/strong&gt;。这条请求就会从你的待评审列表移到对方那里。&lt;/p&gt;

&lt;h2&gt;
  
  
  9. 卡住时
&lt;/h2&gt;

&lt;p&gt;评审过程中遇到下面这些情况，可以按表排查：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;你看到的&lt;/th&gt;
&lt;th&gt;原因&lt;/th&gt;
&lt;th&gt;怎么处理&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;评审详情里没有 &lt;strong&gt;提交评审&lt;/strong&gt; 按钮&lt;/td&gt;
&lt;td&gt;你不是这条请求的评审人，或者这条请求已经合并&lt;/td&gt;
&lt;td&gt;请作者或仓库管理员把你加为评审人。已经合并的请求不能再提交结论&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;作者说看不到你的行评&lt;/td&gt;
&lt;td&gt;行评还是草稿，你还没点 &lt;strong&gt;提交评审&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;打开 &lt;strong&gt;提交评审&lt;/strong&gt;，选一个结论发出。按钮上的 &lt;code&gt;+N&lt;/code&gt; 就是还没发出的条数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;在拉取请求的 &lt;strong&gt;文件变更&lt;/strong&gt; 里找不到行评入口&lt;/td&gt;
&lt;td&gt;那一页只用来浏览，页顶写着「行级评论与建议请在评审页完成」&lt;/td&gt;
&lt;td&gt;点 &lt;strong&gt;前往评审页评论&lt;/strong&gt;，到评审页写&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;你已经批准，合并卡仍写着还不能合并&lt;/td&gt;
&lt;td&gt;批准人数不够；或你批准后作者又推了提交，批准变成 &lt;strong&gt;需重新评审&lt;/strong&gt;；或有冲突、状态检查没过、讨论没解决&lt;/td&gt;
&lt;td&gt;对照合并卡上写出的那一条原因处理。是 &lt;strong&gt;需重新评审&lt;/strong&gt; 就对最新的代码再批准一次&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;点 &lt;strong&gt;应用建议&lt;/strong&gt; 提示 &lt;strong&gt;应用建议失败&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;评论里没有 &lt;code&gt;suggestion&lt;/code&gt; 块，评论不在某一行上，文件不是文本文件，或者这条请求的源分支在 Fork 里&lt;/td&gt;
&lt;td&gt;确认建议写在行评里、块的开头是三个反引号加 &lt;code&gt;suggestion&lt;/code&gt;。Fork 来的请求和二进制文件，请作者手动修改&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  常见问题
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;一条请求要几个人评审？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;由仓库决定。&lt;strong&gt;设置 → 拉取请求&lt;/strong&gt; 里的 &lt;strong&gt;需要的评审人数&lt;/strong&gt;，或者分支保护里的 &lt;strong&gt;必需的评审人数&lt;/strong&gt;，填的就是合并前需要的批准数，达到之后合并卡才会放行。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;选了「评论」，作者还能合并吗？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;能。&lt;strong&gt;评论&lt;/strong&gt; 不计入批准，也不会挡住合并。如果你担心的问题必须先改，就要选 &lt;strong&gt;请求更改&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;评审意见写错了怎么办？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;在那条讨论下面点 &lt;strong&gt;回复&lt;/strong&gt;，写清楚更正的内容，作者会按最新的回复修改。如果是结论本身选错了，就再提交一次评审，换成正确的结论。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;一次改动太大，看不完怎么办？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;在描述或总评里请作者拆成几条小的拉取请求，每条只做一件事。改动控制在几百行以内，评审人才能真正看仔细。&lt;/p&gt;

&lt;h2&gt;
  
  
  小结
&lt;/h2&gt;

&lt;p&gt;一次完整的评审分四步：先在 &lt;strong&gt;评审&lt;/strong&gt; 里打开待办；再读描述和状态检查，弄清作者想做什么；然后在 &lt;strong&gt;文件变更&lt;/strong&gt; 里逐个文件看，对具体的行写意见，能直接给代码时就用 &lt;code&gt;suggestion&lt;/code&gt; 块；最后用 &lt;strong&gt;提交评审&lt;/strong&gt; 一次发出结论。作者推送新提交之后，你只需回来看新增的改动，再提交一次结论。真正的合并，由作者或维护者在拉取请求页完成。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;结论：&lt;/strong&gt; 代码评审在侧栏 &lt;strong&gt;评审&lt;/strong&gt; 页完成：逐文件写下的行评会先存为草稿，点 &lt;strong&gt;提交评审&lt;/strong&gt; 选择 &lt;strong&gt;批准&lt;/strong&gt;、&lt;strong&gt;请求更改&lt;/strong&gt; 或 &lt;strong&gt;评论&lt;/strong&gt; 后才会一并公开并计入合并条件。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  参考文章
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/docs/git/manual/07-code-review" rel="noopener noreferrer"&gt;AngusGit 手册：代码审查（评审列表、行评、提交结论、委托）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/docs/git/guides/review-pull-request" rel="noopener noreferrer"&gt;AngusGit 使用指南：评审一条拉取请求&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/docs/git/manual/06-pull-request" rel="noopener noreferrer"&gt;AngusGit 手册：拉取请求（合并条件、冲突）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/protect-git-main-branch-pull-request" rel="noopener noreferrer"&gt;怎么保护 Git 主分支：先开分支保护，再用合并请求合代码&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/self-hosted-git-first-push-ssh-https" rel="noopener noreferrer"&gt;自建 Git 之后怎么推代码：配 SSH 或 HTTPS，完成第一次 push&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>angusgit</category>
      <category>pullrequest</category>
    </item>
    <item>
      <title>HTTPS 克隆私有仓库：密码填访问令牌</title>
      <dc:creator>Wen Ping</dc:creator>
      <pubDate>Sat, 31 Oct 2026 04:05:46 +0000</pubDate>
      <link>https://dev.to/wenping/https-ke-long-si-you-cang-ku-mi-ma-tian-fang-wen-ling-pai-1h7o</link>
      <guid>https://dev.to/wenping/https-ke-long-si-you-cang-ku-mi-ma-tian-fang-wen-ling-pai-1h7o</guid>
      <description>&lt;p&gt;这篇讲用 HTTPS 地址克隆 AngusGit 私有仓库时，用户名和密码分别填什么。看完你可以生成一把只读、只对当前仓库有效的访问令牌，用它完成克隆，并在本机或 CI 里保存这把令牌，避免写进克隆地址。&lt;/p&gt;

&lt;p&gt;下文用 AngusGit 线上实例上的真实私有仓库 &lt;code&gt;https://git.anguskit.com/anguskit/AngusKit.git&lt;/code&gt; 做示例。照着做时，换成你自己的仓库地址。要用 SSH 密钥，或者要第一次往空仓库推代码，见文末参考里的第一次推送一文。&lt;/p&gt;

&lt;h2&gt;
  
  
  1. 用户名和密码各填什么
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;终端问的&lt;/th&gt;
&lt;th&gt;填什么&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Username for 'https://git.anguskit.com':&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;你的 AngusGit 登录名&lt;/td&gt;
&lt;td&gt;不是团队名，也不是仓库所有者。示例里的所有者是团队 &lt;code&gt;anguskit&lt;/code&gt;，用户名仍填你自己的登录名&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Password for 'https://&amp;lt;登录名&amp;gt;@git.anguskit.com':&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;访问令牌&lt;/td&gt;
&lt;td&gt;登录密码也能用，但令牌更安全，原因见下&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;推荐令牌而不用登录密码，有三个原因：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;登录密码能登录整个控制台。它一旦被存进凭据缓存或脚本又泄露出去，别人能做的远不止克隆代码。令牌只用于 Git 克隆和推送，不能用来登录控制台。&lt;/li&gt;
&lt;li&gt;令牌可以只给 &lt;code&gt;git:read&lt;/code&gt;，只给某几个仓库。即使泄露，别人也只能读这几个仓库。&lt;/li&gt;
&lt;li&gt;令牌有过期时间，也能单独吊销。吊销一把令牌，不影响你的登录密码和其他令牌。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. 生成一把只读令牌
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;打开令牌页&lt;/strong&gt;
侧栏点 &lt;strong&gt;我的&lt;/strong&gt;，切到 &lt;strong&gt;访问令牌&lt;/strong&gt; 页签，点 &lt;strong&gt;生成新令牌&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;名称、期限和身份确认&lt;/strong&gt;
&lt;strong&gt;令牌名称&lt;/strong&gt; 写清用途，例如「笔记本克隆 AngusKit」，以后在列表里一眼能认出来。&lt;strong&gt;过期时间&lt;/strong&gt; 和下一步的权限，生成之后都不能再改，要一次选对。&lt;strong&gt;过期时间&lt;/strong&gt; 可选 &lt;strong&gt;30天&lt;/strong&gt;、&lt;strong&gt;60天&lt;/strong&gt;、&lt;strong&gt;90天&lt;/strong&gt; 或 &lt;strong&gt;365天&lt;/strong&gt;。&lt;strong&gt;密码验证&lt;/strong&gt; 里输入你当前的登录密码，确认是你本人在操作。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;只给需要的权限&lt;/strong&gt;
&lt;strong&gt;权限范围&lt;/strong&gt; 勾选 &lt;code&gt;git:read&lt;/code&gt;，页面说明是「允许 clone / fetch」。&lt;strong&gt;仓库作用域&lt;/strong&gt; 选 &lt;strong&gt;指定仓库&lt;/strong&gt;，在列表里勾选 &lt;code&gt;anguskit/AngusKit&lt;/code&gt;，权限设为 &lt;strong&gt;READ&lt;/strong&gt;。以后这把令牌只能读这一个仓库。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;马上保存&lt;/strong&gt;
点 &lt;strong&gt;生成令牌&lt;/strong&gt;。页面提示「请立即复制此令牌。出于安全考虑，它将不会再次显示。」点 &lt;strong&gt;复制&lt;/strong&gt;，存进你的密码管理器，再点 &lt;strong&gt;我已保存此令牌&lt;/strong&gt;。之后列表里只显示令牌名称，看不到原文；丢了就只能重新生成。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;以后还要用这把令牌推送时，&lt;strong&gt;权限范围&lt;/strong&gt; 要勾 &lt;code&gt;git:write&lt;/code&gt;，仓库权限设为 &lt;strong&gt;WRITE&lt;/strong&gt;。只设 &lt;strong&gt;WRITE&lt;/strong&gt; 而不勾 &lt;code&gt;git:write&lt;/code&gt;，页面会提示「仓库授予 WRITE 时必须同时勾选 git:write」。&lt;/p&gt;

&lt;h2&gt;
  
  
  3. 克隆
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://git.anguskit.com/anguskit/AngusKit.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;终端先打印 &lt;code&gt;Cloning into 'AngusKit'...&lt;/code&gt;，然后依次出现两个提示：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Username for 'https://git.anguskit.com':
Password for 'https://&amp;lt;登录名&amp;gt;@git.anguskit.com':
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;第一个填登录名，第二个粘贴刚才保存的令牌。粘贴时屏幕上不显示任何字符，这是正常的，粘贴完直接回车。接着出现 &lt;code&gt;Receiving objects&lt;/code&gt; 的进度，就说明认证已经通过、正在下载。命令结束后当前目录下多出一个 &lt;code&gt;AngusKit&lt;/code&gt; 文件夹，克隆就完成了。&lt;/p&gt;

&lt;h2&gt;
  
  
  4. 让 Git 记住令牌
&lt;/h2&gt;

&lt;p&gt;不想每次拉取都粘贴一遍令牌，就交给系统的凭据管理器保存：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;macOS：&lt;/strong&gt; Git 默认使用钥匙串，第一次输入之后自动保存。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows：&lt;/strong&gt; Git for Windows 自带凭据管理器，第一次输入之后自动保存。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linux：&lt;/strong&gt; 下面这一行让 Git 把凭据在内存里保留一段时间（默认 15 分钟），不写入磁盘：
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; credential.helper cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;也可以用 &lt;code&gt;credential.helper store&lt;/code&gt; 长期保存，但它会把令牌以明文写进 &lt;code&gt;~/.git-credentials&lt;/code&gt;，只适合你一个人用的电脑。&lt;/p&gt;

&lt;h2&gt;
  
  
  5. 在脚本和 CI 里克隆
&lt;/h2&gt;

&lt;p&gt;CI 机器上没有人来输入密码。这时常见的写法是把令牌拼进地址，例如 &lt;code&gt;https://&amp;lt;登录名&amp;gt;:&amp;lt;令牌&amp;gt;@git.anguskit.com/...&lt;/code&gt;。不要这样做：这个地址会被写进克隆出来的 &lt;code&gt;.git/config&lt;/code&gt;，还容易出现在构建日志里。&lt;/p&gt;

&lt;p&gt;改用请求头传令牌。先把令牌放进 CI 的密钥变量，例如 &lt;code&gt;ANGUSGIT_TOKEN&lt;/code&gt;，然后：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git &lt;span class="nt"&gt;-c&lt;/span&gt; http.extraHeader&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ANGUSGIT_TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  clone https://git.anguskit.com/anguskit/AngusKit.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;-c&lt;/code&gt; 只对这一条命令生效，令牌不会写进 &lt;code&gt;.git/config&lt;/code&gt;。之后在这个目录里再执行 &lt;code&gt;git fetch&lt;/code&gt; 或 &lt;code&gt;git pull&lt;/code&gt; 时，同样要带上这段 &lt;code&gt;-c&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;给 CI 用的令牌，单独生成一把：名称写明是哪条流水线，&lt;strong&gt;权限范围&lt;/strong&gt; 只给 &lt;code&gt;git:read&lt;/code&gt;，&lt;strong&gt;仓库作用域&lt;/strong&gt; 只选它要拉的仓库。这样流水线出问题时，吊销这一把就行，不影响其他人。&lt;/p&gt;

&lt;h2&gt;
  
  
  6. 令牌过期了，或者要换一把
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;访问令牌&lt;/strong&gt; 列表里，每把令牌都显示状态（&lt;strong&gt;活跃&lt;/strong&gt;、&lt;strong&gt;已过期&lt;/strong&gt; 或 &lt;strong&gt;已吊销&lt;/strong&gt;）、&lt;strong&gt;最后使用&lt;/strong&gt; 时间、&lt;strong&gt;使用次数&lt;/strong&gt; 和 &lt;strong&gt;过期于&lt;/strong&gt; 的日期。&lt;/p&gt;

&lt;p&gt;令牌过期或被吊销后，用它克隆或拉取会认证失败。按下面三步换一把：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;按第 2 节重新生成一把令牌。&lt;/li&gt;
&lt;li&gt;清掉 Git 缓存的旧令牌，否则 Git 会一直拿旧的去试：
&lt;pre&gt;&lt;code&gt;printf "protocol=https\nhost=git.anguskit.com\n\n" | git credential reject&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;再执行一次 &lt;code&gt;git pull&lt;/code&gt;，在提示里填登录名和新令牌。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;怀疑令牌泄露时，在列表里点那一把的 &lt;strong&gt;吊销&lt;/strong&gt;，它会立即失效。&lt;strong&gt;吊销全部&lt;/strong&gt; 会让你所有未过期的令牌同时失效，用到这些令牌的脚本和 CI 也会一起停止工作，点之前先想清楚有哪些地方在用。&lt;/p&gt;

&lt;h2&gt;
  
  
  7. 克隆失败时
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;你看到的&lt;/th&gt;
&lt;th&gt;原因&lt;/th&gt;
&lt;th&gt;怎么处理&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fatal: Authentication failed for 'https://git.anguskit.com/anguskit/AngusKit.git/'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;令牌已过期或已吊销；令牌复制时少了字符；用户名填的不是令牌主人的登录名；令牌的 &lt;strong&gt;指定仓库&lt;/strong&gt; 里没有这个仓库；或者 Git 缓存的还是旧令牌&lt;/td&gt;
&lt;td&gt;到 &lt;strong&gt;访问令牌&lt;/strong&gt; 列表看这把令牌的状态和仓库作用域。用户名填你自己的登录名。缓存过旧令牌时，按第 6 节执行 &lt;code&gt;git credential reject&lt;/code&gt; 后重试&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fatal: could not read Username for 'https://git.anguskit.com': terminal prompts disabled&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;在没有终端交互的环境里（CI、脚本）克隆，Git 想问用户名却问不了&lt;/td&gt;
&lt;td&gt;按第 5 节用 &lt;code&gt;http.extraHeader&lt;/code&gt; 传令牌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fatal: repository 'https://git.anguskit.com/anguskit/AngusKit.git/' not found&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;地址里的所有者或仓库名写错了&lt;/td&gt;
&lt;td&gt;打开仓库代码页，点绿色的 &lt;strong&gt;代码&lt;/strong&gt; 按钮，从 &lt;strong&gt;Clone&lt;/strong&gt; 面板重新复制地址&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;克隆正常，推送被拒绝&lt;/td&gt;
&lt;td&gt;令牌的权限范围只有 &lt;code&gt;git:read&lt;/code&gt;，或者仓库权限只有 &lt;strong&gt;READ&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;推送需要 &lt;code&gt;git:write&lt;/code&gt; 加 &lt;strong&gt;WRITE&lt;/strong&gt;。令牌的 &lt;strong&gt;编辑&lt;/strong&gt; 只能改名称和描述，不能改权限，所以要另外生成一把用于推送&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  常见问题
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;改了登录密码，令牌要跟着换吗？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;不用。令牌和登录密码互相独立。改密码之后，令牌在有效期内照样能用。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;访问令牌能用来调用控制台的接口吗？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;不能。访问令牌只用于 Git 的 HTTPS 克隆、拉取和推送。控制台和它的接口仍然通过登录会话访问。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;同一台电脑要用两个账号克隆不同的仓库，怎么办？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;让凭据按完整路径区分。执行 &lt;code&gt;git config --global credential.useHttpPath true&lt;/code&gt; 之后，Git 会按每个仓库的完整地址分别保存凭据，两个账号的令牌不会互相覆盖。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;不想管令牌，有别的办法吗？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;可以改用 SSH。在 &lt;strong&gt;我的 → SSH密钥&lt;/strong&gt; 登记一次公钥，之后用 SSH 地址克隆和推送都不再需要密码或令牌。做法见文末参考里的第一次推送一文。&lt;/p&gt;

&lt;h2&gt;
  
  
  小结
&lt;/h2&gt;

&lt;p&gt;HTTPS 克隆私有仓库时，用户名填登录名，密码填访问令牌。只读的场景，生成一把只勾 &lt;code&gt;git:read&lt;/code&gt;、只选目标仓库的令牌，生成后马上存好。日常使用交给系统凭据管理器记住；CI 里用 &lt;code&gt;http.extraHeader&lt;/code&gt; 传令牌，不拼进地址。令牌过期就重新生成一把，再用 &lt;code&gt;git credential reject&lt;/code&gt; 清掉旧缓存；怀疑泄露就单独吊销那一把。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;结论：&lt;/strong&gt; HTTPS 克隆私有仓库时，用户名填 AngusGit 登录名，密码填 &lt;strong&gt;我的 → 访问令牌&lt;/strong&gt; 生成的 &lt;code&gt;git:read&lt;/code&gt; 令牌；CI 中使用 &lt;code&gt;http.extraHeader&lt;/code&gt; 传参，避免把令牌写进克隆 URL。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  参考文章
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/docs/git/manual/03-org-permissions" rel="noopener noreferrer"&gt;AngusGit 手册：组织与权限（SSH 密钥、访问令牌）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/docs/git/manual/04-repository" rel="noopener noreferrer"&gt;AngusGit 手册：仓库（克隆地址）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/self-hosted-git-first-push-ssh-https" rel="noopener noreferrer"&gt;自建 Git 之后怎么推代码：配 SSH 或 HTTPS，完成第一次 push&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>angusgit</category>
      <category>https</category>
    </item>
    <item>
      <title>合并前怎么拦住仓库里的漏洞</title>
      <dc:creator>Wen Ping</dc:creator>
      <pubDate>Sat, 31 Oct 2026 04:05:10 +0000</pubDate>
      <link>https://dev.to/wenping/he-bing-qian-zen-yao-lan-zhu-cang-ku-li-de-lou-dong-5867</link>
      <guid>https://dev.to/wenping/he-bing-qian-zen-yao-lan-zhu-cang-ku-li-de-lou-dong-5867</guid>
      <description>&lt;p&gt;这篇讲安全扫描已经发现高危漏洞、拉取请求却仍能合并时，怎么在合并前拦住。看完你可以打开 &lt;strong&gt;未通过时拒绝合并&lt;/strong&gt;，设好 &lt;strong&gt;漏洞门禁阈值&lt;/strong&gt;（达到这一级以及更严重的问题就拦），再用分支保护让管理员也绕不过去。&lt;/p&gt;

&lt;p&gt;先登录 AngusGit，打开要设门禁的仓库。安全扫描在团队版、企业版和云服务中提供；社区版没有 &lt;strong&gt;安全&lt;/strong&gt; 菜单。门禁只认已经扫描过的提交，所以先做下面两步：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;系统管理员打开 &lt;strong&gt;管理 → 安全设置&lt;/strong&gt;。页面标题是 &lt;strong&gt;AngusSecurity 集成&lt;/strong&gt;。&lt;strong&gt;配置启用&lt;/strong&gt; 要是 &lt;strong&gt;已启用&lt;/strong&gt;，&lt;strong&gt;平台可达&lt;/strong&gt; 要是 &lt;strong&gt;正常&lt;/strong&gt;。还没开通时点 &lt;strong&gt;开通集成&lt;/strong&gt;。页面要求先设置 &lt;code&gt;angus.git.external-security.enabled=true&lt;/code&gt; 时，把环境变量 &lt;code&gt;ANGUS_GIT_EXTERNAL_SECURITY_ENABLED&lt;/code&gt; 设为 &lt;code&gt;true&lt;/code&gt;，改完重启 AngusGit。&lt;/li&gt;
&lt;li&gt;仓库管理员进入该仓库 &lt;strong&gt;设置 → 安全&lt;/strong&gt;，打开 &lt;strong&gt;本仓自动安全扫描&lt;/strong&gt;，并至少打开 &lt;strong&gt;代码安全&lt;/strong&gt;、&lt;strong&gt;依赖/制品漏洞&lt;/strong&gt;、&lt;strong&gt;密钥检查&lt;/strong&gt; 中的一种，然后保存。保存成功的提示是「安全设置已保存」。页面说明是「默认关闭。关闭后推送不再自动检查」。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. 打开门禁
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;打开仓库的安全设置&lt;/strong&gt;
进入仓库 &lt;strong&gt;设置 → 安全&lt;/strong&gt;，找到 &lt;strong&gt;合并门禁&lt;/strong&gt; 一节。页面提示「只有仓库管理员能修改。」&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;打开「未通过时拒绝合并」&lt;/strong&gt;
说明是「默认关闭。打开后按 PR 最新提交的检查结论判定：还没检查完时等待，漏洞门禁或许可证门禁未通过时拒绝合并。直接合并分支同样受限。代码质量不参与。」&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;选漏洞门禁阈值&lt;/strong&gt;
留空表示沿用租户默认，显示为「沿用租户（…）」；租户也没有设置时，默认是 &lt;strong&gt;高危&lt;/strong&gt;。阈值怎么选见第 2 节。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;确认自动扫描是打开的&lt;/strong&gt;
门禁只认已经扫描过的提交。&lt;strong&gt;本仓自动安全扫描&lt;/strong&gt; 关闭时，页面会提示「自动检查已关闭：新提交不会自动检查，PR 会一直等待。打开自动检查，或在代码页对分支手动检查。」看到这条提示，先把自动扫描打开。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  2. 阈值选哪一级
&lt;/h2&gt;

&lt;p&gt;阈值的意思是「达到这个级别就拦」。选某一级，就是这一级以及比它更严重的问题，只要有一个就拒绝合并。&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;阈值&lt;/th&gt;
&lt;th&gt;什么情况会拦住合并&lt;/th&gt;
&lt;th&gt;适合的情况&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;严重&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;有严重问题&lt;/td&gt;
&lt;td&gt;刚开始推行，存量问题很多，先拦最危险的&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;高危&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;有严重或高危问题&lt;/td&gt;
&lt;td&gt;大多数团队的默认选择&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;中危&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;有严重、高危或中危问题&lt;/td&gt;
&lt;td&gt;安全要求高、存量问题已经清理干净的仓库&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;低危&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;有任何级别的问题&lt;/td&gt;
&lt;td&gt;很少使用，容易频繁拦住正常合并&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;这里的「问题」包括代码安全、依赖/制品漏洞和密钥检查发现的问题，按严重级别计数。许可证问题单独由许可证门禁判断，代码质量问题不参与。&lt;/p&gt;

&lt;p&gt;打开 &lt;strong&gt;未通过时拒绝合并&lt;/strong&gt; 后，漏洞门禁总会参与判断，阈值没有「不启用」这一项，最宽松的设置是 &lt;strong&gt;严重&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;建议从 &lt;strong&gt;高危&lt;/strong&gt; 开始。门禁刚打开时，存量的高危依赖漏洞可能会挡住很多拉取请求，先集中修一轮，再考虑收紧到 &lt;strong&gt;中危&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;修改阈值之后，已经检查过的提交不会按新阈值重新判定。页面提示「改了阈值后，需要重新检查才按新阈值判定。」要马上生效，对相关分支重新扫描一次。&lt;/p&gt;

&lt;h2&gt;
  
  
  3. 门禁到底拦住了什么
&lt;/h2&gt;

&lt;p&gt;打开 &lt;strong&gt;未通过时拒绝合并&lt;/strong&gt; 之后，拉取请求合并时会检查 &lt;strong&gt;源分支最新那一个提交&lt;/strong&gt; 的安全检查结论：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;最新提交的检查结论&lt;/th&gt;
&lt;th&gt;能不能合并&lt;/th&gt;
&lt;th&gt;页面提示&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;还没检查，或正在检查&lt;/td&gt;
&lt;td&gt;不能&lt;/td&gt;
&lt;td&gt;等待对提交 … 的安全检查&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;检查失败，或结果不完整&lt;/td&gt;
&lt;td&gt;不能&lt;/td&gt;
&lt;td&gt;请重新检查&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;漏洞数量达到或超过阈值&lt;/td&gt;
&lt;td&gt;不能&lt;/td&gt;
&lt;td&gt;&lt;code&gt;漏洞门禁未通过：严重 …、高危 …、中危 …、低危 …&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;漏洞数量未达到阈值&lt;/td&gt;
&lt;td&gt;能&lt;/td&gt;
&lt;td&gt;&lt;code&gt;提交 … 的漏洞门禁已通过&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;有三点要特别说明：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;只看最新提交。&lt;/strong&gt; 往源分支推了新提交，结论会回到等待，按新提交重新判定。不会沿用旧提交的通过结论，也不会因为旧提交失败而一直挡住修好的代码。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;不依赖分支保护。&lt;/strong&gt; 即使目标分支没有任何保护规则，这道门禁也生效，通过接口合并同样会被拒绝。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;管理员例外。&lt;/strong&gt; 目标分支有保护规则、规则没有打开 &lt;strong&gt;对管理员同样生效&lt;/strong&gt;、并且操作人是仓库管理员时，可以绕过这道门禁。目标分支没有保护规则时，管理员也会被拦住。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;所以，要做到谁都绕不过去，还要在 &lt;strong&gt;设置 → 分支保护&lt;/strong&gt; 里给 &lt;code&gt;main&lt;/code&gt; 打开 &lt;strong&gt;对管理员同样生效&lt;/strong&gt;。分支保护里还有一个 &lt;strong&gt;要求安全检查通过&lt;/strong&gt; 开关，说明是「按 PR 最新提交的安全检查结论判定，没检查完时等待（默认关闭）」，作用和 &lt;strong&gt;未通过时拒绝合并&lt;/strong&gt; 相同，只在这条保护规则覆盖的分支上生效。已经打开 &lt;strong&gt;未通过时拒绝合并&lt;/strong&gt; 的仓库，不需要再打开它。&lt;/p&gt;

&lt;h2&gt;
  
  
  4. 被拦住之后怎么修
&lt;/h2&gt;

&lt;p&gt;开发者看到拉取请求上的 &lt;code&gt;AngusSecurity&lt;/code&gt; 失败，按下面的步骤处理：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;看是哪些问题&lt;/strong&gt;
在拉取请求的 &lt;strong&gt;状态检查&lt;/strong&gt; 里点 &lt;strong&gt;打开仓安全详情&lt;/strong&gt;，进入仓库安全详情页。按严重级别筛选，先看严重和高危。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;按类别修&lt;/strong&gt;
依赖漏洞看 &lt;strong&gt;修复版本&lt;/strong&gt;，把依赖升级到这个版本或更高；代码安全问题看 &lt;strong&gt;修复建议&lt;/strong&gt; 改代码；密钥问题把密钥从代码里移除，并且一定要去对应的系统里作废这把密钥，因为它已经进入了提交历史。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;推送修复&lt;/strong&gt;
在源分支上提交修复并 &lt;code&gt;git push&lt;/code&gt;。推送会触发新的扫描，拉取请求上的安全检查先回到 &lt;strong&gt;进行中&lt;/strong&gt;，扫完更新。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;确认通过后合并&lt;/strong&gt;
状态变成 &lt;strong&gt;成功&lt;/strong&gt;，说明文字是「提交 … 的漏洞门禁已通过」，就可以合并了。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  5. 常见的卡住情况
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;你看到的&lt;/th&gt;
&lt;th&gt;原因&lt;/th&gt;
&lt;th&gt;怎么处理&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;一直停在「等待对提交 … 的安全检查」，合并不了&lt;/td&gt;
&lt;td&gt;这个提交没有被扫描。常见原因是自动扫描关了，或者提交是在网页上编辑的&lt;/td&gt;
&lt;td&gt;打开自动扫描后重新推送；或者在仓库安全详情页 &lt;strong&gt;立即扫描&lt;/strong&gt; 源分支&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;「提交 … 的安全检查失败，请重新检查」&lt;/td&gt;
&lt;td&gt;扫描本身出错&lt;/td&gt;
&lt;td&gt;重新扫描；仍然失败时到 AngusSecurity 查看扫描任务的错误&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;「提交 … 的检查结果不完整，请重新检查」&lt;/td&gt;
&lt;td&gt;扫描完成但结果不可信，例如部分检查没有跑成功&lt;/td&gt;
&lt;td&gt;重新扫描&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;改了阈值，拉取请求的结论没变&lt;/td&gt;
&lt;td&gt;已检查的提交不会按新阈值重新判定&lt;/td&gt;
&lt;td&gt;对源分支重新扫描一次&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;存量漏洞太多，几乎所有拉取请求都被拦&lt;/td&gt;
&lt;td&gt;阈值相对当前代码太严&lt;/td&gt;
&lt;td&gt;先把阈值放到 &lt;strong&gt;严重&lt;/strong&gt;，集中清理存量问题后再收紧&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;管理员能合并，其他人不能&lt;/td&gt;
&lt;td&gt;分支保护没有打开 &lt;strong&gt;对管理员同样生效&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;按团队规定决定是否打开&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  常见问题
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;只拦 &lt;code&gt;main&lt;/code&gt;，其他分支不拦，可以吗？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;未通过时拒绝合并&lt;/strong&gt; 对这个仓库的所有拉取请求生效。只想拦特定分支时，不打开它，改为在那个分支的保护规则里打开 &lt;strong&gt;要求安全检查通过&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;发现的漏洞确认不影响我们，能放行这一次吗？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;门禁按数量判断，没有针对单个问题的豁免开关。可以在安全详情里把这个问题的 &lt;strong&gt;处置状态&lt;/strong&gt; 改为 &lt;strong&gt;已知悉&lt;/strong&gt; 或 &lt;strong&gt;误报&lt;/strong&gt;，记录判断结论；需要放行时，由仓库管理员按分支保护的规则合并，或者临时调整阈值后重新扫描。这类操作建议在团队里约定好由谁决定。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;门禁和流水线的状态检查有冲突吗？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;没有。两者都显示在拉取请求的 &lt;strong&gt;状态检查&lt;/strong&gt; 里，合并时分别判断，都满足才能合并。&lt;/p&gt;

&lt;h2&gt;
  
  
  小结
&lt;/h2&gt;

&lt;p&gt;让漏洞挡住合并，在仓库 &lt;strong&gt;设置 → 安全&lt;/strong&gt; 打开 &lt;strong&gt;未通过时拒绝合并&lt;/strong&gt;，&lt;strong&gt;漏洞门禁阈值&lt;/strong&gt; 从 &lt;strong&gt;高危&lt;/strong&gt; 开始。门禁按源分支最新提交的检查结论判断：没检查完就等待，达到阈值就拒绝，修好推送后重新判定。它不依赖分支保护，接口合并也受限；要让管理员也受限，在分支保护里打开 &lt;strong&gt;对管理员同样生效&lt;/strong&gt;。前提是 &lt;strong&gt;本仓自动安全扫描&lt;/strong&gt; 已打开，否则拉取请求会一直等待。安全扫描在团队版、企业版和云服务中提供；社区版没有 &lt;strong&gt;安全&lt;/strong&gt; 菜单，需要时请使用团队版或企业版。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;结论：&lt;/strong&gt; 在仓库 &lt;strong&gt;设置 → 安全&lt;/strong&gt; 打开 &lt;strong&gt;未通过时拒绝合并&lt;/strong&gt; 并设好 &lt;strong&gt;漏洞门禁阈值&lt;/strong&gt;（如 &lt;strong&gt;高危&lt;/strong&gt;），拉取请求源分支最新提交只要检出达到该级别的问题就会被拒绝合并。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  参考文章
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/docs/git/manual/19-security" rel="noopener noreferrer"&gt;AngusGit 手册：安全&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/docs/git/manual/05-branch-merge" rel="noopener noreferrer"&gt;AngusGit 手册：分支与合并&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/auto-security-scan-after-push" rel="noopener noreferrer"&gt;推送代码之后：怎么自动跑代码安全扫描&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/view-security-scan-results-in-pull-request" rel="noopener noreferrer"&gt;合并请求里怎么看代码安全扫描结果&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/block-noncompliant-open-source-license-before-merge" rel="noopener noreferrer"&gt;合并前怎么拦住不合规的开源许可证&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/protect-git-main-branch-pull-request" rel="noopener noreferrer"&gt;怎么保护 Git 主分支：先开分支保护，再用合并请求合代码&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>angusgit</category>
      <category>security</category>
    </item>
    <item>
      <title>合并前怎么拦住不合规的开源许可证</title>
      <dc:creator>Wen Ping</dc:creator>
      <pubDate>Sat, 31 Oct 2026 04:04:33 +0000</pubDate>
      <link>https://dev.to/wenping/he-bing-qian-zen-yao-lan-zhu-bu-he-gui-de-kai-yuan-xu-ke-zheng-48jp</link>
      <guid>https://dev.to/wenping/he-bing-qian-zen-yao-lan-zhu-bu-he-gui-de-kai-yuan-xu-ke-zheng-48jp</guid>
      <description>&lt;p&gt;这篇讲依赖用了不合规的开源许可证时，怎么在合并前拦住。看完你可以在仓库 &lt;strong&gt;设置 → 安全&lt;/strong&gt; 里打开许可证门禁和 &lt;strong&gt;未通过时拒绝合并&lt;/strong&gt;，并到 AngusSecurity 策略里勾上 &lt;strong&gt;许可证合规&lt;/strong&gt;，看允许名单和禁止名单在哪填。&lt;/p&gt;

&lt;p&gt;仓库 &lt;code&gt;xlliu/e2e-license-gate&lt;/code&gt; 的安全概览现在写着「许可证门禁未通过，拒绝合并」。许可合规页有两条开放发现，高危的 &lt;code&gt;GPL-3.0-only&lt;/code&gt; 属于强互惠（分发软件时，要把相关代码也按同样的许可证公开），低危的 &lt;code&gt;MIT&lt;/code&gt; 属于宽松（基本只要求保留版权声明）。&lt;/p&gt;

&lt;p&gt;先登录 AngusGit，再打开仓库 &lt;code&gt;xlliu/e2e-license-gate&lt;/code&gt;；你自己的仓库也从登录后的仓库列表进入。安全扫描在团队版、企业版和云服务中提供；社区版没有 &lt;strong&gt;安全&lt;/strong&gt; 菜单。开始前先让扫描跑起来：系统管理员打开 &lt;strong&gt;管理 → 安全设置&lt;/strong&gt;，页面标题是 &lt;strong&gt;AngusSecurity 集成&lt;/strong&gt;，&lt;strong&gt;配置启用&lt;/strong&gt; 为 &lt;strong&gt;已启用&lt;/strong&gt;、&lt;strong&gt;平台可达&lt;/strong&gt; 为 &lt;strong&gt;正常&lt;/strong&gt;；还没开通时点 &lt;strong&gt;开通集成&lt;/strong&gt;。页面要求 &lt;code&gt;angus.git.external-security.enabled=true&lt;/code&gt; 时，把环境变量 &lt;code&gt;ANGUS_GIT_EXTERNAL_SECURITY_ENABLED&lt;/code&gt; 设为 &lt;code&gt;true&lt;/code&gt; 并重启 AngusGit。仓库管理员再到该仓库 &lt;strong&gt;设置 → 安全&lt;/strong&gt;，打开 &lt;strong&gt;本仓自动安全扫描&lt;/strong&gt; 和 &lt;strong&gt;许可证合规&lt;/strong&gt;，保存后提示「安全设置已保存」。对照本文时按仓库里已经保存的设置来看即可，不必再点 &lt;strong&gt;保存&lt;/strong&gt;、&lt;strong&gt;立即扫描&lt;/strong&gt; 或 &lt;strong&gt;立即执行&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq8t75ksii55pjj13zltp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq8t75ksii55pjj13zltp.png" alt="仓库设置管拦截，策略名单要先勾许可证合规" width="799" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. 先弄清「不兼容」是怎么判断的
&lt;/h2&gt;

&lt;p&gt;AngusGit 仓库设置里的许可证门禁有三项：&lt;strong&gt;不兼容时拒绝&lt;/strong&gt; 和 &lt;strong&gt;无法判定也拒绝&lt;/strong&gt; 会拦住合并，选 &lt;strong&gt;不启用&lt;/strong&gt; 则不按许可证拦截。&lt;/p&gt;

&lt;p&gt;勾上 AngusSecurity 策略里的 &lt;strong&gt;许可证合规&lt;/strong&gt; 之后，允许名单、禁止名单和分发场景才会出现。判断顺序是：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;禁止名单&lt;/strong&gt;：依赖的许可证在禁止名单里，判为不合规。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;允许名单&lt;/strong&gt;：允许名单不为空时，名单以外的许可证都判为不合规。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;分发场景&lt;/strong&gt;：前两项都没有命中时，按软件怎么交付来判断。表单说明是：「GPL 在仅内部使用和 SaaS 下默认不算不兼容，分发二进制或容器镜像时算。AGPL 除了仅内部使用都算不兼容。」&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;分发场景没填时，按 &lt;strong&gt;分发二进制&lt;/strong&gt; 判断：强互惠（GPL）在这种默认下算不兼容，宽松许可证（例如 MIT）算兼容。&lt;strong&gt;无法判定&lt;/strong&gt; 指扫描没认出许可证；选 &lt;strong&gt;无法判定也拒绝&lt;/strong&gt; 时，这类依赖也会挡住合并。&lt;/p&gt;

&lt;h2&gt;
  
  
  2. 策略里的名单要先打开许可证合规
&lt;/h2&gt;

&lt;p&gt;AngusGit 投递的扫描默认走 AngusSecurity 策略 &lt;code&gt;[System] GIT Inbound&lt;/code&gt;。策略详情编号是 &lt;code&gt;378599664200187905&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyf8ko2o8gbyim678t15e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyf8ko2o8gbyim678t15e.png" alt="策略 [System] GIT Inbound：门禁阈值高危，能力是代码安全、密钥检查、依赖/制品漏洞" width="798" height="214"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;详情页上能看到的是：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;标题 &lt;code&gt;[System] GIT Inbound&lt;/code&gt;，徽章 &lt;strong&gt;已启用&lt;/strong&gt;，说明 &lt;code&gt;Auto-created for GIT inbound scans&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;门禁阈值&lt;/strong&gt; 是 &lt;strong&gt;高危&lt;/strong&gt;，说明「出现高危及以上的发现即判定未通过」。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;能力集&lt;/strong&gt; 是 &lt;strong&gt;代码安全&lt;/strong&gt;、&lt;strong&gt;密钥检查&lt;/strong&gt;、&lt;strong&gt;依赖/制品漏洞&lt;/strong&gt;。没有 &lt;strong&gt;许可证合规&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;右侧概要：默认 &lt;strong&gt;否&lt;/strong&gt;，创建时间 2026/08/03 12:02:29。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;编辑页里 &lt;strong&gt;许可证合规&lt;/strong&gt; 是一个复选框，说明是「依赖许可证是否合规」。没勾上时，允许名单、禁止名单、分发场景都不显示。这条策略目前没勾 &lt;strong&gt;许可证合规&lt;/strong&gt;；勾上之后才会出现名单和分发场景，表单里的标识只是占位示例，不是这条策略已经保存的内容。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;打开策略&lt;/strong&gt;
在 AngusSecurity 侧栏进入 &lt;strong&gt;策略&lt;/strong&gt;。列表说明是「配置检查门禁策略和默认阈值。」打开 &lt;code&gt;[System] GIT Inbound&lt;/code&gt;，点 &lt;strong&gt;编辑策略&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;勾上许可证合规&lt;/strong&gt;
在 &lt;strong&gt;检测能力&lt;/strong&gt; 里勾上 &lt;strong&gt;许可证合规&lt;/strong&gt;。保存前先确认公司允许把许可证结论写进这条共用策略。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;选分发场景&lt;/strong&gt;
勾上之后才出现 &lt;strong&gt;分发场景&lt;/strong&gt;：&lt;strong&gt;仅内部使用&lt;/strong&gt;、&lt;strong&gt;SaaS&lt;/strong&gt;、&lt;strong&gt;分发二进制&lt;/strong&gt;、&lt;strong&gt;分发容器镜像&lt;/strong&gt;。产品以安装包或容器镜像交付时，选后两项之一。不选则按分发二进制判断。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;填名单（按需）&lt;/strong&gt;
&lt;strong&gt;允许名单&lt;/strong&gt; 和 &lt;strong&gt;禁止名单&lt;/strong&gt; 填 SPDX 标识，逗号分隔。表单占位分别是 &lt;code&gt;MIT, Apache-2.0, BSD-3-Clause&lt;/code&gt; 和 &lt;code&gt;AGPL-3.0, SSPL-1.0&lt;/code&gt;。同一个许可证不能同时出现在两个名单里。允许名单一旦不为空，名单以外的许可证都判为不合规。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;保存&lt;/strong&gt;
保存后对之后的检查生效。已经写在拉取请求上的状态检查不会自动重算。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;内置目录没有收录的许可证，可以在 &lt;strong&gt;策略&lt;/strong&gt; 页的 &lt;strong&gt;自定义许可证&lt;/strong&gt; 页签里添加，标识用 SPDX 标识或 &lt;code&gt;LicenseRef-*&lt;/code&gt;。内置目录已经能认出 GPL 时，不必先去这个页签。&lt;/p&gt;

&lt;h2&gt;
  
  
  3. 在 AngusGit 里打开检查和门禁
&lt;/h2&gt;

&lt;p&gt;仓库自己的开关在 &lt;strong&gt;设置 → 安全&lt;/strong&gt;，地址是 &lt;code&gt;?tab=settings&amp;amp;section=security&lt;/code&gt;。&lt;code&gt;xlliu/e2e-license-gate&lt;/code&gt; 现在的表单是：&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frtchhqc7m90rdgzvn28v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frtchhqc7m90rdgzvn28v.png" alt="仓库安全设置：许可证合规开着，许可证门禁是不兼容时拒绝，未通过时拒绝合并开着" width="799" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;截图里能看到的开关：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;代码安全&lt;/strong&gt;、&lt;strong&gt;密钥检查&lt;/strong&gt;、&lt;strong&gt;许可证合规&lt;/strong&gt; 开着。&lt;strong&gt;依赖/制品漏洞&lt;/strong&gt;、&lt;strong&gt;代码质量&lt;/strong&gt; 关着。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;未通过时拒绝合并&lt;/strong&gt; 开着。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;漏洞门禁阈值&lt;/strong&gt; 是 &lt;strong&gt;高危&lt;/strong&gt;。&lt;strong&gt;许可证门禁&lt;/strong&gt; 是 &lt;strong&gt;不兼容时拒绝&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;详情页顶上的能力点只点亮了 &lt;strong&gt;密钥检查&lt;/strong&gt; 和 &lt;strong&gt;许可证合规&lt;/strong&gt;，设置页的 &lt;strong&gt;代码安全&lt;/strong&gt; 开关却是开的：两处读的不是同一组字段。打开页面对照即可，不必为了让两处一致而立刻保存。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;许可证合规&lt;/strong&gt; 的说明是「识别依赖许可证，并按门禁决定是否拒绝合并。」如果设了许可证门禁、却没打开这项能力，页面会提示：「许可证门禁已启用，但没有打开「许可证合规」检查。打开拒绝合并后，PR 会因为缺少许可证结论而无法合并。」&lt;/p&gt;

&lt;p&gt;只设许可证门禁、不打开 &lt;strong&gt;未通过时拒绝合并&lt;/strong&gt;，结果会记下来，但不会拦住合并。许可证门禁选 &lt;strong&gt;不启用&lt;/strong&gt; 时，策略里的名单只用于展示风险。&lt;/p&gt;

&lt;h2&gt;
  
  
  4. 这个仓库现在的结果
&lt;/h2&gt;

&lt;p&gt;安全详情在 &lt;code&gt;https://git.anguskit.com/security/repos/xlliu/e2e-license-gate&lt;/code&gt;。概览分数 95，严重、高危、中危、低危、信息、密钥、质量都是 0，&lt;strong&gt;许可风险&lt;/strong&gt; 是 1。默认分支显示 &lt;code&gt;main@d3ebc5a&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F23wryrlp15e4sr84yxp0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F23wryrlp15e4sr84yxp0.png" alt="概览：门禁建议未通过，许可证门禁未通过，拒绝合并" width="799" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;红字是「门禁建议: 未通过」，下面一行是「许可证门禁未通过，拒绝合并」。已有开放发现时，先看这些发现即可，不必马上点 &lt;strong&gt;立即扫描&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;许可合规&lt;/strong&gt; 页签、处置状态 &lt;strong&gt;开放&lt;/strong&gt;，共 2 条：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;级别&lt;/th&gt;
&lt;th&gt;标题&lt;/th&gt;
&lt;th&gt;类别&lt;/th&gt;
&lt;th&gt;位置&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;高危&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GPL-3.0-only in e2e-with-gpl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;强互惠&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;package.json&lt;/code&gt;，组件 &lt;code&gt;e2e-with-gpl@1.0.0&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;低危&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MIT in e2e-with-gpl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;宽松&lt;/td&gt;
&lt;td&gt;&lt;code&gt;LICENSE&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;高危那条的分支是 &lt;code&gt;e2e-git07 @591d671&lt;/code&gt;，处置状态 &lt;strong&gt;开放&lt;/strong&gt;，说明是 &lt;code&gt;Declared by project manifest&lt;/code&gt;。抽屉里可以点 &lt;strong&gt;在代码中查看&lt;/strong&gt; 或 &lt;strong&gt;在安全台打开&lt;/strong&gt;；只是查看结论时，保持处置状态为 &lt;strong&gt;开放&lt;/strong&gt; 即可。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fohzry44i095kvhndyyfe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fohzry44i095kvhndyyfe.png" alt="GPL-3.0-only 的发现抽屉：强互惠，组件 e2e-with-gpl@1.0.0" width="799" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;页面上的许可证类别还有公有领域、弱互惠、网络互惠、专有、未识别、禁止使用。这条 GPL 显示的是 &lt;strong&gt;强互惠&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;拉取请求上的状态检查认的是该提交最新一次扫描，文案有三种：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;许可证没过：「许可证门禁未通过」。&lt;/li&gt;
&lt;li&gt;漏洞和许可证都开着并且都过了：「提交 {提交号前 8 位} 的漏洞门禁与许可证门禁均已通过」。这个仓库已合并的 #1、#2 就是这句。&lt;/li&gt;
&lt;li&gt;只记了漏洞门禁：「提交 {提交号前 8 位} 的漏洞门禁已通过」。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;拉取请求 #4 &lt;code&gt;e2e X-03 GPL merge gate&lt;/code&gt;（&lt;code&gt;e2e-x03&lt;/code&gt; → &lt;code&gt;main&lt;/code&gt;，已合并）现在是第三种。链接是 &lt;code&gt;https://git.anguskit.com/pulls/xlliu/e2e-license-gate/4&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo77vlpanxoznqpvq6jph.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo77vlpanxoznqpvq6jph.png" alt="拉取请求 #4 的状态检查是成功：提交 d3ebc5ab 的漏洞门禁已通过" width="799" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;同一提交 &lt;code&gt;d3ebc5a&lt;/code&gt; 上，更早的推送扫描里许可证门禁是失败；后来一次手动扫描把许可证标成跳过，门禁按最新一次记成通过。所以 #4 这行没有写成「许可证门禁未通过」。概览仍写着拒绝合并，是因为开放的 GPL 发现还在，仓库设置里的许可证门禁也还开着。已经合并的检查行不会因为后来改设置而重算。要按新阈值重算，需要对分支重新扫描。&lt;/p&gt;

&lt;p&gt;被拦住之后，常见处理是换成许可证合适的依赖。法务确认可以用的，再由管策略的人把它放进允许名单，或改分发场景，然后推送新提交。改名单只影响之后的检查。&lt;/p&gt;

&lt;h2&gt;
  
  
  5. 最容易配错的地方
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;你看到的&lt;/th&gt;
&lt;th&gt;原因&lt;/th&gt;
&lt;th&gt;怎么处理&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;「许可证门禁已启用，但这次检查没有包含许可证合规」&lt;/td&gt;
&lt;td&gt;打开了许可证门禁，但没有打开 &lt;strong&gt;许可证合规&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;在 &lt;strong&gt;设置 → 安全&lt;/strong&gt; 打开 &lt;strong&gt;许可证合规&lt;/strong&gt;，再推送一次&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;几乎所有拉取请求都提示「许可证门禁未通过」&lt;/td&gt;
&lt;td&gt;允许名单不为空，但漏写了常用许可证&lt;/td&gt;
&lt;td&gt;补全允许名单，或者改用禁止名单&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;引入了 GPL 依赖却没有被拦&lt;/td&gt;
&lt;td&gt;分发场景是 &lt;strong&gt;仅内部使用&lt;/strong&gt; 或 &lt;strong&gt;SaaS&lt;/strong&gt;。这两种场景下，强互惠默认不算不兼容&lt;/td&gt;
&lt;td&gt;按实际交付方式选择分发场景，或者把该 GPL 标识加进禁止名单&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;策略详情里看不到允许名单&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;许可证合规&lt;/strong&gt; 没勾上，名单不会出现&lt;/td&gt;
&lt;td&gt;编辑策略并勾上 &lt;strong&gt;许可证合规&lt;/strong&gt;。勾上之前先确认这条策略是仓库共用的&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;改了名单，已合并的拉取请求结论没变&lt;/td&gt;
&lt;td&gt;新规则只对之后的检查生效，状态检查认最新一次扫描&lt;/td&gt;
&lt;td&gt;推送新提交，或对源分支重新扫描&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;概览写拒绝合并，拉取请求却是成功&lt;/td&gt;
&lt;td&gt;开放发现还在，但该提交最新一次扫描把许可证标成了跳过&lt;/td&gt;
&lt;td&gt;看扫描历史里同一次提交的推送扫描和手动扫描，不要只看合并请求上的一行&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  常见问题
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;许可证门禁和漏洞门禁要分开配吗？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;两者分开判断，都在 &lt;strong&gt;合并门禁&lt;/strong&gt; 里，由 &lt;strong&gt;未通过时拒绝合并&lt;/strong&gt; 决定是否拦截。打开拒绝合并之后，漏洞门禁一定会参与，阈值没有「不启用」。这个仓库的漏洞阈值是 &lt;strong&gt;高危&lt;/strong&gt;，当前严重到低危的数量都是 0。只想主要管许可证时，把漏洞阈值调到 &lt;strong&gt;严重&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;这和代码溯源有什么区别？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;许可证检查看的是依赖声明了什么许可证。代码溯源看的是这段代码和别的仓库像不像。两件事分开做。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;每个仓库的名单能不能不一样？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;允许名单、禁止名单、分发场景在 AngusSecurity 的策略上。AngusGit 投递的扫描共用 &lt;code&gt;[System] GIT Inbound&lt;/code&gt; 时，名单对走这条策略的仓库相同。每个仓库可以各自打开许可证合规、选择 &lt;strong&gt;不兼容时拒绝&lt;/strong&gt; 或 &lt;strong&gt;无法判定也拒绝&lt;/strong&gt;，并决定是否拒绝合并。这个仓库就是自己开了许可证门禁；系统策略的能力集里目前没有许可证合规。&lt;/p&gt;

&lt;h2&gt;
  
  
  小结
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;xlliu/e2e-license-gate&lt;/code&gt; 的仓库设置已经打开 &lt;strong&gt;许可证合规&lt;/strong&gt;，许可证门禁是 &lt;strong&gt;不兼容时拒绝&lt;/strong&gt;，并且打开了 &lt;strong&gt;未通过时拒绝合并&lt;/strong&gt;。开放发现里的 &lt;code&gt;GPL-3.0-only&lt;/code&gt; 是强互惠；分发场景没填时按二进制分发，这种许可证算不兼容。概览因此写着「许可证门禁未通过，拒绝合并」。&lt;/p&gt;

&lt;p&gt;策略 &lt;code&gt;[System] GIT Inbound&lt;/code&gt; 的能力集目前是代码安全、密钥检查、依赖/制品漏洞，门禁阈值是高危。允许名单和禁止名单要等勾上 &lt;strong&gt;许可证合规&lt;/strong&gt; 才会出现。这条策略目前没勾、也没保存名单。已合并的 #4 状态检查是「提交 d3ebc5ab 的漏洞门禁已通过」，因为它认的是后来那次跳过许可证的扫描。&lt;/p&gt;

&lt;p&gt;安全扫描在团队版、企业版和云服务中提供。社区版没有这条门禁。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;结论：&lt;/strong&gt; 仓库设置里同时打开许可证合规、不兼容时拒绝和未通过时拒绝合并，不合规的许可证会记进安全概览并建议拒绝合并。允许名单和分发场景在 AngusSecurity 策略里，要先勾上许可证合规再填写，保存后只对之后的扫描生效。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  参考文章
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/docs/git/manual/19-security" rel="noopener noreferrer"&gt;AngusGit 手册：安全&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/auto-security-scan-after-push" rel="noopener noreferrer"&gt;推送代码之后：怎么自动跑代码安全扫描&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/block-vulnerabilities-before-merge" rel="noopener noreferrer"&gt;合并前怎么拦住仓库里的漏洞&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anguskit.com/zh/blog/view-security-scan-results-in-pull-request" rel="noopener noreferrer"&gt;合并请求里怎么看代码安全扫描结果&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>angusgit</category>
      <category>security</category>
    </item>
  </channel>
</rss>
