larc r30

130 lines ยท 3.4 KB Raw
1 package cli
2
3 import (
4 "fmt"
5 "os"
6 "path/filepath"
7
8 "github.com/spf13/cobra"
9 "larc.wejust.rest/larc/internal/core"
10 "larc.wejust.rest/larc/internal/repo"
11 "larc.wejust.rest/larc/internal/status"
12 )
13
14 func BranchJumpCmd() *cobra.Command {
15 cmd := &cobra.Command{
16 Use: "branchjump <branch>",
17 Short: "Switch to a different branch",
18 Aliases: []string{"bj"},
19 Args: cobra.ExactArgs(1),
20 RunE: func(cmd *cobra.Command, args []string) error {
21 wd, _ := os.Getwd()
22 repoRoot, err := repo.FindRepoRoot(wd)
23 if err != nil {
24 return fmt.Errorf("not a larc repository")
25 }
26
27 r, err := repo.Open(repoRoot)
28 if err != nil {
29 return err
30 }
31 defer r.Close()
32
33 branches, err := r.Meta.ListBranches()
34 if err != nil {
35 return err
36 }
37
38 if len(branches) == 0 {
39 fmt.Println("No branches yet.")
40 return nil
41 }
42
43 currentBranch, _ := r.CurrentBranch()
44
45 branchName := args[0]
46 var selectedBranch *core.Branch
47 for _, b := range branches {
48 if b.Name == branchName {
49 selectedBranch = b
50 break
51 }
52 }
53
54 if selectedBranch == nil {
55 return fmt.Errorf("branch '%s' not found", branchName)
56 }
57
58 if selectedBranch.Name == currentBranch {
59 fmt.Printf("Already on branch %s.\n", selectedBranch.Name)
60 return nil
61 }
62
63 fmt.Printf("Jumping to branch %s...\n", selectedBranch.Name)
64
65 // Get the head revision of the selected branch
66 rev, err := r.Meta.GetRevision(selectedBranch.HeadRev)
67 if err != nil {
68 return fmt.Errorf("failed to get revision for branch %s: %w", selectedBranch.Name, err)
69 }
70
71 // Get the tree for the new revision
72 tree, err := r.GetTree(rev.TreeHash)
73 if err != nil {
74 return fmt.Errorf("failed to get tree for revision r%d: %w", rev.Number, err)
75 }
76
77 // Clean the working directory
78 // NOTE(kroot): This is a simple implementation that just removes all files.
79 // A more robust implementation would compare the files and only
80 // remove/update the ones that have changed.
81
82 scanner := status.NewScanner(r)
83 entries, err := scanner.GetStagedEntries()
84 if err != nil {
85 return err
86 }
87 for _, entry := range entries {
88 os.Remove(filepath.Join(repoRoot, entry.Path))
89 }
90
91 // Checkout the new tree
92 for _, entry := range tree.Entries {
93 filePath := filepath.Join(repoRoot, entry.Path)
94 if err := os.MkdirAll(filepath.Dir(filePath), 0755); err != nil {
95 return fmt.Errorf("failed to create directory %s: %w", filepath.Dir(filePath), err)
96 }
97 data, err := r.Blobs.Read(entry.BlobHash)
98 if err != nil {
99 return fmt.Errorf("failed to read blob %s: %w", entry.BlobHash, err)
100 }
101 if err := os.WriteFile(filePath, data, os.FileMode(entry.Mode)); err != nil {
102 return fmt.Errorf("failed to write file %s: %w", filePath, err)
103 }
104 }
105
106 // Update the current branch and revision
107 if err := r.SetCurrentBranch(selectedBranch.Name); err != nil {
108 return fmt.Errorf("failed to set current branch: %w", err)
109 }
110 if err := r.SetCurrentRevision(selectedBranch.HeadRev); err != nil {
111 return fmt.Errorf("failed to set current revision: %w", err)
112 }
113
114 // Update the index
115 scanner.ClearStaging()
116 for _, entry := range tree.Entries {
117 scanner.Stage(entry.Path, entry.BlobHash, entry.Size)
118 }
119 if err := scanner.SaveIndex(); err != nil {
120 return fmt.Errorf("failed to save index: %w", err)
121 }
122
123 fmt.Printf("Successfully jumped to branch %s.\n", selectedBranch.Name)
124
125 return nil
126 },
127 }
128 return cmd
129 }
130