github.com/driusan/dgit@v0.0.0-20221118233547-f39f0c15edbb/git/refspec.go (about) 1 package git 2 3 import ( 4 "bytes" 5 "fmt" 6 "strings" 7 ) 8 9 // A RefSpec refers to a reference contained under .git/refs 10 type RefSpec string 11 12 func (r RefSpec) String() string { 13 if len(r) < 1 { 14 return "" 15 } 16 17 // This will only trim a single nil byte, but if there's more 18 // than that we're doing something really wrong. 19 return strings.TrimSpace(strings.TrimSuffix(string(r), "\000")) 20 } 21 22 // Src represents the ref name of the ref at the remote location for a refspec. 23 // for instance, in the refspec "refs/heads/foo:refs/remotes/origin/foo", 24 // src refers to refs/heads/foo, the name of the remote reference, while dst 25 // refers to refs/remotes/origin/master, the location that we store our cache 26 // of what that remote reference is. 27 func (r RefSpec) Src() Refname { 28 if len(r) < 1 { 29 return "" 30 } 31 32 if r[0] == '+' { 33 r = r[1:] 34 } 35 if pos := strings.Index(string(r), ":"); pos >= 0 { 36 return Refname(r[:pos]) 37 } 38 39 // There was no ":", so we just take the name. 40 return Refname(r) 41 } 42 43 // Dst represents the local destination of a remote ref in a refspec. 44 // For instance, in the refspec "refs/heads/foo:refs/remotes/origin/foo", 45 // Dst refers to "refs/remotes/origin/foo". If the refspec does not have an 46 // explicit Dst specified, Dst returns an empty refname. 47 func (r RefSpec) Dst() Refname { 48 if len(r) < 1 { 49 return "" 50 } 51 52 if r[0] == '+' { 53 r = r[1:] 54 } 55 if pos := strings.Index(string(r), ":"); pos >= 0 { 56 return Refname(r[pos+1:]) 57 } 58 return "" 59 } 60 61 func (r RefSpec) HasPrefix(s string) bool { 62 return strings.HasPrefix(r.String(), s) 63 } 64 65 // Returns the file that holds r. 66 func (r RefSpec) File(c *Client) File { 67 return c.GitDir.File(File(r.String())) 68 } 69 70 // Returns the value of RefSpec in Client's GitDir, or the empty string 71 // if it doesn't exist. 72 func (r RefSpec) Value(c *Client) (string, error) { 73 f := r.File(c) 74 val, err := f.ReadAll() 75 return strings.TrimSpace(val), err 76 } 77 78 func (r RefSpec) Sha1(c *Client) (Sha1, error) { 79 v, err := r.Value(c) 80 if err != nil { 81 return Sha1{}, err 82 } 83 return Sha1FromString(v) 84 } 85 86 func (r RefSpec) CommitID(c *Client) (CommitID, error) { 87 sha1, err := r.Sha1(c) 88 if err != nil { 89 return CommitID{}, err 90 } 91 switch t := sha1.Type(c); t { 92 case "commit": 93 return CommitID(sha1), nil 94 case "tag": 95 obj, err := c.GetObject(sha1) 96 if err != nil { 97 return CommitID{}, err 98 } 99 content := obj.GetContent() 100 reader := bytes.NewBuffer(content) 101 line, err := reader.ReadBytes('\n') 102 if err != nil { 103 return CommitID{}, err 104 } 105 objid := strings.TrimPrefix(string(line), "object ") 106 objsha, err := CommitIDFromString(objid) 107 if err != nil { 108 return CommitID{}, err 109 } 110 line, err = reader.ReadBytes('\n') 111 if err != nil { 112 return CommitID{}, err 113 } 114 if string(line) != "type commit\n" { 115 return CommitID{}, fmt.Errorf("Tag does not point to a commit: %v", string(line)) 116 } 117 return objsha, nil 118 default: 119 return CommitID{}, fmt.Errorf("Invalid commit type %v", t) 120 } 121 } 122 123 func (r RefSpec) TreeID(c *Client) (TreeID, error) { 124 cmt, err := r.CommitID(c) 125 if err != nil { 126 return TreeID{}, err 127 } 128 return cmt.TreeID(c) 129 } 130 131 // A Branch is a type of RefSpec that lives under refs/heads/ or refs/remotes/heads 132 // Use GetBranch to get a valid branch from a branchname, don't cast from string 133 type Branch RefSpec 134 135 // Implements Stringer on Branch 136 func (b Branch) String() string { 137 return RefSpec(b).String() 138 } 139 140 // Returns a valid Branch object for an existing branch. 141 func GetBranch(c *Client, branchname string) (Branch, error) { 142 if b := Branch("refs/heads/" + branchname); b.Exists(c) { 143 return b, nil 144 } 145 146 // remote branches are branches too! 147 if b := Branch("refs/remotes/" + branchname); b.Exists(c) { 148 return b, nil 149 } 150 return "", InvalidBranch 151 } 152 153 // Returns true if the branch exists under c's GitDir 154 func (b Branch) Exists(c *Client) bool { 155 return c.GitDir.File(File(b)).Exists() 156 } 157 158 // Implements Commitish interface on Branch. 159 func (b Branch) CommitID(c *Client) (CommitID, error) { 160 val, err := RefSpec(b).Value(c) 161 if err != nil { 162 return CommitID{}, err 163 } 164 sha, err := Sha1FromString(val) 165 return CommitID(sha), err 166 } 167 168 // Implements Treeish on Branch. 169 func (b Branch) TreeID(c *Client) (TreeID, error) { 170 cmt, err := b.CommitID(c) 171 if err != nil { 172 return TreeID{}, err 173 } 174 return cmt.TreeID(c) 175 } 176 177 // Returns the branch name, without the refspec portion. 178 func (b Branch) BranchName() string { 179 s := string(b) 180 if strings.HasPrefix(s, "refs/heads/") { 181 return strings.TrimPrefix(s, "refs/heads/") 182 } 183 return strings.TrimPrefix(s, "refs/") 184 } 185 186 // Delete a branch 187 func (b Branch) DeleteBranch(c *Client) error { 188 location := c.GitDir.File(File(b)) 189 err := location.Remove() 190 if err != nil { 191 return err 192 } 193 return nil 194 }