File: create.go

package info (click to toggle)
gitlab-shell 14.35.0%2Bds1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 23,652 kB
  • sloc: ruby: 1,129; makefile: 583; sql: 391; sh: 384
file content (107 lines) | stat: -rw-r--r-- 3,578 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package objectpool

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"os"

	"gitlab.com/gitlab-org/gitaly/v16/internal/git"
	"gitlab.com/gitlab-org/gitaly/v16/internal/git/catfile"
	"gitlab.com/gitlab-org/gitaly/v16/internal/git/housekeeping"
	"gitlab.com/gitlab-org/gitaly/v16/internal/git/localrepo"
	"gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/storage"
	"gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/transaction"
	"gitlab.com/gitlab-org/gitaly/v16/internal/structerr"
	"gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb"
)

// Create creates an object pool for the given source repository. This is done by creating a local
// clone of the source repository where source and target repository will then have the same
// references afterwards.
//
// The source repository will not join the object pool. Thus, its objects won't get deduplicated.
func Create(
	ctx context.Context,
	locator storage.Locator,
	gitCmdFactory git.CommandFactory,
	catfileCache catfile.Cache,
	txManager transaction.Manager,
	housekeepingManager housekeeping.Manager,
	proto *gitalypb.ObjectPool,
	sourceRepo *localrepo.Repo,
) (*ObjectPool, error) {
	objectPoolPath, err := locator.GetRepoPath(proto.GetRepository(), storage.WithRepositoryVerificationSkipped())
	if err != nil {
		return nil, err
	}

	if _, err := os.Stat(objectPoolPath); err == nil {
		return nil, structerr.NewFailedPrecondition("target path exists already").
			WithMetadata("object_pool_path", objectPoolPath)
	} else if !errors.Is(err, os.ErrNotExist) {
		return nil, structerr.NewInternal("checking object pool existence: %w", err).
			WithMetadata("object_pool_path", objectPoolPath)
	}

	sourceRepoPath, err := sourceRepo.Path()
	if err != nil {
		return nil, fmt.Errorf("getting source repository path: %w", err)
	}

	objectHash, err := sourceRepo.ObjectHash(ctx)
	if err != nil {
		return nil, fmt.Errorf("detecting source repo object hash: %w", err)
	}

	var stderr bytes.Buffer
	cmd, err := gitCmdFactory.NewWithoutRepo(ctx,
		git.Command{
			Name: "clone",
			Flags: []git.Option{
				git.Flag{Name: "--quiet"},
				git.Flag{Name: "--bare"},
				git.Flag{Name: "--local"},
			},
			Args: []string{sourceRepoPath, objectPoolPath},
		},
		git.WithRefTxHook(sourceRepo),
		git.WithStderr(&stderr),
		// When cloning an empty repository then Git isn't capable to figure out the correct
		// object hash that the new repository needs to use and just uses the default object
		// format. To work around this shortcoming we thus set the default object hash to
		// match the source repository's object hash.
		git.WithEnv("GIT_DEFAULT_HASH="+objectHash.Format),
	)
	if err != nil {
		return nil, fmt.Errorf("spawning clone: %w", err)
	}

	if err := cmd.Wait(); err != nil {
		return nil, fmt.Errorf("cloning to pool: %w, stderr: %q", err, stderr.String())
	}

	objectPool, err := FromProto(locator, gitCmdFactory, catfileCache, txManager, housekeepingManager, proto)
	if err != nil {
		return nil, err
	}

	// git-clone(1) writes the remote configuration into the object pool. We nowadays don't have
	// remote configuration in the gitconfig anymore, so let's remove it before returning. Note
	// that we explicitly don't use git-remote(1) to do this, as this command would also remove
	// references part of the remote.
	if err := objectPool.ExecAndWait(ctx, git.Command{
		Name: "config",
		Flags: []git.Option{
			git.Flag{Name: "--remove-section"},
		},
		Args: []string{
			"remote.origin",
		},
	}); err != nil {
		return nil, fmt.Errorf("removing origin remote config: %w", err)
	}

	return objectPool, nil
}