Monorepo for Tangled
at punchcard-prefs 132 lines 2.8 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", 38 } 39 40 var outFormat strings.Builder 41 outFormat.WriteString("--format=") 42 for i, f := range fields { 43 if i != 0 { 44 outFormat.WriteString(fieldSeparator) 45 } 46 fmt.Fprintf(&outFormat, "%%(%s)", f) 47 } 48 outFormat.WriteString("") 49 outFormat.WriteString(recordSeparator) 50 51 args := []string{outFormat.String(), "--sort=-creatordate"} 52 53 // only add the count if the limit is a non-zero value, 54 // if it is zero, get as many tags as we can 55 if opts.Limit > 0 { 56 args = append(args, fmt.Sprintf("--count=%d", opts.Offset+opts.Limit)) 57 } 58 59 args = append(args, opts.Pattern) 60 61 output, err := g.forEachRef(args...) 62 if err != nil { 63 return nil, fmt.Errorf("failed to get tags: %w", err) 64 } 65 66 records := strings.Split(strings.TrimSpace(string(output)), recordSeparator) 67 if len(records) == 1 && records[0] == "" { 68 return nil, nil 69 } 70 71 startIdx := opts.Offset 72 if startIdx >= len(records) { 73 return nil, nil 74 } 75 76 endIdx := len(records) 77 if opts.Limit > 0 { 78 endIdx = min(startIdx+opts.Limit, len(records)) 79 } 80 81 records = records[startIdx:endIdx] 82 tags := make([]object.Tag, 0, len(records)) 83 84 for _, line := range records { 85 parts := strings.SplitN(strings.TrimSpace(line), fieldSeparator, len(fields)) 86 if len(parts) < 6 { 87 continue 88 } 89 90 tagName := parts[0] 91 objectHash := parts[1] 92 objectType := parts[2] 93 targetHash := parts[3] // dereferenced object hash (empty for lightweight tags) 94 // targetType := parts[4] // dereferenced object type (empty for lightweight tags) 95 taggerName := parts[5] 96 taggerEmail := parts[6] 97 taggerDate := parts[7] 98 message := parts[8] 99 100 // parse creation time 101 var createdAt time.Time 102 if unix, err := strconv.ParseInt(taggerDate, 10, 64); err == nil { 103 createdAt = time.Unix(unix, 0) 104 } 105 106 // parse object type 107 typ, err := plumbing.ParseObjectType(objectType) 108 if err != nil { 109 return nil, err 110 } 111 112 // strip email separators 113 taggerEmail = strings.TrimSuffix(strings.TrimPrefix(taggerEmail, "<"), ">") 114 115 tag := object.Tag{ 116 Hash: plumbing.NewHash(objectHash), 117 Name: tagName, 118 Tagger: object.Signature{ 119 Name: taggerName, 120 Email: taggerEmail, 121 When: createdAt, 122 }, 123 Message: message, 124 TargetType: typ, 125 Target: plumbing.NewHash(targetHash), 126 } 127 128 tags = append(tags, tag) 129 } 130 131 return tags, nil 132}