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  }