test server: emit HEAD symref-target under v2 ls-refs symrefs · Entire

test server: emit HEAD symref-target under v2 ls-refs symrefs

cc9e564→main·

Soph·2mo ago·1 file·+40 added/-0 removed

The v2 ls-refs handler ignored the "symrefs" argument and never emitted HEAD with a symref-target attribute, so source-side HEAD info captured via v2 was always empty in tests. Real git emits

HEAD symref-target:\n

when the client requests "symrefs" and a ref-prefix would include HEAD (or no prefixes are given). Add that to the test server with two small helpers: coversHead checks prefix coverage, lsRefsHeadLine formats the pkt-line by resolving HEAD's symref target through the repo.

Foundation for upcoming work on surfacing source HEAD.

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

Sessions

66bf19cf9e23View transcript

Changes

1

3770 unmodified lines

3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
4 unmodified lines

3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
240 unmodified lines

4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075

3770 unmodified lines

func (s *smartHTTPRepoServer) handleUploadPackV2LSRefs(w http.ResponseWriter, req v2TestCommandRequest, body []byte) {
    prefixes := make([]string, 0, len(req.Args))
    wantSymrefs := false
    for _, arg := range req.Args {
        if strings.HasPrefix(arg, "ref-prefix ") {
            prefixes = append(prefixes, strings.TrimPrefix(arg, "ref-prefix "))
        } else if arg == "symrefs" {
            wantSymrefs = true
        }
    }

4 unmodified lines

}

var buf bytes.Buffer
// Real git emits HEAD with symref-target attribute under "symrefs", as
// long as a ref-prefix covers HEAD (or no prefixes are given).
if wantSymrefs && coversHead(prefixes) {
    if line, ok := s.lsRefsHeadLine(); ok {
        if _, err := pktline.WriteString(&buf, line); err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
    }
}
for _, ref := range refs {
    if _, err := pktline.WriteString(&buf, ref.Hash().String()+" "+ref.Name().String()+"\n"); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
240 unmodified lines

strings.Contains(msg, "connection reset by peer")
}

// coversHead reports whether the ls-refs prefix list would include HEAD.
func coversHead(prefixes []string) bool {
    if len(prefixes) == 0 {
        return true
    }
    for _, p := range prefixes {
        if p == "HEAD" {
            return true
        }
    }
    return false
}

// lsRefsHeadLine formats a v2 ls-refs HEAD line with the symref-target
// attribute, matching what real git advertises under "symrefs".
func (s *smartHTTPRepoServer) lsRefsHeadLine() (string, bool) {
    head, err := s.repo.Storer.Reference(plumbing.HEAD)
    if err != nil || head.Type() != plumbing.SymbolicReference {
        return "", false
    }
    resolved, err := s.repo.Reference(head.Target(), true)
    if err != nil {
        return "", false
    }
    return fmt.Sprintf("%s HEAD symref-target:%s\n", resolved.Hash(), head.Target()), true
}

func (s *smartHTTPRepoServer) refsMatchingPrefixes(prefixes []string) ([]*plumbing.Reference, error) {
    iter, err := s.repo.Storer.IterReferences()
    if err != nil {