101 lines
2.4 KiB
Go
101 lines
2.4 KiB
Go
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
|
|
}
|