You've already forked agentic-coding-workflow
feat(git): add FetchRemoteBranch, Checkout, ListRemoteBranches to Runner
This commit is contained in:
@@ -0,0 +1,64 @@
|
|||||||
|
package git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FetchRemoteBranch fetches a single remote branch (origin/<branch>).
|
||||||
|
// Lighter than Fetch --all when we only need one branch.
|
||||||
|
func (r *DefaultRunner) FetchRemoteBranch(ctx context.Context, dir, branch string, cred *Credential) error {
|
||||||
|
ctx, cancel := withCtx(ctx, r.cfg.FetchTimeout)
|
||||||
|
defer cancel()
|
||||||
|
env, cleanup, err := credentialEnv(r.cfg.TmpDir, cred)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer cleanup()
|
||||||
|
_, _, err = r.exec(ctx, dir, env, "fetch", "origin", branch)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checkout switches the working tree to the given branch.
|
||||||
|
func (r *DefaultRunner) Checkout(ctx context.Context, dir, branch string) error {
|
||||||
|
ctx, cancel := withCtx(ctx, r.cfg.OpsTimeout)
|
||||||
|
defer cancel()
|
||||||
|
_, _, err := r.exec(ctx, dir, nil, "checkout", branch)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRemoteBranches queries the remote for all branch names via git ls-remote.
|
||||||
|
// Returns branch names with refs/heads/ prefix stripped.
|
||||||
|
func (r *DefaultRunner) ListRemoteBranches(ctx context.Context, dir string, cred *Credential) ([]string, error) {
|
||||||
|
ctx, cancel := withCtx(ctx, r.cfg.FetchTimeout)
|
||||||
|
defer cancel()
|
||||||
|
env, cleanup, err := credentialEnv(r.cfg.TmpDir, cred)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer cleanup()
|
||||||
|
out, _, err := r.exec(ctx, dir, env, "ls-remote", "--heads")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return parseRemoteBranches(out), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseRemoteBranches parses git ls-remote --heads output.
|
||||||
|
// Each line: "<sha>\trefs/heads/<branch>"
|
||||||
|
func parseRemoteBranches(buf []byte) []string {
|
||||||
|
var branches []string
|
||||||
|
sc := bufio.NewScanner(bytes.NewReader(buf))
|
||||||
|
for sc.Scan() {
|
||||||
|
line := sc.Text()
|
||||||
|
if idx := strings.Index(line, "\trefs/heads/"); idx >= 0 {
|
||||||
|
name := line[idx+len("\trefs/heads/"):]
|
||||||
|
if name != "" {
|
||||||
|
branches = append(branches, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return branches
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ const stderrTruncateLimit = 2000
|
|||||||
type Runner interface {
|
type Runner interface {
|
||||||
Clone(ctx context.Context, dst, remoteURL, branch string, cred *Credential) error
|
Clone(ctx context.Context, dst, remoteURL, branch string, cred *Credential) error
|
||||||
Fetch(ctx context.Context, dir string, cred *Credential) error
|
Fetch(ctx context.Context, dir string, cred *Credential) error
|
||||||
|
FetchRemoteBranch(ctx context.Context, dir, branch string, cred *Credential) error
|
||||||
Push(ctx context.Context, dir, branch string, cred *Credential) error
|
Push(ctx context.Context, dir, branch string, cred *Credential) error
|
||||||
Commit(ctx context.Context, dir, message string, addAll bool) (sha string, err error)
|
Commit(ctx context.Context, dir, message string, addAll bool) (sha string, err error)
|
||||||
Status(ctx context.Context, dir string) ([]FileStatus, error)
|
Status(ctx context.Context, dir string) ([]FileStatus, error)
|
||||||
@@ -30,6 +31,8 @@ type Runner interface {
|
|||||||
WorktreeAdd(ctx context.Context, dir, branch, path, startPoint string) error
|
WorktreeAdd(ctx context.Context, dir, branch, path, startPoint string) error
|
||||||
WorktreeRemove(ctx context.Context, dir, path string) error
|
WorktreeRemove(ctx context.Context, dir, path string) error
|
||||||
WorktreeList(ctx context.Context, dir string) ([]Worktree, error)
|
WorktreeList(ctx context.Context, dir string) ([]Worktree, error)
|
||||||
|
ListRemoteBranches(ctx context.Context, dir string, cred *Credential) ([]string, error)
|
||||||
|
Checkout(ctx context.Context, dir, branch string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileStatus 表示 `git status --porcelain=v1` 的一行。
|
// FileStatus 表示 `git status --porcelain=v1` 的一行。
|
||||||
|
|||||||
@@ -226,6 +226,11 @@ func (f *fakeGit) WorktreeRemove(_ context.Context, _, _ string) error
|
|||||||
func (f *fakeGit) WorktreeList(_ context.Context, _ string) ([]git.Worktree, error) {
|
func (f *fakeGit) WorktreeList(_ context.Context, _ string) ([]git.Worktree, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
func (f *fakeGit) FetchRemoteBranch(_ context.Context, _, _ string, _ *git.Credential) error { return nil }
|
||||||
|
func (f *fakeGit) Checkout(_ context.Context, _, _ string) error { return nil }
|
||||||
|
func (f *fakeGit) ListRemoteBranches(_ context.Context, _ string, _ *git.Credential) ([]string, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
type noopAudit struct{}
|
type noopAudit struct{}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user