---
date: 2026-05-11
tags: [gitea, api, identity, ops-script]
---

# Gitea `/branches` API 已经把 commit author 映射到 Gitea username，别自己再 parse email

## 场景

写 ops 脚本（`scripts/ops/sweep-remote-stale.py`）要按提交人把分支分组、@ 出本人。
最朴素的做法是 parse `commit.author.email` 的 local-part 当 Gitea username
（剥 `N+` 前缀），然后调 `GET /users/<name>` 验证存在。

## 不直观的事

Gitea `/api/v1/repos/{owner}/{repo}/branches` 返回的 commit 对象里，
`commit.author` 和 `commit.committer` **已经有一个 `username` 字段**——Gitea
在接受 push 时就做了 email → user 映射，直接拿就行。映射不到的（CI bot、
NOPASSWD 自动 push 等）这个字段是空字符串 `""`。

```json
{
  "commit": {
    "author": {
      "name": "chentao.jia",
      "email": "4+chentao.jia@noreply.localhost",
      "username": "chentao.jia"        ← Gitea 已 resolve
    }
  }
}
```

```json
{
  "commit": {
    "author": {
      "name": "Ubuntu",
      "email": "ubuntu@localhost.localdomain",
      "username": ""                     ← 空 = orphan，无对应 Gitea 账号
    }
  }
}
```

## 怎么用

```python
gitea_username = ((commit.get("author") or {}).get("username") or "").strip()
username = gitea_username or email_to_gitea_username(author_email)
if username and gitea_user_exists(api, username, cache):
    assigned[username].append(branch)
else:
    orphan.append(branch)
```

优先用 Gitea 给的 username（已 resolve，零成本），空才 fallback 到 email
解析（仍然要 `GET /users/<name>` 兜底验证存在）。**这等于 orphan 判定免费**：
`username==""` 直接就是无主。

## 适用范围

任何按 commit author 分组通知的 Gitea ops 脚本：分支清理、PR 触发者识别、
代码 owner 推断、commit 审计映射。`weekly-review.py` 当前还在用 `%aE` git
log 自己 normalize，未来如果接入 @ 通知也可以走这条捷径。

## 反面例子

朴素实现走 email → local-part → `GET /users/<name>` 这一圈，对每个分支多一次
API 调用（55 分支扫描多 50 余次），还在 noreply.localhost 边缘情况下容易看走眼。
直接读 Gitea 已 resolve 的 username 一律不用调。

## 验证

```bash
curl -s -H "Authorization: token $GITEA_API_TOKEN" \
  "$BASE/api/v1/repos/$REPO/branches/$BRANCH" |
  python3 -c "import json,sys; d=json.load(sys.stdin); print(d['commit']['author'])"
```
