// Copyright 2017-2025 WIT.COM Inc. All rights reserved. // Use of this source code is governed by the GPL 3.0 package main import ( "errors" "path/filepath" "slices" "strings" "go.wit.com/lib/env" "go.wit.com/lib/protobuf/gitpb" "go.wit.com/log" ) func searchRemote(remote *gitpb.Stats, ref *gitpb.Stat) *gitpb.Stat { n, found := slices.BinarySearchFunc(remote.Stats, ref, func(a, b *gitpb.Stat) int { return strings.Compare(a.Hash, b.Hash) }) if found { foundref := remote.Stats[n] if ref.Hash != foundref.Hash { log.Info("Something went wrong", ref.Hash, foundref.Hash) return nil } return foundref } log.Printf("searchRemote(nil) len(%d) NOT FOUND: %v\n", remote.Len(), ref) for remoteref := range remote.IterAll() { if remoteref.Hash == ref.Hash { log.Printf("searchRemote(nil) len(%d) FOUND: %v\n", remote.Len(), remoteref) return remoteref } if remoteref.TreeHash == ref.Hash { log.Printf("searchRemote(nil) len(%d) FOUND: %v\n", remote.Len(), remoteref) return remoteref } if remoteref.TagHash == ref.Hash { log.Printf("searchRemote(nil) len(%d) FOUND: %v\n", remote.Len(), remoteref) return remoteref } // log.Printf("searchRemote(nil) len(%d) NO MATCH: %s %s %s\n", remote.Len(), remoteref.Hash, remoteref.TreeHash, "nope") } log.Printf("searchRemote(nil) len(%d) NOT FOUND: %v\n", remote.Len(), ref) return nil } // processes stats.Tags in the remote refs .pb // tries to find the tags in the local refs.pb file func doMapTags(r *gitpb.Repo) (*gitpb.Stats, error) { var allerr error stats, err := r.LoadRefs() if err != nil { return stats, err } var worked bool // look through the Tags for _, rmote := range r.Config.Remotes { remotestats, err := r.LoadRemote(rmote.Name) if err != nil { return stats, err } if remotestats.Len() == 0 { // need to init remote stats return stats, errors.New("run dev stats --update-remote") } for ref := range remotestats.IterAll() { if ref.Tags == "" { continue } line := strings.TrimSpace(ref.Tags) line = strings.TrimPrefix(line, "(") line = strings.TrimSuffix(line, ")") for _, part := range strings.Split(line, ",") { var found bool var tag string part = strings.TrimSpace(part) parts := strings.Split(part, " ") if (len(parts) == 2) && (parts[0] == "tag:") { tag = filepath.Join("refs/tags", parts[1]) } else if len(parts) == 1 { tag = parts[0] if strings.HasPrefix(tag, rmote.Name+"/") { tag = filepath.Join("refs/remotes", tag) } else { tag = filepath.Join("refs/heads", tag) } } else if len(parts) == 3 { if parts[0] == "HEAD" { tag = filepath.Join("refs/heads", parts[2]) } } else { log.Printf("hash(%s) could not find tag: (%v)\n", ref.Hash, parts) } for tagref := range stats.IterAll() { if tagref.Name == tag { found = true if tagref.Hash == "" { log.Printf("Found taghash(%s) hash(%s) has tags: %s (%s)\n", tagref.TagHash, ref.Hash, part, tag) tagref.Hash = ref.Hash worked = true } else { if tagref.Hash != ref.Hash { log.Printf("EVEN WEIRDER ERROR: taghash(%s) hash(%s) has tags: %s (%s)\n", tagref.TagHash, ref.Hash, part, tag) } } } } if !found { s := log.Sprintf("ref.hash(%s) could not find tag(%s) in remote. part=(%s)", ref.Hash, tag, part) log.Info(s) allerr = errors.New(s) } } } } if worked { stats.Save() return stats, errors.New("New tags found!") } return stats, allerr } func remoteDups(all *gitpb.Stats) error { var dups []*gitpb.Stat last := new(gitpb.Stat) for ref := range all.IterAll() { if ref.Hash == last.Hash { dups = append(dups, ref) } else { last = ref } } s := log.Sprintf("found %d dups", len(dups)) log.Info(s, dups) if len(dups) == 0 { return nil } return errors.New(s) } func makeAllGitStats(r *gitpb.Repo) error { var err error // make all the remote files before the local files for _, rmote := range r.Config.Remotes { morestats, err := r.MakeRemote(rmote.Name) if err != nil { return errors.Join(err, errors.New("MakeRemote() failed")) } if env.True("resort") { morestats.SaveByHash() return errors.New("stats should have been resorted and saved") } remoterefs, err := r.MakeRemoteRefs(rmote.Name) if err != nil { return errors.Join(err, errors.New("MakeRemoteRefs() failed")) } if env.True("resort") { remoterefs.SaveByHash() return errors.New("stats should have been resorted and saved") } } // make the local refs pb stats, err := r.LoadRefs() if err != nil { return errors.Join(err, errors.New("r.LoadRefs() failed")) } if env.True("resort") { stats.SaveByHash() log.Info("stats should have been resorted and saved") return nil } err = r.UpdateRefs(stats) if err != nil { return errors.Join(err, errors.New("r.UpdateRefs() failed")) } return nil } func doReload(r *gitpb.Repo) error { err := makeAllGitStats(r) if err != nil { return err } stats, err := doMapTags(r) if err != nil { return errors.Join(err, errors.New("doMapTags() failed")) } // * remotes duplicates // * sets fields that might have not been set before // this probably should go away once the other code works for _, rmote := range r.Config.Remotes { remotestats, err := r.LoadRemote(rmote.Name) if err != nil { return err } var save bool remoteDups(remotestats) for ref := range stats.IterAll() { found := searchRemote(remotestats, ref) if found == nil { log.Printf("len(%d) NOT FOUND: %v\n", remotestats.Len(), ref) return nil } if ref.SanitizedSubject != found.SanitizedSubject { log.Info("subject", ref.SanitizedSubject, found.SanitizedSubject) ref.SanitizedSubject = found.SanitizedSubject save = true } if (ref.AuthorTime == nil) || (ref.AuthorTime.AsTime() != found.AuthorTime.AsTime()) { log.Info("author time", ref.AuthorTime, found.AuthorTime) ref.AuthorTime = found.AuthorTime save = true } if (ref.CommitTime == nil) || (ref.CommitTime.AsTime() != found.CommitTime.AsTime()) { log.Info("commit time", ref.CommitTime, found.CommitTime) ref.CommitTime = found.CommitTime save = true } } if save { stats.Save() return errors.New("some things were updated") } } /* sort.Slice(stats, func(i, j int) bool { return gitpb. }) */ // sorts refs by version. tries, but the sort doesn't quite work yet for some reason // saves the sorted file wasSorted := stats.SortVersion() if !wasSorted { newstats := gitpb.NewStats() for _, ref := range stats.Stats { newstats.Clone(ref) } newstats.Filename = stats.Filename newstats.Save() } footer := stats.PrintTableLimit(2) log.Info("doReload() footer:", footer) /* footer := stats.PrintTable() log.Info("doReload() footer:", footer) */ return nil }