knotserver/git: refactor GitRepo.Tags to use git for-each-ref #309

merged
opened by oppi.li targeting master from push-rqxyyurmpkps
Changed files
+118 -44
knotserver
-33
knotserver/git/git.go
··· 264 return io.ReadAll(reader) 265 } 266 267 - func (g *GitRepo) Tags() ([]*TagReference, error) { 268 - iter, err := g.r.Tags() 269 - if err != nil { 270 - return nil, fmt.Errorf("tag objects: %w", err) 271 - } 272 - 273 - tags := make([]*TagReference, 0) 274 - 275 - if err := iter.ForEach(func(ref *plumbing.Reference) error { 276 - obj, err := g.r.TagObject(ref.Hash()) 277 - switch err { 278 - case nil: 279 - tags = append(tags, &TagReference{ 280 - ref: ref, 281 - tag: obj, 282 - }) 283 - case plumbing.ErrObjectNotFound: 284 - tags = append(tags, &TagReference{ 285 - ref: ref, 286 - }) 287 - default: 288 - return err 289 - } 290 - return nil 291 - }); err != nil { 292 - return nil, err 293 - } 294 - 295 - tagList := &TagList{r: g.r, refs: tags} 296 - sort.Sort(tagList) 297 - return tags, nil 298 - } 299 - 300 func (g *GitRepo) Branches() ([]types.Branch, error) { 301 bi, err := g.r.Branches() 302 if err != nil {
··· 264 return io.ReadAll(reader) 265 } 266 267 func (g *GitRepo) Branches() ([]types.Branch, error) { 268 bi, err := g.r.Branches() 269 if err != nil {
+99
knotserver/git/tag.go
···
··· 1 + package git 2 + 3 + import ( 4 + "fmt" 5 + "slices" 6 + "strconv" 7 + "strings" 8 + "time" 9 + 10 + "github.com/go-git/go-git/v5/plumbing" 11 + "github.com/go-git/go-git/v5/plumbing/object" 12 + ) 13 + 14 + func (g *GitRepo) Tags() ([]object.Tag, error) { 15 + fields := []string{ 16 + "refname:short", 17 + "objectname", 18 + "objecttype", 19 + "*objectname", 20 + "*objecttype", 21 + "taggername", 22 + "taggeremail", 23 + "taggerdate:unix", 24 + "contents", 25 + } 26 + 27 + var outFormat strings.Builder 28 + outFormat.WriteString("--format=") 29 + for i, f := range fields { 30 + if i != 0 { 31 + outFormat.WriteString(fieldSeparator) 32 + } 33 + outFormat.WriteString(fmt.Sprintf("%%(%s)", f)) 34 + } 35 + outFormat.WriteString("") 36 + outFormat.WriteString(recordSeparator) 37 + 38 + output, err := g.forEachRef(outFormat.String(), "refs/tags") 39 + if err != nil { 40 + return nil, fmt.Errorf("failed to get tags: %w", err) 41 + } 42 + 43 + records := strings.Split(strings.TrimSpace(string(output)), recordSeparator) 44 + if len(records) == 1 && records[0] == "" { 45 + return nil, nil 46 + } 47 + 48 + tags := make([]object.Tag, 0, len(records)) 49 + 50 + for _, line := range records { 51 + parts := strings.SplitN(strings.TrimSpace(line), fieldSeparator, len(fields)) 52 + if len(parts) < 6 { 53 + continue 54 + } 55 + 56 + tagName := parts[0] 57 + objectHash := parts[1] 58 + objectType := parts[2] 59 + targetHash := parts[3] // dereferenced object hash (empty for lightweight tags) 60 + // targetType := parts[4] // dereferenced object type (empty for lightweight tags) 61 + taggerName := parts[5] 62 + taggerEmail := parts[6] 63 + taggerDate := parts[7] 64 + message := parts[8] 65 + 66 + // parse creation time 67 + var createdAt time.Time 68 + if unix, err := strconv.ParseInt(taggerDate, 10, 64); err == nil { 69 + createdAt = time.Unix(unix, 0) 70 + } 71 + 72 + // parse object type 73 + typ, err := plumbing.ParseObjectType(objectType) 74 + if err != nil { 75 + return nil, err 76 + } 77 + 78 + // strip email separators 79 + taggerEmail = strings.TrimSuffix(strings.TrimPrefix(taggerEmail, "<"), ">") 80 + 81 + tag := object.Tag{ 82 + Hash: plumbing.NewHash(objectHash), 83 + Name: tagName, 84 + Tagger: object.Signature{ 85 + Name: taggerName, 86 + Email: taggerEmail, 87 + When: createdAt, 88 + }, 89 + Message: message, 90 + TargetType: typ, 91 + Target: plumbing.NewHash(targetHash), 92 + } 93 + 94 + tags = append(tags, tag) 95 + } 96 + 97 + slices.Reverse(tags) 98 + return tags, nil 99 + }
+19 -11
knotserver/routes.go
··· 96 total int 97 branches []types.Branch 98 files []types.NiceTree 99 - tags []*git.TagReference 100 ) 101 102 var wg sync.WaitGroup ··· 169 170 rtags := []*types.TagReference{} 171 for _, tag := range tags { 172 tr := types.TagReference{ 173 - Tag: tag.TagObject(), 174 } 175 176 tr.Reference = types.Reference{ 177 - Name: tag.Name(), 178 - Hash: tag.Hash().String(), 179 } 180 181 - if tag.Message() != "" { 182 - tr.Message = tag.Message() 183 } 184 185 rtags = append(rtags, &tr) ··· 488 489 rtags := []*types.TagReference{} 490 for _, tag := range tags { 491 tr := types.TagReference{ 492 - Tag: tag.TagObject(), 493 } 494 495 tr.Reference = types.Reference{ 496 - Name: tag.Name(), 497 - Hash: tag.Hash().String(), 498 } 499 500 - if tag.Message() != "" { 501 - tr.Message = tag.Message() 502 } 503 504 rtags = append(rtags, &tr)
··· 96 total int 97 branches []types.Branch 98 files []types.NiceTree 99 + tags []object.Tag 100 ) 101 102 var wg sync.WaitGroup ··· 169 170 rtags := []*types.TagReference{} 171 for _, tag := range tags { 172 + var target *object.Tag 173 + if tag.Target != plumbing.ZeroHash { 174 + target = &tag 175 + } 176 tr := types.TagReference{ 177 + Tag: target, 178 } 179 180 tr.Reference = types.Reference{ 181 + Name: tag.Name, 182 + Hash: tag.Hash.String(), 183 } 184 185 + if tag.Message != "" { 186 + tr.Message = tag.Message 187 } 188 189 rtags = append(rtags, &tr) ··· 492 493 rtags := []*types.TagReference{} 494 for _, tag := range tags { 495 + var target *object.Tag 496 + if tag.Target != plumbing.ZeroHash { 497 + target = &tag 498 + } 499 tr := types.TagReference{ 500 + Tag: target, 501 } 502 503 tr.Reference = types.Reference{ 504 + Name: tag.Name, 505 + Hash: tag.Hash.String(), 506 } 507 508 + if tag.Message != "" { 509 + tr.Message = tag.Message 510 } 511 512 rtags = append(rtags, &tr)