code.gitea.io/gitea@v1.22.3/modules/git/tag.go (about)

     1  // Copyright 2015 The Gogs Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package git
     5  
     6  import (
     7  	"bytes"
     8  	"sort"
     9  
    10  	"code.gitea.io/gitea/modules/util"
    11  )
    12  
    13  // Tag represents a Git tag.
    14  type Tag struct {
    15  	Name      string
    16  	ID        ObjectID
    17  	Object    ObjectID // The id of this commit object
    18  	Type      string
    19  	Tagger    *Signature
    20  	Message   string
    21  	Signature *CommitSignature
    22  }
    23  
    24  // Commit return the commit of the tag reference
    25  func (tag *Tag) Commit(gitRepo *Repository) (*Commit, error) {
    26  	return gitRepo.getCommit(tag.Object)
    27  }
    28  
    29  func parsePayloadSignature(data []byte, messageStart int) (payload, msg, sign string) {
    30  	pos := messageStart
    31  	signStart, signEnd := -1, -1
    32  	for {
    33  		eol := bytes.IndexByte(data[pos:], '\n')
    34  		if eol < 0 {
    35  			break
    36  		}
    37  		line := data[pos : pos+eol]
    38  		signType, hasPrefix := bytes.CutPrefix(line, []byte("-----BEGIN "))
    39  		signType, hasSuffix := bytes.CutSuffix(signType, []byte(" SIGNATURE-----"))
    40  		if hasPrefix && hasSuffix {
    41  			signEndBytes := append([]byte("\n-----END "), signType...)
    42  			signEndBytes = append(signEndBytes, []byte(" SIGNATURE-----")...)
    43  			signEnd = bytes.Index(data[pos:], signEndBytes)
    44  			if signEnd != -1 {
    45  				signStart = pos
    46  				signEnd = pos + signEnd + len(signEndBytes)
    47  			}
    48  		}
    49  		pos += eol + 1
    50  	}
    51  
    52  	if signStart != -1 && signEnd != -1 {
    53  		msgEnd := max(messageStart, signStart-1)
    54  		return string(data[:msgEnd]), string(data[messageStart:msgEnd]), string(data[signStart:signEnd])
    55  	}
    56  	return string(data), string(data[messageStart:]), ""
    57  }
    58  
    59  // Parse commit information from the (uncompressed) raw
    60  // data from the commit object.
    61  // \n\n separate headers from message
    62  func parseTagData(objectFormat ObjectFormat, data []byte) (*Tag, error) {
    63  	tag := new(Tag)
    64  	tag.ID = objectFormat.EmptyObjectID()
    65  	tag.Object = objectFormat.EmptyObjectID()
    66  	tag.Tagger = &Signature{}
    67  
    68  	pos := 0
    69  	for {
    70  		eol := bytes.IndexByte(data[pos:], '\n')
    71  		if eol == -1 {
    72  			break // shouldn't happen, but could just tolerate it
    73  		}
    74  		if eol == 0 {
    75  			pos++
    76  			break // end of headers
    77  		}
    78  		line := data[pos : pos+eol]
    79  		key, val, _ := bytes.Cut(line, []byte(" "))
    80  		switch string(key) {
    81  		case "object":
    82  			id, err := NewIDFromString(string(val))
    83  			if err != nil {
    84  				return nil, err
    85  			}
    86  			tag.Object = id
    87  		case "type":
    88  			tag.Type = string(val) // A commit can have one or more parents
    89  		case "tagger":
    90  			tag.Tagger = parseSignatureFromCommitLine(util.UnsafeBytesToString(val))
    91  		}
    92  		pos += eol + 1
    93  	}
    94  	payload, msg, sign := parsePayloadSignature(data, pos)
    95  	tag.Message = msg
    96  	if len(sign) > 0 {
    97  		tag.Signature = &CommitSignature{Signature: sign, Payload: payload}
    98  	}
    99  	return tag, nil
   100  }
   101  
   102  type tagSorter []*Tag
   103  
   104  func (ts tagSorter) Len() int {
   105  	return len([]*Tag(ts))
   106  }
   107  
   108  func (ts tagSorter) Less(i, j int) bool {
   109  	return []*Tag(ts)[i].Tagger.When.After([]*Tag(ts)[j].Tagger.When)
   110  }
   111  
   112  func (ts tagSorter) Swap(i, j int) {
   113  	[]*Tag(ts)[i], []*Tag(ts)[j] = []*Tag(ts)[j], []*Tag(ts)[i]
   114  }
   115  
   116  // sortTagsByTime
   117  func sortTagsByTime(tags []*Tag) {
   118  	sorter := tagSorter(tags)
   119  	sort.Sort(sorter)
   120  }