in internal/git/gitpipe/revision.go [204:371]
func Revlist(
ctx context.Context,
repo *localrepo.Repo,
revisions []string,
options ...RevlistOption,
) RevisionIterator {
var cfg revlistConfig
for _, option := range options {
option(&cfg)
}
resultChan := make(chan RevisionResult)
go func() {
defer close(resultChan)
flags := []gitcmd.Option{
gitcmd.Flag{Name: "--stdin"},
}
if cfg.objects {
flags = append(flags,
gitcmd.Flag{Name: "--in-commit-order"},
gitcmd.Flag{Name: "--objects"},
gitcmd.Flag{Name: "--object-names"},
)
}
if cfg.blobLimit > 0 {
flags = append(flags, gitcmd.Flag{
Name: fmt.Sprintf("--filter=blob:limit=%d", cfg.blobLimit),
})
}
if cfg.objectType != "" {
flags = append(flags,
gitcmd.Flag{Name: fmt.Sprintf("--filter=object:type=%s", cfg.objectType)},
gitcmd.Flag{Name: "--filter-provided-objects"},
)
}
switch cfg.order {
case OrderNone:
// Default order, nothing to do.
case OrderTopo:
flags = append(flags, gitcmd.Flag{Name: "--topo-order"})
case OrderDate:
flags = append(flags, gitcmd.Flag{Name: "--date-order"})
}
if cfg.reverse {
flags = append(flags, gitcmd.Flag{Name: "--reverse"})
}
if cfg.maxParents > 0 {
flags = append(flags, gitcmd.Flag{
Name: fmt.Sprintf("--max-parents=%d", cfg.maxParents),
},
)
}
if cfg.disabledWalk {
flags = append(flags, gitcmd.Flag{Name: "--no-walk"})
}
if cfg.firstParent {
flags = append(flags, gitcmd.Flag{Name: "--first-parent"})
}
if !cfg.before.IsZero() {
flags = append(flags, gitcmd.Flag{
Name: fmt.Sprintf("--before=%s", cfg.before.String()),
})
}
if !cfg.after.IsZero() {
flags = append(flags, gitcmd.Flag{
Name: fmt.Sprintf("--after=%s", cfg.after.String()),
})
}
if len(cfg.author) > 0 {
flags = append(flags, gitcmd.Flag{
Name: fmt.Sprintf("--author=%s", string(cfg.author)),
})
}
if cfg.regexIgnoreCase {
flags = append(flags, gitcmd.Flag{Name: "--regexp-ignore-case"})
}
if len(cfg.commitMessagePatterns) > 0 {
for _, pattern := range cfg.commitMessagePatterns {
flags = append(flags, gitcmd.Flag{Name: fmt.Sprintf("--grep=%s", pattern)})
}
}
if cfg.skip > 0 {
flags = append(flags, gitcmd.Flag{
Name: fmt.Sprintf("--skip=%d", cfg.skip),
},
)
}
var stderr strings.Builder
revlist, err := repo.Exec(ctx,
gitcmd.Command{
Name: "rev-list",
Flags: flags,
},
gitcmd.WithStderr(&stderr),
gitcmd.WithSetupStdout(),
gitcmd.WithStdin(strings.NewReader(strings.Join(revisions, "\n"))),
)
if err != nil {
sendRevisionResult(ctx, resultChan, RevisionResult{
err: fmt.Errorf("rev-list: %w, stderr: %q", err, stderr.String()),
})
return
}
var execErrors error
defer func() {
if err := revlist.Wait(); err != nil {
execErrors = errors.Join(execErrors, fmt.Errorf("rev-list pipeline command: %w, stderr: %q", err, stderr.String()))
}
if execErrors != nil {
sendRevisionResult(ctx, resultChan, RevisionResult{
err: execErrors,
})
}
}()
scanner := bufio.NewScanner(revlist)
for scanner.Scan() {
// We need to copy the line here because we'll hand it over to the caller
// asynchronously, and the next call to `Scan()` will overwrite the buffer.
line := make([]byte, len(scanner.Bytes()))
copy(line, scanner.Bytes())
oid, name, ok := bytes.Cut(line, []byte{' '})
result := RevisionResult{
OID: git.ObjectID(oid),
}
if ok && len(name) > 0 {
result.ObjectName = name
}
if cfg.skipResult != nil && cfg.skipResult(&result) {
continue
}
if isDone := sendRevisionResult(ctx, resultChan, result); isDone {
return
}
}
if err := scanner.Err(); err != nil {
execErrors = errors.Join(execErrors, fmt.Errorf("scanning rev-list output: %w", err))
}
}()
return &revisionIterator{
ctx: ctx,
ch: resultChan,
}
}