diffs.get_diff(view 枚举) | 后端 handler/diffs/get_diff.rs:12-19 DiffView 为 #[serde(rename_all="lowercase")] 的 Shortstat/Numstat/Files/Tree,前端 commits.ts:52 发 'numstat' 正确。大小写实证:view=Numstat → {"code":400,...,"error":"Query deserialize error: unknown variant Numstat, expected one of shortstat, numstat, files, tree"};view=bogus 同。生成类型 DiffView 与之一致 |
diffs.get_diff(use_merge_base) | 仅 Tree 分支使用(get_diff.rs:87-92);前端不传(commits.ts:52 只发 base/head/view)。实证 view=numstat&use_merge_base=true → 200(被接受并忽略),无副作用 |
diffs.diff_raw(返回形状) | 后端 handler/diffs/raw.rs:25 声明 AppResponseData<String>,实测是 JSON envelope 内 data 为字符串({"code":200,...,"data":"diff --git a/...),并非裸 text/plain。前端 unwrapApi<string>(commits.ts:70)消费方式正确 |
diffs.get_diff(numstat 行字段) | rpcgit/diff/types.rs:60-65 NumStat{additions,deletions,path,old_path};前端 model/diff.ts:184-189 NumstatRow 同名同义。实证返回 {"additions":73,"deletions":0,"old_path":null,"path":"server/…"} |
diffs.get_diff(非法 base/head) | 不可解析 revision → 404(实证 base=doesnotexist → {"code":404,"message":"not found"}),不是 400;前端按通用错误处理,无形状假设 |
trees.get_tree(query 名) | handler/trees/read.rs:20-32 允许 path/recurse/resolve/show_trees/view;前端 {path, show_trees} 均在集合内。注意后端是 recurse 不是 recursive,前端未误用 |
trees.get_tree(path 语义) | path 为空字符串时被 invoke() 的 query() 丢弃(services/api/index.ts:61-70 过滤 ''),后端 #[serde(default)] 补 "",等价。子目录实证 path=.github 正常返回 |
blobs.blob_content | service/blobs.rs:30-36 BlobContentDto{oid,data:Vec<u8>,size,is_binary,truncated} → data 序列化为数字数组。实证 data:[118,45,101,110,118,...]。前端 readme.ts:150-153 用 Array.from(blob.data) 转字节、blob.is_binary/blob.truncated/blob.size 全部按此消费。路径 3 段 {space_name, repo_name, oid} 与 /blobs/{oid}/content 一致 |
commits.list_commits(字段) | 前端发 revision/offset/limit/first_parent/path,service/commits.rs:24-39 ListCommitsCmd 逐字包含;limit:50、offset:0 边界内。实证 200 且 limit=1 生效 |
commits.list_commits(has_more) | service/commits.rs:74-95 CommitsPage{items,total,limit,offset,has_more,diff_view_style};前端 commits.ts:32-36 读 page.items/total/has_more。实证 "total":2601,"limit":1,"offset":0,"has_more":true |
blame.blame | handler/blame.rs:10-18 query=revision,path,range_start,range_end;前端只发 revision,path,合法。返回 Vec<BlameRangeDto>,实测 {"starting_line":1,"ending_line":2,"commit_sha":"ac0b1d4…","abbreviated_oid":"ac0b1d4","path":null},前端 blameLineMap(blame.ts:54-61)用的 starting_line/ending_line 存在 |
conflicts.get_conflicts | handler/conflicts/get_conflicts.rs:10-16 query=base,head,ancestor;前端 pulls/model/api.ts:156-160 发 {base,head} 合法。实证 {"mergeable":true,"conflicts":[],"fast_forwardable":true},与 MergeStatusDto 一致 |
conflicts.conflict_detail | handler/conflicts/detail.rs:10-15 query=base,head,path;前端 blame.ts:26-29 三字段齐全。返回 ConflictDetailDto{path,content:Option<String>} 与前端 {path, content?} 一致。正例未实证:demo/demorepos 无冲突对,base=HEAD~1&head=HEAD&path=README.md 返回 404 |
submodules.submodule_list | handler/submodules/list.rs:10-13 query=revision;前端 blame.ts:44-47 只发 revision。返回 Vec<SubmoduleDto{path,url,branch,commit_sha}>,实证 [{"path":"browser-extension","url":"","branch":null,"commit_sha":"385d36c…"},…],submodules-section.tsx 消费的四个字段全部存在 |
archives.get_archive(format 枚举) | service/archives.rs:21-25 的 format 是 rpcgit::archive::types::ArchiveFormat(rpcgit/archive/types.rs:5-11,无 rename_all)→ serde 只认 Tar/TarGz/TarBz2/Zip。前端 archives.ts:30-35 QUERY_FORMAT 映射正是这四个值,正确。实证 format=tar_gz → {"code":400,...,"error":"Query deserialize error: unknown variant tar_gz, expected one of Tar, TarGz, TarBz2, Zip"};format=Tar → 通过解析,仅因无产物报 invalid argument: archive artifact not found |
archives.create_archive(body) | handler/archives/create.rs:30-36 ArchiveCreateCmd{revision,format:ArchiveFormatDto,path_filter},而 ArchiveFormatDto 是 #[serde(rename_all="snake_case")](同文件 :11)→ 只认 tar/tar_gz/tar_bz2/zip。前端 POST 传 cmd.format 的 snake_case 值、GET 才映射 PascalCase,两边分别正确。path_filter: null 对 Option<String> + #[serde(default)] 等价于 None |
archives.get_archive(返回值/无轮询) | ArchiveResultDto{format:String,size:i64,cache_key,download_url:Option<String>} 没有状态字段;前端 archives-settings.tsx:140-183 三处都是一次性调用后读 download_url/size,不做轮询,不存在「轮询错字段」 |
archives.create_bundle | handler/archives/create_bundle.rs:10-24 无 query/body,路径 2 段;前端 archives.ts:26 仅传 pathParams,一致 |
remotes.list_remotes | 返回裸 Vec<RemoteDto>(handler/remotes/list.rs:17,24),前端 unwrapApi<RemoteRow[]>(remotes.ts:21)按数组消费。实证 "data":[] |
remotes.upsert_remote / remove | handler/remotes/add.rs:16-23、remove.rs:16-19:path={space_name,repo_name,name} 3 段、body 仅 {url};前端 remotes.ts:25-27、:32 一致。返回 AppResponseData<NoData>,前端 unwrapApi<null> 与 data:null 对应 |
remotes.fetch / push / clone_import | body 分别为 RemoteFetchBody{remote,auth}、RemotePushBody{remote,refspec,auth}、RemoteImportBody{url,auth}(handler/remotes/{fetch,push,clone_import}.rs)。RemoteAuthDto{token:Option<String>,username:Option<String>}(service/remotes.rs:28-34)⊇ 前端 auth: {token};auth: null 与 Option<RemoteAuthDto> + #[serde(default)] 等价。refspec: null 同理 |
lfs.list_locks | handler/lfs/list_locks.rs:10-16 query=cursor,limit;前端 lfs.ts:15 发 {cursor, limit:50}(cursor 为空时回落 undefined 因而被丢弃)。返回 {locks,next_cursor?}(lfs/types.rs:110-115),实证 {"locks":[]}(无 next_cursor 时不出现,skip_serializing_if),前端 page.data?.locks ?? [] 兼容 |
lfs.verify_locks | handler/lfs/verify_locks.rs:10-14 query=limit,是 POST;生成签名 verifyLocks(spaceName, repoName, params?, options?) 与 OPS {query:true, body:false} 吻合。返回 {ours,theirs,next_cursor?},前端 locks-settings.tsx:58-61 读 ours/theirs 正确 |
lfs.lock | handler/lfs/lock.rs:10-15 body={path,oid};前端 lfs.ts:23 发 {path, oid: null},Option<String> + #[serde(default)] 接受 null |
lfs.unlock / unlock_by_id | handler/lfs/unlock.rs:9-14 query=path,force;unlock_by_id.rs:9-20 path={space_name,repo_name,lock_id} + query=force。前端 lfs.ts:31、:39 键名与顺序一致。注意 path 走 query(URLSearchParams 编码),不受上文 P 类缺陷影响 |
cleanup.list_cleanup_objects | lib/service/webapi/src/cleanup.rs:250,257 返回裸数组 AppResponseData<Vec<CleanupObjectDto>>;前端 rows<CleanupObject>(space-admin.ts:376)按数组消费,正确。path:1 = /spaces/{space_name}/cleanup 单段一致 |
cleanup.set_cleanup_visibility / set_cleanup_archive | body 分别 SetCleanupVisibilityCmd{is_private}(cleanup.rs:71-74)、SetRepoArchivedCmd{archived}(:76-79);前端 space-admin.ts:382、:390 逐字一致。path:3 分别对应 /visibility 与 /archive 两个后缀 |
cleanup.delete_cleanup_object / clear_cleanup_overwrites | 无 body,path:3 对应 :539 与 :668 的 {kind}/{object_id}[/overwrites];前端 space-admin.ts:396、:403 一致。返回 AppResponseData<NoData> ↔ unwrapApi<null> |
push.push_config | lib/service/user/src/handlers/push_config.rs:13-17,21 → PushConfigDto{enabled,public_key}、path="/push/config"、无参数。前端 notifications-section.tsx:26-31 类型 {enabled, public_key} 一致。实证 {"enabled":true,"public_key":"BCRWNH5S0jM0wMo0…"} |