1package cmd
  2
  3import (
  4	"fmt"
  5	"strings"
  6
  7	"github.com/charmbracelet/soft-serve/git"
  8	"github.com/charmbracelet/soft-serve/server/backend"
  9	gitm "github.com/gogs/git-module"
 10	"github.com/spf13/cobra"
 11)
 12
 13func branchCommand() *cobra.Command {
 14	cmd := &cobra.Command{
 15		Use:   "branch",
 16		Short: "Manage repository branches",
 17	}
 18
 19	cmd.AddCommand(
 20		branchListCommand(),
 21		branchDefaultCommand(),
 22		branchDeleteCommand(),
 23	)
 24
 25	return cmd
 26}
 27
 28func branchListCommand() *cobra.Command {
 29	cmd := &cobra.Command{
 30		Use:               "list REPOSITORY",
 31		Short:             "List repository branches",
 32		Args:              cobra.ExactArgs(1),
 33		PersistentPreRunE: checkIfReadable,
 34		RunE: func(cmd *cobra.Command, args []string) error {
 35			ctx := cmd.Context()
 36			be := backend.FromContext(ctx)
 37			rn := strings.TrimSuffix(args[0], ".git")
 38			rr, err := be.Repository(ctx, rn)
 39			if err != nil {
 40				return err
 41			}
 42
 43			r, err := rr.Open()
 44			if err != nil {
 45				return err
 46			}
 47
 48			branches, _ := r.Branches()
 49			for _, b := range branches {
 50				cmd.Println(b)
 51			}
 52
 53			return nil
 54		},
 55	}
 56
 57	return cmd
 58}
 59
 60func branchDefaultCommand() *cobra.Command {
 61	cmd := &cobra.Command{
 62		Use:   "default REPOSITORY [BRANCH]",
 63		Short: "Set or get the default branch",
 64		Args:  cobra.RangeArgs(1, 2),
 65		RunE: func(cmd *cobra.Command, args []string) error {
 66			ctx := cmd.Context()
 67			be := backend.FromContext(ctx)
 68			rn := strings.TrimSuffix(args[0], ".git")
 69			switch len(args) {
 70			case 1:
 71				if err := checkIfReadable(cmd, args); err != nil {
 72					return err
 73				}
 74				rr, err := be.Repository(ctx, rn)
 75				if err != nil {
 76					return err
 77				}
 78
 79				r, err := rr.Open()
 80				if err != nil {
 81					return err
 82				}
 83
 84				head, err := r.HEAD()
 85				if err != nil {
 86					return err
 87				}
 88
 89				cmd.Println(head.Name().Short())
 90			case 2:
 91				if err := checkIfCollab(cmd, args); err != nil {
 92					return err
 93				}
 94
 95				rr, err := be.Repository(ctx, rn)
 96				if err != nil {
 97					return err
 98				}
 99
100				r, err := rr.Open()
101				if err != nil {
102					return err
103				}
104
105				branch := args[1]
106				branches, _ := r.Branches()
107				var exists bool
108				for _, b := range branches {
109					if branch == b {
110						exists = true
111						break
112					}
113				}
114
115				if !exists {
116					return git.ErrReferenceNotExist
117				}
118
119				if _, err := r.SymbolicRef(git.HEAD, gitm.RefsHeads+branch, gitm.SymbolicRefOptions{
120					CommandOptions: gitm.CommandOptions{
121						Context: ctx,
122					},
123				}); err != nil {
124					return err
125				}
126			}
127
128			return nil
129		},
130	}
131
132	return cmd
133}
134
135func branchDeleteCommand() *cobra.Command {
136	cmd := &cobra.Command{
137		Use:               "delete REPOSITORY BRANCH",
138		Aliases:           []string{"remove", "rm", "del"},
139		Short:             "Delete a branch",
140		PersistentPreRunE: checkIfCollab,
141		RunE: func(cmd *cobra.Command, args []string) error {
142			ctx := cmd.Context()
143			be := backend.FromContext(ctx)
144			rn := strings.TrimSuffix(args[0], ".git")
145			rr, err := be.Repository(ctx, rn)
146			if err != nil {
147				return err
148			}
149
150			r, err := rr.Open()
151			if err != nil {
152				return err
153			}
154
155			branch := args[1]
156			branches, _ := r.Branches()
157			var exists bool
158			for _, b := range branches {
159				if branch == b {
160					exists = true
161					break
162				}
163			}
164
165			if !exists {
166				return git.ErrReferenceNotExist
167			}
168
169			head, err := r.HEAD()
170			if err != nil {
171				return err
172			}
173
174			if head.Name().Short() == branch {
175				return fmt.Errorf("cannot delete the default branch")
176			}
177
178			return r.DeleteBranch(branch, gitm.DeleteBranchOptions{Force: true})
179		},
180	}
181
182	return cmd
183}