You've already forked agentic-coding-workflow
feat(infra/git): clone/fetch/push/commit/status verbs + integration tests
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
)
|
||||
|
||||
// Status 返回 git status --porcelain=v1 的结构化解析。空 worktree 返回空切片。
|
||||
func (r *DefaultRunner) Status(ctx context.Context, dir string) ([]FileStatus, error) {
|
||||
ctx, cancel := withCtx(ctx, r.cfg.OpsTimeout)
|
||||
defer cancel()
|
||||
out, _, err := r.exec(ctx, dir, nil, "status", "--porcelain=v1", "-z")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parsePorcelainV1(out), nil
|
||||
}
|
||||
|
||||
// parsePorcelainV1 解析 -z 输出(NUL 分隔,不转义)。
|
||||
// 每条记录格式:XY <SP> <path>\0
|
||||
// renamed/copied 时是 XY <SP> <to>\0<from>\0;本实现把 to 当 path,丢弃 from。
|
||||
func parsePorcelainV1(buf []byte) []FileStatus {
|
||||
var out []FileStatus
|
||||
for len(buf) > 0 {
|
||||
nul := bytes.IndexByte(buf, 0)
|
||||
if nul < 0 {
|
||||
break
|
||||
}
|
||||
rec := buf[:nul]
|
||||
buf = buf[nul+1:]
|
||||
if len(rec) < 4 {
|
||||
continue
|
||||
}
|
||||
x, y := rec[0], rec[1]
|
||||
// rec[2] 是空格
|
||||
path := string(rec[3:])
|
||||
out = append(out, FileStatus{Path: path, IndexX: x, WorktreeY: y})
|
||||
// renamed/copied:跳过 from 段
|
||||
if x == 'R' || x == 'C' {
|
||||
nul = bytes.IndexByte(buf, 0)
|
||||
if nul >= 0 {
|
||||
buf = buf[nul+1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user