package git import ( "bufio" "bytes" "context" "strings" ) // FetchRemoteBranch fetches a single remote branch (origin/). // 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: "\trefs/heads/" func parseRemoteBranches(buf []byte) []string { var branches []string sc := bufio.NewScanner(bytes.NewReader(buf)) for sc.Scan() { line := strings.TrimSpace(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 }