diff options
| author | 2026-06-12 15:45:49 +0000 | |
|---|---|---|
| committer | 2026-06-12 15:45:49 +0000 | |
| commit | c0880c8ac16b291eab28c3f8a761fc326523b6ea (patch) | |
| tree | b6991601bcf10268f174a7b983ca4a54c1b6d135 /internal/testgit | |
| parent | object/store/packed: Use the new format/packfile header parser (diff) | |
internal/testgit: Enrich PackObjects
Diffstat (limited to 'internal/testgit')
| -rw-r--r-- | internal/testgit/packobjects.go | 124 |
1 files changed, 111 insertions, 13 deletions
diff --git a/internal/testgit/packobjects.go b/internal/testgit/packobjects.go index 9f56eef0..62f86963 100644 --- a/internal/testgit/packobjects.go +++ b/internal/testgit/packobjects.go @@ -2,7 +2,8 @@ package testgit import ( "bytes" - "iter" + "errors" + "fmt" "path/filepath" "strings" "testing" @@ -10,38 +11,135 @@ import ( "lindenii.org/go/furgit/object/id" ) -// PackObjectsOptions controls one pack-objects invocation. +// ErrInvalidPackObjectsOptions reports an inconsistent +// combination of pack-objects options. +var ErrInvalidPackObjectsOptions = errors.New("internal/testgit: invalid pack-objects options") + +// PackObjectsOptions controls one on-disk pack-objects invocation. type PackObjectsOptions struct { // RevIndex requests writing a .rev reverse index alongside the pack. RevIndex bool + + // Revs changes how the input is interpreted. + // + // When false, each include is one object name, + // and exactly those objects are packed. + // + // When true, each include is a revision argument, + // and pack-objects packs the closure of objects + // reachable from the includes but not from the excludes, + // the same walk git rev-list --objects performs. + Revs bool + + // Exclude lists objects to omit by reachability, + // fed as "^<oid>" revision arguments. + // A non-nil Exclude requires Revs. + Exclude []id.ObjectID } -// PackObjects packs the supplied objects with git pack-objects +// PackObjects packs the include objects with git pack-objects // into a temporary directory, // and returns the artifact path prefix "<dir>/pack-<hash>", // to which ".pack", ".idx", and ".rev" suffixes apply. -func (repo *Repo) PackObjects(tb testing.TB, oids iter.Seq[id.ObjectID], opts PackObjectsOptions) (string, error) { +func (repo *Repo) PackObjects(tb testing.TB, include []id.ObjectID, opts PackObjectsOptions) (string, error) { tb.Helper() - dir := tb.TempDir() - - var stdin bytes.Buffer - for oid := range oids { - stdin.WriteString(oid.String()) - stdin.WriteByte('\n') + if opts.Exclude != nil && !opts.Revs { + return "", fmt.Errorf("%w: Exclude requires Revs", ErrInvalidPackObjectsOptions) } + dir := tb.TempDir() + revIndex := "false" if opts.RevIndex { revIndex = "true" } - out, err := repo.run(tb, &stdin, - "git", "-c", "pack.writeReverseIndex="+revIndex, - "pack-objects", "--end-of-options", filepath.Join(dir, "pack")) + args := []string{"-c", "pack.writeReverseIndex=" + revIndex, "pack-objects"} + if opts.Revs { + args = append(args, "--revs") + } + + args = append(args, "--end-of-options", filepath.Join(dir, "pack")) + + out, err := repo.run(tb, packObjectsStdin(include, opts.Exclude), "git", args...) if err != nil { return "", err } return filepath.Join(dir, "pack-"+strings.TrimSpace(string(out))), nil } + +// PackObjectsStdoutOptions controls one streamed pack-objects invocation. +type PackObjectsStdoutOptions struct { + // Revs changes how the input is interpreted. + // + // When false, each include is one object name, + // and exactly those objects are packed. + // + // When true, each include is a revision argument, + // and pack-objects packs the closure of objects + // reachable from the includes but not from the excludes, + // the same walk git rev-list --objects performs. + Revs bool + + // Thin omits excluded base objects from the pack, + // producing a thin pack that must be completed before use. + // Thin requires Revs. + Thin bool + + // Exclude lists objects to omit by reachability, + // fed as "^<oid>" revision arguments. + // A non-nil Exclude requires Revs. + Exclude []id.ObjectID +} + +// PackObjectsStdout packs the include objects with git pack-objects +// and returns the pack stream written to standard output. +func (repo *Repo) PackObjectsStdout(tb testing.TB, include []id.ObjectID, opts PackObjectsStdoutOptions) ([]byte, error) { + tb.Helper() + + if opts.Exclude != nil && !opts.Revs { + return nil, fmt.Errorf("%w: Exclude requires Revs", ErrInvalidPackObjectsOptions) + } + + if opts.Thin && !opts.Revs { + return nil, fmt.Errorf("%w: Thin requires Revs", ErrInvalidPackObjectsOptions) + } + + args := []string{"pack-objects", "--stdout"} + if opts.Revs { + args = append(args, "--revs") + } + + if opts.Thin { + args = append(args, "--thin") + } + + out, err := repo.run(tb, packObjectsStdin(include, opts.Exclude), "git", args...) + if err != nil { + return nil, err + } + + return out, nil +} + +// packObjectsStdin builds the pack-objects standard input: +// the include object IDs, +// followed by the exclude object IDs as "^<oid>" lines. +func packObjectsStdin(include, exclude []id.ObjectID) *bytes.Buffer { + var stdin bytes.Buffer + + for _, oid := range include { + stdin.WriteString(oid.String()) + stdin.WriteByte('\n') + } + + for _, oid := range exclude { + stdin.WriteByte('^') + stdin.WriteString(oid.String()) + stdin.WriteByte('\n') + } + + return &stdin +} |
