馃嵃 Personal Multi-Git Remote Manager
go git
at main 257 lines 4.7 kB view raw
1package cli 2 3import ( 4 "errors" 5 "fmt" 6 "os" 7 "strings" 8 9 "github.com/ebisu/mugi/internal/remote" 10) 11 12type CommandType int 13 14const ( 15 CommandOperation CommandType = iota 16 CommandAdd 17 CommandRemove 18 CommandList 19) 20 21type Command struct { 22 Type CommandType 23 Operation remote.Operation 24 Repo string 25 Remotes []string 26 Path string 27 ConfigPath string 28 Verbose bool 29 Force bool 30 Linear bool 31 Help bool 32 Version bool 33} 34 35var ErrUnknownCommand = errors.New("unknown command") 36 37func Parse(args []string) (Command, error) { 38 cmd := Command{ 39 Remotes: []string{remote.All}, 40 } 41 42 if len(args) == 0 { 43 cmd.Help = true 44 45 return cmd, nil 46 } 47 48 args, cmd.ConfigPath = extractConfigFlag(args) 49 args, cmd.Verbose = extractVerboseFlag(args) 50 args, cmd.Force = extractForceFlag(args) 51 args, cmd.Linear = extractLinearFlag(args) 52 53 for _, arg := range args { 54 if arg == "-h" || arg == "--help" || arg == "help" { 55 cmd.Help = true 56 57 return cmd, nil 58 } 59 60 if arg == "-v" || arg == "--version" || arg == "version" { 61 cmd.Version = true 62 63 return cmd, nil 64 } 65 } 66 67 if len(args) == 0 { 68 cmd.Help = true 69 70 return cmd, nil 71 } 72 73 switch args[0] { 74 case "pull": 75 cmd.Type = CommandOperation 76 cmd.Operation = remote.Pull 77 case "push": 78 cmd.Type = CommandOperation 79 cmd.Operation = remote.Push 80 case "fetch": 81 cmd.Type = CommandOperation 82 cmd.Operation = remote.Fetch 83 case "add": 84 cmd.Type = CommandAdd 85 86 if len(args) < 2 { 87 cmd.Path = "." 88 } else { 89 cmd.Path = args[1] 90 } 91 92 return cmd, nil 93 case "rm", "remove": 94 cmd.Type = CommandRemove 95 96 if len(args) < 2 { 97 return cmd, fmt.Errorf("rm requires a repository name") 98 } 99 100 cmd.Repo = args[1] 101 102 return cmd, nil 103 case "list", "ls": 104 cmd.Type = CommandList 105 106 return cmd, nil 107 default: 108 return cmd, fmt.Errorf("%w: %s", ErrUnknownCommand, args[0]) 109 } 110 111 remaining := args[1:] 112 113 if len(remaining) == 0 { 114 cmd.Repo = remote.All 115 116 return cmd, nil 117 } 118 119 cmd.Repo = remaining[0] 120 remaining = remaining[1:] 121 122 if len(remaining) > 0 { 123 cmd.Remotes = remaining 124 } 125 126 return cmd, nil 127} 128 129func Usage() string { 130 return `Mugi - Personal Multi-Git Remote Manager 131 132Usage: 133 mugi [flags] <command> [repo] [remotes...] 134 135Commands: 136 pull Pull from remote(s) 137 push Push to remote(s) 138 fetch Fetch from remote(s) 139 add <path> Add repository to config 140 rm <name> Remove repository from config 141 list List tracked repositories 142 help Show this help 143 version Show version 144 145Flags: 146 -c, --config <path> Override config file path 147 -V, --verbose Show detailed output 148 -f, --force Force push (use with caution) 149 -l, --linear Run operations sequentially 150 151Examples: 152 mugi pull Pull all repositories from all remotes 153 mugi push windmark gh cb Push to GitHub and Codeberg 154 mugi add . Add current directory to config 155 mugi add ~/Developer/mugi Add repository at path 156 mugi rm mugi Remove repository from config 157 mugi list List all tracked repositories 158 159Config: ` + configPath() 160} 161 162func configPath() string { 163 xdg := os.Getenv("XDG_CONFIG_HOME") 164 165 if xdg == "" { 166 home, _ := os.UserHomeDir() 167 168 return home + "/.config/mugi/config.yaml" 169 } 170 171 return xdg + "/mugi/config.yaml" 172} 173 174func extractConfigFlag(args []string) ([]string, string) { 175 var remaining []string 176 var configPath string 177 178 for i := 0; i < len(args); i++ { 179 arg := args[i] 180 181 if arg == "-c" || arg == "--config" { 182 if i+1 < len(args) { 183 configPath = args[i+1] 184 i++ 185 } 186 187 continue 188 } 189 190 if v, ok := strings.CutPrefix(arg, "--config="); ok { 191 configPath = v 192 193 continue 194 } 195 196 if v, ok := strings.CutPrefix(arg, "-c"); ok && v != "" { 197 configPath = v 198 199 continue 200 } 201 202 remaining = append(remaining, arg) 203 } 204 205 return remaining, configPath 206} 207 208func extractVerboseFlag(args []string) ([]string, bool) { 209 var remaining []string 210 var verbose bool 211 212 for _, arg := range args { 213 if arg == "-V" || arg == "--verbose" { 214 verbose = true 215 216 continue 217 } 218 219 remaining = append(remaining, arg) 220 } 221 222 return remaining, verbose 223} 224 225func extractForceFlag(args []string) ([]string, bool) { 226 var remaining []string 227 var force bool 228 229 for _, arg := range args { 230 if arg == "-f" || arg == "--force" { 231 force = true 232 233 continue 234 } 235 236 remaining = append(remaining, arg) 237 } 238 239 return remaining, force 240} 241 242func extractLinearFlag(args []string) ([]string, bool) { 243 var remaining []string 244 var linear bool 245 246 for _, arg := range args { 247 if arg == "-l" || arg == "--linear" { 248 linear = true 249 250 continue 251 } 252 253 remaining = append(remaining, arg) 254 } 255 256 return remaining, linear 257}