Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'internal/service/repository/bundle/create.go')
-rw-r--r--internal/service/repository/bundle/create.go148
1 files changed, 148 insertions, 0 deletions
diff --git a/internal/service/repository/bundle/create.go b/internal/service/repository/bundle/create.go
new file mode 100644
index 000000000..49b08a5ee
--- /dev/null
+++ b/internal/service/repository/bundle/create.go
@@ -0,0 +1,148 @@
+package bundle
+
+import (
+ "bufio"
+ "bytes"
+ "context"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "gitlab.com/gitlab-org/gitaly/internal/git"
+ "gitlab.com/gitlab-org/gitaly/internal/git/catfile"
+ "gitlab.com/gitlab-org/gitaly/internal/helper"
+ "gitlab.com/gitlab-org/gitaly/internal/safe"
+ "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb"
+)
+
+const (
+ Header = "# gitaly bundle v1"
+)
+
+func Create(ctx context.Context, repo *gitalypb.Repository) ([]byte, *safe.FileWriter, error) {
+ repoPath, err := helper.GetPath(repo)
+ if err != nil {
+ return nil, nil, fmt.Errorf("getting repo path: %w", err)
+ }
+
+ bundleRefsPath := filepath.Join(repoPath, ".gitaly_bundle", "refs")
+ if err = os.MkdirAll(filepath.Dir(bundleRefsPath), 0755); err != nil {
+ return nil, nil, fmt.Errorf("mkdir -p %s: %w", bundleRefsPath, err)
+ }
+
+ var b bytes.Buffer
+
+ if _, err = b.WriteString(Header); err != nil {
+ return nil, nil, fmt.Errorf("writing to buffer: %w", err)
+ }
+ if _, err = b.Write([]byte("\n")); err != nil {
+ return nil, nil, fmt.Errorf("writing to buffer: %w", err)
+ }
+
+ bundleRefsWriter, err := safe.CreateFileWriter(bundleRefsPath)
+ if err != nil {
+ return nil, nil, fmt.Errorf("creating bundle refs writer %w", err)
+ }
+
+ refWriter := io.MultiWriter(bundleRefsWriter, &b)
+
+ cmd, err := git.SafeCmd(ctx, repo, nil, git.SubCmd{
+ Name: "for-each-ref",
+ Flags: []git.Option{git.ValueFlag{
+ Name: "--format",
+ Value: "%(objectname) %(refname)",
+ }},
+ })
+ if err != nil {
+ return nil, nil, fmt.Errorf("creating for-each-ref command: %w", err)
+ }
+
+ var objectIDs bytes.Buffer
+
+ s := bufio.NewScanner(cmd)
+
+ for s.Scan() {
+ line := strings.SplitN(s.Text(), " ", 2)
+ objectIDs.WriteString(line[0])
+ objectIDs.Write([]byte("\n"))
+ refWriter.Write([]byte(s.Text()))
+ refWriter.Write([]byte("\n"))
+ }
+
+ if err = s.Err(); err != nil {
+ return nil, nil, fmt.Errorf("reading output of for-each-ref: %w", err)
+ }
+
+ if err = cmd.Wait(); err != nil {
+ return nil, nil, fmt.Errorf("running for-each-ref: %w", err)
+ }
+
+ if _, err = os.Stat(bundleRefsPath); err == nil {
+ c, err := catfile.New(ctx, repo)
+ if err != nil {
+ return nil, nil, fmt.Errorf("creating catfile batch: %w", err)
+ }
+ defer c.Close()
+
+ f, err := os.Open(bundleRefsPath)
+
+ if err != nil {
+ return nil, nil, fmt.Errorf("opening bundle refs file: %w", err)
+ }
+ defer f.Close()
+
+ s = bufio.NewScanner(f)
+ for s.Scan() {
+ line := s.Text()
+ if line == "" {
+ break
+ }
+ if strings.HasPrefix(line, "#") {
+ continue
+ }
+ lineSplit := strings.SplitN(line, " ", 2)
+
+ _, err := c.Info(lineSplit[0])
+ if catfile.IsNotFound(err) {
+ continue
+ }
+ objectIDs.WriteString(fmt.Sprintf("^%s\n", lineSplit[0]))
+ }
+
+ if err = f.Close(); err != nil {
+ return nil, nil, fmt.Errorf("closing bundle refs file: %w", err)
+ }
+ }
+
+ b.Write([]byte("\n"))
+
+ var counter writerCounter
+ out := io.MultiWriter(&b, &counter)
+
+ cmd, err = git.SafeBareCmd(ctx, git.CmdStream{In: &objectIDs, Out: out}, nil, []git.Option{git.ValueFlag{Name: "-C", Value: repoPath}}, git.SubCmd{
+ Name: "pack-objects",
+ Flags: []git.Option{git.Flag{Name: "--thin"}, git.Flag{Name: "--stdout"}, git.Flag{Name: "--non-empty"}, git.Flag{Name: "--delta-base-offset"}},
+ })
+ if err != nil {
+ return nil, nil, fmt.Errorf("creating pack-objects command: %w", err)
+ }
+
+ if err = cmd.Wait(); err != nil {
+ return nil, nil, fmt.Errorf("running pack-objects command: %w", err)
+ }
+
+ if counter == 0 {
+ return nil, nil, nil
+ }
+
+ return b.Bytes(), bundleRefsWriter, nil
+}
+
+type writerCounter int
+
+func (w *writerCounter) Write(b []byte) (int, error) {
+ *w += writerCounter(len(b))
+ return len(b), nil
+}