A vibe coded tangled fork which supports pijul.
at master 160 lines 3.5 kB view raw
1package pijul 2 3import ( 4 "bufio" 5 "bytes" 6 "fmt" 7 "strings" 8) 9 10// Channel represents a Pijul channel (analogous to a Git branch) 11type Channel struct { 12 Name string `json:"name"` 13 IsCurrent bool `json:"is_current,omitempty"` 14} 15 16// Channels returns the list of channels in the repository 17func (p *PijulRepo) Channels() ([]Channel, error) { 18 output, err := p.channelCmd() 19 if err != nil { 20 return nil, fmt.Errorf("listing channels: %w", err) 21 } 22 23 return parseChannelOutput(output) 24} 25 26// parseChannelOutput parses the output of pijul channel 27// Expected format: 28// 29// * main 30// feature-branch 31// another-branch 32func parseChannelOutput(output []byte) ([]Channel, error) { 33 var channels []Channel 34 scanner := bufio.NewScanner(bytes.NewReader(output)) 35 36 for scanner.Scan() { 37 line := scanner.Text() 38 if strings.TrimSpace(line) == "" { 39 continue 40 } 41 42 isCurrent := strings.HasPrefix(line, "* ") 43 name := strings.TrimSpace(strings.TrimPrefix(line, "* ")) 44 name = strings.TrimSpace(strings.TrimPrefix(name, " ")) 45 46 if name != "" { 47 channels = append(channels, Channel{ 48 Name: name, 49 IsCurrent: isCurrent, 50 }) 51 } 52 } 53 54 return channels, scanner.Err() 55} 56 57// ChannelOptions options for channel listing 58type ChannelOptions struct { 59 Limit int 60 Offset int 61} 62 63// ChannelsWithOptions returns channels with pagination 64func (p *PijulRepo) ChannelsWithOptions(opts *ChannelOptions) ([]Channel, error) { 65 channels, err := p.Channels() 66 if err != nil { 67 return nil, err 68 } 69 70 if opts == nil { 71 return channels, nil 72 } 73 74 // Apply offset 75 if opts.Offset > 0 { 76 if opts.Offset >= len(channels) { 77 return []Channel{}, nil 78 } 79 channels = channels[opts.Offset:] 80 } 81 82 // Apply limit 83 if opts.Limit > 0 && opts.Limit < len(channels) { 84 channels = channels[:opts.Limit] 85 } 86 87 return channels, nil 88} 89 90// CreateChannel creates a new channel 91func (p *PijulRepo) CreateChannel(name string) error { 92 _, err := p.channelCmd("new", name) 93 return err 94} 95 96// DeleteChannel deletes a channel 97func (p *PijulRepo) DeleteChannel(name string) error { 98 _, err := p.channelCmd("delete", name) 99 return err 100} 101 102// RenameChannel renames a channel 103func (p *PijulRepo) RenameChannel(oldName, newName string) error { 104 _, err := p.channelCmd("rename", oldName, newName) 105 return err 106} 107 108// SwitchChannel switches to a different channel 109func (p *PijulRepo) SwitchChannel(name string) error { 110 _, err := p.channelCmd("switch", name) 111 if err != nil { 112 return fmt.Errorf("switching to channel %s: %w", name, err) 113 } 114 p.channelName = name 115 return nil 116} 117 118// CurrentChannelName returns the name of the current channel 119func (p *PijulRepo) CurrentChannelName() (string, error) { 120 channels, err := p.Channels() 121 if err != nil { 122 return "", err 123 } 124 125 for _, ch := range channels { 126 if ch.IsCurrent { 127 return ch.Name, nil 128 } 129 } 130 131 // If no current channel marked, default to "main" 132 return "main", nil 133} 134 135// ForkChannel creates a new channel from an existing one 136// This is equivalent to Git's "git checkout -b newbranch oldbranch" 137func (p *PijulRepo) ForkChannel(newName, fromChannel string) error { 138 args := []string{"fork", newName} 139 if fromChannel != "" { 140 args = append(args, "--channel", fromChannel) 141 } 142 _, err := p.channelCmd(args...) 143 return err 144} 145 146// ChannelExists checks if a channel exists 147func (p *PijulRepo) ChannelExists(name string) (bool, error) { 148 channels, err := p.Channels() 149 if err != nil { 150 return false, err 151 } 152 153 for _, ch := range channels { 154 if ch.Name == name { 155 return true, nil 156 } 157 } 158 159 return false, nil 160}