aboutsummaryrefslogtreecommitdiff
path: root/internal/testgit/packobjects.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/testgit/packobjects.go')
-rw-r--r--internal/testgit/packobjects.go145
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
+}