package git import ( "bytes" "context" "os/exec" "strings" ) func run(ctx context.Context, args ...string) (string, error) { cmd := exec.CommandContext(ctx, "git", args...) var out bytes.Buffer var errb bytes.Buffer cmd.Stdout = &out cmd.Stderr = &errb if err := cmd.Run(); err != nil { if errb.Len() > 0 { return "", fmtError(errb.String()) } return "", err } return strings.TrimSpace(out.String()), nil } // Run is a public version of run for use in other packages func Run(ctx context.Context, args ...string) (string, error) { return run(ctx, args...) } func Fetch(ctx context.Context) error { _, err := run(ctx, "fetch", "--prune") return err } func RebaseOntoTracking(ctx context.Context) error { branch, err := CurrentBranch(ctx) if err != nil { return err } up, err := UpstreamFor(ctx, branch) if err != nil { return err } _, err = run(ctx, "rebase", up) return err } func PushCurrent(ctx context.Context, forceWithLease bool) error { args := []string{"push"} if forceWithLease { args = append(args, "--force-with-lease") } args = append(args, "--set-upstream", "origin", "HEAD") _, err := run(ctx, args...) return err } func LocalBranches(ctx context.Context) ([]string, error) { out, err := run(ctx, "branch", "--format=%(refname:short)") if err != nil { return nil, err } if out == "" { return nil, nil } return strings.Split(out, "\n"), nil } func DeleteBranch(ctx context.Context, name string) error { _, err := run(ctx, "branch", "-D", name) return err } func CurrentBranch(ctx context.Context) (string, error) { return run(ctx, "rev-parse", "--abbrev-ref", "HEAD") } func UpstreamFor(ctx context.Context, branch string) (string, error) { return run(ctx, "rev-parse", "--abbrev-ref", branch+"@{upstream}") } func CreateBranch(ctx context.Context, name string) error { _, err := run(ctx, "checkout", "-b", name) return err } func SwitchBranch(ctx context.Context, name string) error { _, err := run(ctx, "checkout", name) return err } func IsCleanWorkingDir(ctx context.Context) (bool, error) { out, err := run(ctx, "status", "--porcelain") if err != nil { return false, err } return out == "", nil } func AddAll(ctx context.Context) error { _, err := run(ctx, "add", "-A") return err } func Commit(ctx context.Context, message string) error { _, err := run(ctx, "commit", "-m", message) return err } func fmtError(s string) error { return &gitError{msg: strings.TrimSpace(s)} } type gitError struct { msg string } func (e *gitError) Error() string { return e.msg } // GetRemoteURL returns the URL of the specified remote func GetRemoteURL(ctx context.Context, remote string) (string, error) { return run(ctx, "remote", "get-url", remote) }