package git import ( "fmt" "os" "path/filepath" gogit "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/transport" "github.com/go-git/go-git/v5/plumbing/transport/http" ) // Service handles Git operations for projects. type Service struct{} // New creates a new Git Service. func New() *Service { return &Service{} } // CloneOrPull clones a repo if it doesn't exist locally, or pulls latest changes. // Returns the commit hash of HEAD after the operation. func (s *Service) CloneOrPull(url, branch, username, password, dstDir string) (string, error) { // Check if repo already exists repo, err := gogit.PlainOpen(dstDir) if err == nil { // Repo exists, pull return s.pull(repo, branch, username, password) } // Clone fresh return s.clone(url, branch, username, password, dstDir) } func (s *Service) clone(url, branch, username, password, dstDir string) (string, error) { // Ensure parent directory exists os.MkdirAll(filepath.Dir(dstDir), 0755) // Remove target if it exists (shouldn't, but be safe) os.RemoveAll(dstDir) var auth transport.AuthMethod if username != "" || password != "" { auth = &http.BasicAuth{ Username: username, Password: password, } } refName := plumbing.NewBranchReferenceName(branch) repo, err := gogit.PlainClone(dstDir, false, &gogit.CloneOptions{ URL: url, Auth: auth, ReferenceName: refName, SingleBranch: true, Depth: 1, Progress: nil, }) if err != nil { return "", fmt.Errorf("clone failed: %w", err) } return getHeadHash(repo) } func (s *Service) pull(repo *gogit.Repository, branch, username, password string) (string, error) { w, err := repo.Worktree() if err != nil { return "", fmt.Errorf("worktree: %w", err) } var auth transport.AuthMethod if username != "" || password != "" { auth = &http.BasicAuth{ Username: username, Password: password, } } refName := plumbing.NewBranchReferenceName(branch) err = w.Pull(&gogit.PullOptions{ RemoteName: "origin", Auth: auth, ReferenceName: refName, SingleBranch: true, }) if err != nil && err != gogit.NoErrAlreadyUpToDate { return "", fmt.Errorf("pull failed: %w", err) } return getHeadHash(repo) } func getHeadHash(repo *gogit.Repository) (string, error) { ref, err := repo.Head() if err != nil { return "", err } return ref.Hash().String(), nil }