internal/git/gitcmd/reference.go (64 lines of code) (raw):
package gitcmd
import (
"bufio"
"bytes"
"context"
"errors"
"fmt"
"strings"
"gitlab.com/gitlab-org/gitaly/v16/internal/git"
)
// GetReferencesConfig is configuration that can be passed to GetReferences in order to change its default behaviour.
type GetReferencesConfig struct {
// Patterns limits the returned references to only those which match the given pattern. If no patterns are given
// then all references will be returned.
Patterns []string
// Limit limits
Limit uint
}
// GetReferences enumerates references in the given repository. By default, it returns all references that exist in the
// repository. This behaviour can be tweaked via the `GetReferencesConfig`.
func GetReferences(ctx context.Context, repoExecutor RepositoryExecutor, cfg GetReferencesConfig) ([]git.Reference, error) {
flags := []Option{Flag{Name: "--format=%(refname)%00%(objectname)%00%(symref)"}}
if cfg.Limit > 0 {
flags = append(flags, Flag{Name: fmt.Sprintf("--count=%d", cfg.Limit)})
}
cmd, err := repoExecutor.Exec(ctx, Command{
Name: "for-each-ref",
Flags: flags,
Args: cfg.Patterns,
}, WithSetupStdout())
if err != nil {
return nil, err
}
scanner := bufio.NewScanner(cmd)
var refs []git.Reference
for scanner.Scan() {
line := bytes.SplitN(scanner.Bytes(), []byte{0}, 3)
if len(line) != 3 {
return nil, errors.New("unexpected reference format")
}
if len(line[2]) == 0 {
refs = append(refs, git.NewReference(git.ReferenceName(line[0]), git.ObjectID(line[1])))
} else {
refs = append(refs, git.NewSymbolicReference(git.ReferenceName(line[0]), git.ReferenceName(line[1])))
}
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("reading standard input: %w", err)
}
if err := cmd.Wait(); err != nil {
return nil, err
}
return refs, nil
}
// GetSymbolicRef reads the symbolic reference.
func GetSymbolicRef(ctx context.Context, repoExecutor RepositoryExecutor, refname git.ReferenceName) (git.Reference, error) {
var stdout strings.Builder
if err := repoExecutor.ExecAndWait(ctx, Command{
Name: "symbolic-ref",
Args: []string{string(refname)},
}, WithDisabledHooks(), WithStdout(&stdout)); err != nil {
return git.Reference{}, err
}
symref, trailing, ok := strings.Cut(stdout.String(), "\n")
if !ok {
return git.Reference{}, fmt.Errorf("expected symbolic reference to be terminated by newline")
} else if len(trailing) > 0 {
return git.Reference{}, fmt.Errorf("symbolic reference has trailing data")
}
return git.NewSymbolicReference(refname, git.ReferenceName(symref)), nil
}