@@ -50,12 +50,16 @@ type Repository interface {
5050 GetCommitsForPathsSinceTag (paths []string , tagName string ) ([]* Commit , error )
5151 GetCommitsForPathsSinceCommit (paths []string , sinceCommit string ) ([]* Commit , error )
5252 CreateBranchAndCheckout (name string ) error
53+ CheckoutCommitAndCreateBranch (name , commitHash string ) error
54+ NewAndDeletedFiles () ([]string , error )
5355 Push (branchName string ) error
5456 Restore (paths []string ) error
5557 CleanUntracked (paths []string ) error
5658 pushRefSpec (refSpec string ) error
5759 Checkout (commitHash string ) error
5860 GetHashForPath (commitHash , path string ) (string , error )
61+ ResetHard () error
62+ DeleteLocalBranches (names []string ) error
5963}
6064
6165const RootPath = "."
@@ -257,6 +261,30 @@ func (r *LocalRepository) ChangedFiles() ([]string, error) {
257261 return changedFiles , nil
258262}
259263
264+ // NewAndDeletedFiles returns a list of files that are new or deleted.
265+ func (r * LocalRepository ) NewAndDeletedFiles () ([]string , error ) {
266+ slog .Debug ("getting new and deleted files" )
267+ worktree , err := r .repo .Worktree ()
268+ if err != nil {
269+ return nil , err
270+ }
271+ status , err := worktree .Status ()
272+ if err != nil {
273+ return nil , err
274+ }
275+ var files []string
276+ for file , fileStatus := range status {
277+ switch {
278+ case fileStatus .Worktree == git .Untracked ,
279+ fileStatus .Staging == git .Added ,
280+ fileStatus .Worktree == git .Deleted ,
281+ fileStatus .Staging == git .Deleted :
282+ files = append (files , file )
283+ }
284+ }
285+ return files , nil
286+ }
287+
260288// Remotes returns the remotes within the repository.
261289func (r * LocalRepository ) Remotes () ([]* Remote , error ) {
262290 gitRemotes , err := r .repo .Remotes ()
@@ -512,6 +540,21 @@ func (r *LocalRepository) CreateBranchAndCheckout(name string) error {
512540 })
513541}
514542
543+ // CheckoutCommitAndCreateBranch creates a new git branch from a specific commit hash
544+ // and checks out the branch in the local git repository.
545+ func (r * LocalRepository ) CheckoutCommitAndCreateBranch (name , commitHash string ) error {
546+ slog .Debug ("creating branch from commit and checking out" , "name" , name , "commit" , commitHash )
547+ worktree , err := r .repo .Worktree ()
548+ if err != nil {
549+ return err
550+ }
551+ return worktree .Checkout (& git.CheckoutOptions {
552+ Hash : plumbing .NewHash (commitHash ),
553+ Branch : plumbing .NewBranchReferenceName (name ),
554+ Create : true ,
555+ })
556+ }
557+
515558// Push pushes the local branch to the origin remote.
516559func (r * LocalRepository ) Push (branchName string ) error {
517560 // https://stackoverflow.com/a/75727620
@@ -683,3 +726,38 @@ func (r *LocalRepository) GetHashForPath(commitHash, path string) (string, error
683726 }
684727 return getHashForPath (commit , path )
685728}
729+
730+ // ResetHard resets the repository to HEAD, discarding all local changes.
731+ func (r * LocalRepository ) ResetHard () error {
732+ worktree , err := r .repo .Worktree ()
733+ if err != nil {
734+ return err
735+ }
736+ return worktree .Reset (& git.ResetOptions {
737+ Mode : git .HardReset ,
738+ })
739+ }
740+
741+ // DeleteLocalBranches deletes a list of local branches.
742+ // It returns an error if any branch deletion fails, or nil if all succeed.
743+ func (r * LocalRepository ) DeleteLocalBranches (names []string ) error {
744+ slog .Debug ("starting batch deletion of local branches" , "count" , len (names ))
745+ headRef , headErr := r .repo .Head ()
746+ if headErr != nil && ! errors .Is (headErr , plumbing .ErrReferenceNotFound ) {
747+ return fmt .Errorf ("failed to get HEAD to protect against its deletion: %w" , headErr )
748+ }
749+ for _ , name := range names {
750+ refName := plumbing .NewBranchReferenceName (name )
751+ _ , err := r .repo .Storer .Reference (refName )
752+ if err != nil {
753+ return fmt .Errorf ("failed to check existence of branch %s: %w" , name , err )
754+ }
755+ if headErr == nil && headRef .Name () == refName {
756+ return fmt .Errorf ("cannot delete branch %s: it is the currently checked out branch (HEAD)" , name )
757+ }
758+ if err := r .repo .Storer .RemoveReference (refName ); err != nil {
759+ return fmt .Errorf ("failed to delete branch %s: %w" , name , err )
760+ }
761+ }
762+ return nil
763+ }
0 commit comments