Skip to content

Commit

Permalink
feat(controller): more git pkg improvements (#2531)
Browse files Browse the repository at this point in the history
Signed-off-by: Kent Rancourt <[email protected]>
  • Loading branch information
krancour authored Sep 16, 2024
1 parent eb7376a commit 7a07a29
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 37 deletions.
34 changes: 27 additions & 7 deletions internal/controller/git/bare_repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ import (

// BareRepo is an interface for interacting with a bare Git repository.
type BareRepo interface {
// AddWorkTree adds a working tree to the repository. The working tree will be
// created at the specified path and will be checked out to the specified ref.
AddWorkTree(path, ref string) (WorkTree, error)
// AddWorkTree adds a working tree to the repository.
AddWorkTree(path string, opts *AddWorkTreeOptions) (WorkTree, error)
// Close cleans up file system resources used by this repository. This should
// always be called before a repository goes out of scope.
Close() error
Expand All @@ -25,6 +24,9 @@ type BareRepo interface {
// HomeDir returns an absolute path to the home directory of the system user
// who has cloned this repo.
HomeDir() string
// RemoteBranchExists returns a bool indicating if the specified branch exists
// in the remote repository.
RemoteBranchExists(branch string) (bool, error)
// RemoveWorkTree removes a working tree from the repository. The working tree
// will be removed from the file system.
RemoveWorkTree(path string) error
Expand Down Expand Up @@ -135,7 +137,21 @@ func LoadBareRepo(path string, opts *LoadBareRepoOptions) (BareRepo, error) {
return b, nil
}

func (b *bareRepo) AddWorkTree(path, ref string) (WorkTree, error) {
// AddWorkTreeOptions represents options for adding a working tree to a bare
// repository.
type AddWorkTreeOptions struct {
// Orphan specifies whether the working tree should be created from a new,
// orphaned branch. If true, the Ref field will be ignored.
Orphan bool
// Ref specifies the branch or commit to check out in the working tree. Will
// be ignored if Orphan is true.
Ref string
}

func (b *bareRepo) AddWorkTree(path string, opts *AddWorkTreeOptions) (WorkTree, error) {
if opts == nil {
opts = &AddWorkTreeOptions{}
}
path, err := filepath.Abs(path)
if err != nil {
return nil, fmt.Errorf("error resolving absolute path for %s: %w", path, err)
Expand All @@ -147,9 +163,13 @@ func (b *bareRepo) AddWorkTree(path, ref string) (WorkTree, error) {
if slices.Contains(workTreePaths, path) {
return nil, fmt.Errorf("working tree already exists at %q", path)
}
if _, err = libExec.Exec(
b.buildGitCommand("worktree", "add", path, ref),
); err != nil {
args := []string{"worktree", "add", path}
if opts.Orphan {
args = append(args, "--orphan")
} else {
args = append(args, opts.Ref)
}
if _, err = libExec.Exec(b.buildGitCommand(args...)); err != nil {
return nil, fmt.Errorf("error adding working tree at %q: %w", path, err)
}
if path, err = filepath.EvalSymlinks(path); err != nil {
Expand Down
10 changes: 7 additions & 3 deletions internal/controller/git/bare_repo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,13 @@ func TestBareRepo(t *testing.T) {
})

workingTreePath := filepath.Join(rep.HomeDir(), "working-tree")
// "master" is still the default branch name for a new repository unless
// you configure it otherwise.
workTree, err := rep.AddWorkTree(workingTreePath, "master")
workTree, err := rep.AddWorkTree(
workingTreePath,
// "master" is still the default branch name for a new repository unless
// you configure it otherwise.
&AddWorkTreeOptions{Ref: "master"},
)

require.NoError(t, err)
defer workTree.Close()

Expand Down
25 changes: 25 additions & 0 deletions internal/controller/git/base_repo.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package git

import (
"errors"
"fmt"
"net/url"
"os"
Expand Down Expand Up @@ -244,6 +245,30 @@ func (b *baseRepo) HomeDir() string {
return b.homeDir
}

func (b *baseRepo) RemoteBranchExists(branch string) (bool, error) {
_, err := libExec.Exec(b.buildGitCommand(
"ls-remote",
"--heads",
"--exit-code", // Return 2 if not found
b.url,
branch,
))
var exitErr *libExec.ExitError
if errors.As(err, &exitErr) && exitErr.ExitCode == 2 {
// Branch does not exist
return false, nil
}
if err != nil {
return false, fmt.Errorf(
"error checking for existence of branch %q in remote repo %q: %w",
branch,
b.url,
err,
)
}
return true, nil
}

func (b *baseRepo) URL() string {
return b.url
}
35 changes: 11 additions & 24 deletions internal/controller/git/work_tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ type WorkTree interface {
AddAllAndCommit(message string) error
// Clean cleans the working tree.
Clean() error
// Clear executes `git rm -rf .` to remove all files from the working tree.
Clear() error
// Close cleans up file system resources used by this working tree. This
// should always be called before a WorkTree goes out of scope.
Close() error
Expand Down Expand Up @@ -150,6 +152,15 @@ func (w *workTree) Clean() error {
return nil
}

func (w *workTree) Clear() error {
if _, err := libExec.Exec(
w.buildGitCommand("rm", "-rf", "--ignore-unmatch", "."),
); err != nil {
return fmt.Errorf("error clearing worktree: %w", err)
}
return nil
}

func (w *workTree) Close() error {
if w.bareRepo != nil {
return w.bareRepo.RemoveWorkTree(w.dir)
Expand Down Expand Up @@ -496,30 +507,6 @@ func (w *workTree) RefsHaveDiffs(commit1 string, commit2 string) (bool, error) {
return false, fmt.Errorf("error diffing commits %s..%s: %w", commit1, commit2, err)
}

func (w *workTree) RemoteBranchExists(branch string) (bool, error) {
_, err := libExec.Exec(w.buildGitCommand(
"ls-remote",
"--heads",
"--exit-code", // Return 2 if not found
w.url,
branch,
))
var exitErr *libExec.ExitError
if errors.As(err, &exitErr) && exitErr.ExitCode == 2 {
// Branch does not exist
return false, nil
}
if err != nil {
return false, fmt.Errorf(
"error checking for existence of branch %q in remote repo %q: %w",
branch,
w.url,
err,
)
}
return true, nil
}

func (w *workTree) ResetHard() error {
if _, err := libExec.Exec(w.buildGitCommand("reset", "--hard")); err != nil {
return fmt.Errorf("error resetting branch working tree: %w", err)
Expand Down
9 changes: 6 additions & 3 deletions internal/controller/git/work_tree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,12 @@ func TestWorkTree(t *testing.T) {
defer rep.Close()

workingTreePath := filepath.Join(rep.HomeDir(), "working-tree")
// "master" is still the default branch name for a new repository unless
// you configure it otherwise.
workTree, err := rep.AddWorkTree(workingTreePath, "master")
workTree, err := rep.AddWorkTree(
workingTreePath,
// "master" is still the default branch name for a new repository unless
// you configure it otherwise.
&AddWorkTreeOptions{Ref: "master"},
)
require.NoError(t, err)
defer workTree.Close()

Expand Down

0 comments on commit 7a07a29

Please sign in to comment.