Mirror of @tangled.org/core. Running on a Raspberry Pi Zero 2
at master 147 lines 3.2 kB view raw
1package git 2 3import ( 4 "fmt" 5 "strconv" 6 "strings" 7 "time" 8 9 "github.com/go-git/go-git/v5/plumbing" 10 "github.com/go-git/go-git/v5/plumbing/object" 11) 12 13type TagsOptions struct { 14 Limit int 15 Offset int 16 Pattern string 17} 18 19func (g *GitRepo) Tags(opts *TagsOptions) ([]object.Tag, error) { 20 if opts == nil { 21 opts = &TagsOptions{} 22 } 23 24 if opts.Pattern == "" { 25 opts.Pattern = "refs/tags" 26 } 27 28 fields := []string{ 29 "refname:short", 30 "objectname", 31 "objecttype", 32 "*objectname", 33 "*objecttype", 34 "taggername", 35 "taggeremail", 36 "taggerdate:unix", 37 "contents:subject", 38 "contents:body", 39 "contents:signature", 40 } 41 42 var outFormat strings.Builder 43 outFormat.WriteString("--format=") 44 for i, f := range fields { 45 if i != 0 { 46 outFormat.WriteString(fieldSeparator) 47 } 48 fmt.Fprintf(&outFormat, "%%(%s)", f) 49 } 50 outFormat.WriteString("") 51 outFormat.WriteString(recordSeparator) 52 53 args := []string{outFormat.String(), "--sort=-creatordate"} 54 55 // only add the count if the limit is a non-zero value, 56 // if it is zero, get as many tags as we can 57 if opts.Limit > 0 { 58 args = append(args, fmt.Sprintf("--count=%d", opts.Offset+opts.Limit)) 59 } 60 61 args = append(args, opts.Pattern) 62 63 output, err := g.forEachRef(args...) 64 if err != nil { 65 return nil, fmt.Errorf("failed to get tags: %w", err) 66 } 67 68 records := strings.Split(strings.TrimSpace(string(output)), recordSeparator) 69 if len(records) == 1 && records[0] == "" { 70 return nil, nil 71 } 72 73 startIdx := opts.Offset 74 if startIdx >= len(records) { 75 return nil, nil 76 } 77 78 endIdx := len(records) 79 if opts.Limit > 0 { 80 endIdx = min(startIdx+opts.Limit, len(records)) 81 } 82 83 records = records[startIdx:endIdx] 84 tags := make([]object.Tag, 0, len(records)) 85 86 for _, line := range records { 87 parts := strings.SplitN(strings.TrimSpace(line), fieldSeparator, len(fields)) 88 if len(parts) < 6 { 89 continue 90 } 91 92 tagName := parts[0] 93 objectHash := parts[1] 94 objectType := parts[2] 95 targetHash := parts[3] // dereferenced object hash (empty for lightweight tags) 96 // targetType := parts[4] // dereferenced object type (empty for lightweight tags) 97 taggerName := parts[5] 98 taggerEmail := parts[6] 99 taggerDate := parts[7] 100 subject := parts[8] 101 body := parts[9] 102 signature := parts[10] 103 104 // combine subject and body for the message 105 var message string 106 if subject != "" && body != "" { 107 message = subject + "\n\n" + body 108 } else if subject != "" { 109 message = subject 110 } else { 111 message = body 112 } 113 114 // parse creation time 115 var createdAt time.Time 116 if unix, err := strconv.ParseInt(taggerDate, 10, 64); err == nil { 117 createdAt = time.Unix(unix, 0) 118 } 119 120 // parse object type 121 typ, err := plumbing.ParseObjectType(objectType) 122 if err != nil { 123 return nil, err 124 } 125 126 // strip email separators 127 taggerEmail = strings.TrimSuffix(strings.TrimPrefix(taggerEmail, "<"), ">") 128 129 tag := object.Tag{ 130 Hash: plumbing.NewHash(objectHash), 131 Name: tagName, 132 Tagger: object.Signature{ 133 Name: taggerName, 134 Email: taggerEmail, 135 When: createdAt, 136 }, 137 Message: message, 138 PGPSignature: signature, 139 TargetType: typ, 140 Target: plumbing.NewHash(targetHash), 141 } 142 143 tags = append(tags, tag) 144 } 145 146 return tags, nil 147}