diff options
Diffstat (limited to 'internal/testgit/packobjects.go')
| -rw-r--r-- | internal/testgit/packobjects.go | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/internal/testgit/packobjects.go b/internal/testgit/packobjects.go new file mode 100644 index 00000000..62f86963 --- /dev/null +++ b/internal/testgit/packobjects.go @@ -0,0 +1,145 @@ +package testgit + +import ( + "bytes" + "errors" + "fmt" + "path/filepath" + "strings" + "testing" + + "lindenii.org/go/furgit/object/id" +) + +// 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 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, include []id.ObjectID, opts PackObjectsOptions) (string, error) { + tb.Helper() + + if opts.Exclude != nil && !opts.Revs { + return "", fmt.Errorf("%w: Exclude requires Revs", ErrInvalidPackObjectsOptions) + } + + dir := tb.TempDir() + + revIndex := "false" + if opts.RevIndex { + revIndex = "true" + } + + 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 +} |
