diff options
author | Patrick Steinhardt <psteinhardt@gitlab.com> | 2023-09-05 10:42:53 +0300 |
---|---|---|
committer | Patrick Steinhardt <psteinhardt@gitlab.com> | 2023-09-05 10:42:53 +0300 |
commit | 4325481ab92a44ae5de5f58e126fe1202512998a (patch) | |
tree | 7afd7ecb9bb3464b9f7e94b00f64cb4eceaa8695 /cmd | |
parent | 516b9e1684879959073a19cfe7e3a024ee2f95ec (diff) |
gitaly-git2go: Remove the now-unused command
Remove the now-unused gitaly-git2o command as well as its supporting
infrastructure.
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/gitaly-git2go/featureflags.go | 41 | ||||
-rw-r--r-- | cmd/gitaly-git2go/git2goutil/commit.go | 47 | ||||
-rw-r--r-- | cmd/gitaly-git2go/git2goutil/repo.go | 10 | ||||
-rw-r--r-- | cmd/gitaly-git2go/git2goutil/sign.go | 22 | ||||
-rw-r--r-- | cmd/gitaly-git2go/main.go | 165 | ||||
-rw-r--r-- | cmd/gitaly-git2go/merge.go | 57 | ||||
-rw-r--r-- | cmd/gitaly-git2go/rebase.go | 192 | ||||
-rw-r--r-- | cmd/gitaly-git2go/rebase_test.go | 423 | ||||
-rw-r--r-- | cmd/gitaly-git2go/testhelper_test.go | 44 | ||||
-rw-r--r-- | cmd/gitaly-git2go/util.go | 32 |
10 files changed, 0 insertions, 1033 deletions
diff --git a/cmd/gitaly-git2go/featureflags.go b/cmd/gitaly-git2go/featureflags.go deleted file mode 100644 index 055f18be9..000000000 --- a/cmd/gitaly-git2go/featureflags.go +++ /dev/null @@ -1,41 +0,0 @@ -//go:build static && system_libgit2 && gitaly_test - -package main - -import ( - "context" - "encoding/gob" - "flag" - - "gitlab.com/gitlab-org/gitaly/v16/internal/featureflag" - "gitlab.com/gitlab-org/gitaly/v16/internal/git2go" -) - -// This subcommand is only called in tests, so we don't want to register it like -// the other subcommands but instead will do it in an init block. The gitaly_test build -// flag will guarantee that this is not built and registered in the -// gitaly-git2go binary -func init() { - subcommands["feature-flags"] = &featureFlagsSubcommand{} -} - -type featureFlagsSubcommand struct{} - -func (featureFlagsSubcommand) Flags() *flag.FlagSet { - return flag.NewFlagSet("feature-flags", flag.ExitOnError) -} - -func (featureFlagsSubcommand) Run(ctx context.Context, decoder *gob.Decoder, encoder *gob.Encoder) error { - var flags []git2go.FeatureFlag - for flag, value := range featureflag.FromContext(ctx) { - flags = append(flags, git2go.FeatureFlag{ - Name: flag.Name, - MetadataKey: flag.MetadataKey(), - Value: value, - }) - } - - return encoder.Encode(git2go.FeatureFlags{ - Flags: flags, - }) -} diff --git a/cmd/gitaly-git2go/git2goutil/commit.go b/cmd/gitaly-git2go/git2goutil/commit.go deleted file mode 100644 index 804715884..000000000 --- a/cmd/gitaly-git2go/git2goutil/commit.go +++ /dev/null @@ -1,47 +0,0 @@ -package git2goutil - -import ( - "fmt" - - git "github.com/libgit2/git2go/v34" -) - -// CommitSubmitter is the helper struct to make signed Commits conveniently. -type CommitSubmitter struct { - Repo *git.Repository - SigningKeyPath string -} - -// NewCommitSubmitter creates a new CommitSubmitter. -func NewCommitSubmitter(repo *git.Repository, signingKeyPath string) *CommitSubmitter { - return &CommitSubmitter{ - Repo: repo, - SigningKeyPath: signingKeyPath, - } -} - -// Commit commits a commit with or without signature depends on SigningKeyPath value. -func (cs *CommitSubmitter) Commit( - author, committer *git.Signature, - messageEncoding git.MessageEncoding, - message string, - tree *git.Tree, - parents ...*git.Commit, -) (*git.Oid, error) { - commitBytes, err := cs.Repo.CreateCommitBuffer(author, committer, messageEncoding, message, tree, parents...) - if err != nil { - return nil, err - } - - signature, err := CreateCommitSignature(cs.SigningKeyPath, commitBytes) - if err != nil { - return nil, fmt.Errorf("create commit signature: %w", err) - } - - commitID, err := cs.Repo.CreateCommitWithSignature(string(commitBytes), string(signature), "") - if err != nil { - return nil, err - } - - return commitID, nil -} diff --git a/cmd/gitaly-git2go/git2goutil/repo.go b/cmd/gitaly-git2go/git2goutil/repo.go deleted file mode 100644 index 1a1c4ede2..000000000 --- a/cmd/gitaly-git2go/git2goutil/repo.go +++ /dev/null @@ -1,10 +0,0 @@ -package git2goutil - -import ( - git "github.com/libgit2/git2go/v34" -) - -// OpenRepository opens the repository located at path as a Git2Go repository. -func OpenRepository(path string) (*git.Repository, error) { - return git.OpenRepositoryExtended(path, git.RepositoryOpenFromEnv, "") -} diff --git a/cmd/gitaly-git2go/git2goutil/sign.go b/cmd/gitaly-git2go/git2goutil/sign.go deleted file mode 100644 index efa8d1b1d..000000000 --- a/cmd/gitaly-git2go/git2goutil/sign.go +++ /dev/null @@ -1,22 +0,0 @@ -package git2goutil - -import ( - "fmt" - - "gitlab.com/gitlab-org/gitaly/v16/internal/signature" -) - -// CreateCommitSignature reads the given signing key and produces PKCS#7 detached signature. -// When the path to the signing key is not present, an empty signature is returned. -func CreateCommitSignature(signingKeyPath string, contentToSign []byte) ([]byte, error) { - if signingKeyPath == "" { - return nil, nil - } - - signingKeys, err := signature.ParseSigningKeys(signingKeyPath) - if err != nil { - return nil, fmt.Errorf("failed to parse signing key: %w", err) - } - - return signingKeys.CreateSignature(contentToSign) -} diff --git a/cmd/gitaly-git2go/main.go b/cmd/gitaly-git2go/main.go deleted file mode 100644 index 9c5c4009e..000000000 --- a/cmd/gitaly-git2go/main.go +++ /dev/null @@ -1,165 +0,0 @@ -//go:build static && system_libgit2 - -package main - -import ( - "context" - "encoding/gob" - "flag" - "fmt" - "os" - "strings" - - "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus" - git "github.com/libgit2/git2go/v34" - "github.com/sirupsen/logrus" - "gitlab.com/gitlab-org/gitaly/v16/internal/featureflag" - "gitlab.com/gitlab-org/gitaly/v16/internal/git2go" - glog "gitlab.com/gitlab-org/gitaly/v16/internal/log" - "gitlab.com/gitlab-org/labkit/correlation" -) - -type subcmd interface { - Flags() *flag.FlagSet - Run(ctx context.Context, decoder *gob.Decoder, encoder *gob.Encoder) error -} - -var subcommands = map[string]subcmd{ - "rebase": &rebaseSubcommand{}, -} - -func fatalf(logger logrus.FieldLogger, encoder *gob.Encoder, format string, args ...interface{}) { - err := encoder.Encode(git2go.Result{ - Err: git2go.SerializableError(fmt.Errorf(format, args...)), - }) - if err != nil { - logger.WithError(err).Error("encode to gob failed") - } - // An exit code of 1 would indicate an error over stderr. Since our errors - // are encoded over gob, we need to exit cleanly - os.Exit(0) -} - -func main() { - decoder := gob.NewDecoder(os.Stdin) - encoder := gob.NewEncoder(os.Stdout) - - var logFormat, logLevel, correlationID string - var enabledFeatureFlags, disabledFeatureFlags featureFlagArg - - flags := flag.NewFlagSet(git2go.BinaryName, flag.PanicOnError) - flags.StringVar(&logFormat, "log-format", "", "logging format") - flags.StringVar(&logLevel, "log-level", "", "logging level") - flags.StringVar(&correlationID, "correlation-id", "", "correlation ID used for request tracing") - flags.Var( - &enabledFeatureFlags, - "enabled-feature-flags", - "comma separated list of explicitly enabled feature flags", - ) - flags.Var( - &disabledFeatureFlags, - "disabled-feature-flags", - "comma separated list of explicitly disabled feature flags", - ) - _ = flags.Parse(os.Args[1:]) - - if correlationID == "" { - correlationID = correlation.SafeRandomID() - } - - ctx := correlation.ContextWithCorrelation(context.Background(), correlationID) - - logger, err := glog.Configure(os.Stderr, logFormat, logLevel) - if err != nil { - fmt.Printf("configuring logger failed: %v", err) - os.Exit(1) - } - - logger = logger.WithFields(logrus.Fields{ - "command.name": git2go.BinaryName, - "correlation_id": correlationID, - "enabled_feature_flags": enabledFeatureFlags, - "disabled_feature_flags": disabledFeatureFlags, - }) - - if flags.NArg() < 1 { - fatalf(logger, encoder, "missing subcommand") - } - - subcmd, ok := subcommands[flags.Arg(0)] - if !ok { - fatalf(logger, encoder, "unknown subcommand: %q", flags.Arg(0)) - } - - subcmdFlags := subcmd.Flags() - if err := subcmdFlags.Parse(flags.Args()[1:]); err != nil { - fatalf(logger, encoder, "parsing flags of %q: %s", subcmdFlags.Name(), err) - } - - if subcmdFlags.NArg() != 0 { - fatalf(logger, encoder, "%s: trailing arguments", subcmdFlags.Name()) - } - - if err := git.EnableFsyncGitDir(true); err != nil { - fatalf(logger, encoder, "enable fsync: %s", err) - } - - for _, configLevel := range []git.ConfigLevel{ - git.ConfigLevelSystem, - git.ConfigLevelXDG, - git.ConfigLevelGlobal, - } { - if err := git.SetSearchPath(configLevel, "/dev/null"); err != nil { - fatalf(logger, encoder, "setting search path: %s", err) - } - } - - subcmdLogger := logger.WithField("command.subcommand", subcmdFlags.Name()) - subcmdLogger.Infof("starting %s command", subcmdFlags.Name()) - - ctx = ctxlogrus.ToContext(ctx, subcmdLogger) - ctx = enabledFeatureFlags.ToContext(ctx, true) - ctx = disabledFeatureFlags.ToContext(ctx, false) - - if err := subcmd.Run(ctx, decoder, encoder); err != nil { - subcmdLogger.WithError(err).Errorf("%s command failed", subcmdFlags.Name()) - fatalf(logger, encoder, "%s: %s", subcmdFlags.Name(), err) - } - - subcmdLogger.Infof("%s command finished", subcmdFlags.Name()) -} - -type featureFlagArg []featureflag.FeatureFlag - -func (v *featureFlagArg) String() string { - metadataKeys := make([]string, 0, len(*v)) - for _, flag := range *v { - metadataKeys = append(metadataKeys, flag.MetadataKey()) - } - return strings.Join(metadataKeys, ",") -} - -func (v *featureFlagArg) Set(s string) error { - if s == "" { - return nil - } - - for _, metadataKey := range strings.Split(s, ",") { - flag, err := featureflag.FromMetadataKey(metadataKey) - if err != nil { - return err - } - - *v = append(*v, flag) - } - - return nil -} - -func (v featureFlagArg) ToContext(ctx context.Context, enabled bool) context.Context { - for _, flag := range v { - ctx = featureflag.IncomingCtxWithFeatureFlag(ctx, flag, enabled) - } - - return ctx -} diff --git a/cmd/gitaly-git2go/merge.go b/cmd/gitaly-git2go/merge.go deleted file mode 100644 index 92f0f53fb..000000000 --- a/cmd/gitaly-git2go/merge.go +++ /dev/null @@ -1,57 +0,0 @@ -//go:build static && system_libgit2 - -package main - -import ( - "errors" - "fmt" - - git "github.com/libgit2/git2go/v34" -) - -func getConflictingFiles(index *git.Index) ([]string, error) { - conflicts, err := getConflicts(index) - if err != nil { - return nil, fmt.Errorf("getting conflicts: %w", err) - } - - conflictingFiles := make([]string, 0, len(conflicts)) - for _, conflict := range conflicts { - switch { - case conflict.Our != nil: - conflictingFiles = append(conflictingFiles, conflict.Our.Path) - case conflict.Ancestor != nil: - conflictingFiles = append(conflictingFiles, conflict.Ancestor.Path) - case conflict.Their != nil: - conflictingFiles = append(conflictingFiles, conflict.Their.Path) - default: - return nil, errors.New("invalid conflict") - } - } - - return conflictingFiles, nil -} - -func getConflicts(index *git.Index) ([]git.IndexConflict, error) { - var conflicts []git.IndexConflict - - iterator, err := index.ConflictIterator() - if err != nil { - return nil, err - } - defer iterator.Free() - - for { - conflict, err := iterator.Next() - if err != nil { - if git.IsErrorCode(err, git.ErrorCodeIterOver) { - break - } - return nil, err - } - - conflicts = append(conflicts, conflict) - } - - return conflicts, nil -} diff --git a/cmd/gitaly-git2go/rebase.go b/cmd/gitaly-git2go/rebase.go deleted file mode 100644 index cf221007f..000000000 --- a/cmd/gitaly-git2go/rebase.go +++ /dev/null @@ -1,192 +0,0 @@ -//go:build static && system_libgit2 - -package main - -import ( - "context" - "encoding/gob" - "errors" - "flag" - "fmt" - - git "github.com/libgit2/git2go/v34" - "gitlab.com/gitlab-org/gitaly/v16/cmd/gitaly-git2go/git2goutil" - "gitlab.com/gitlab-org/gitaly/v16/internal/git2go" -) - -type rebaseSubcommand struct{} - -func (cmd *rebaseSubcommand) Flags() *flag.FlagSet { - return flag.NewFlagSet("rebase", flag.ExitOnError) -} - -func (cmd *rebaseSubcommand) Run(ctx context.Context, decoder *gob.Decoder, encoder *gob.Encoder) error { - var request git2go.RebaseCommand - if err := decoder.Decode(&request); err != nil { - return err - } - - commitID, err := cmd.rebase(ctx, &request) - return encoder.Encode(git2go.Result{ - CommitID: commitID, - Err: git2go.SerializableError(err), - }) -} - -func (cmd *rebaseSubcommand) verify(ctx context.Context, r *git2go.RebaseCommand) error { - if r.Repository == "" { - return errors.New("missing repository") - } - if r.Committer.Name == "" { - return errors.New("missing committer name") - } - if r.Committer.Email == "" { - return errors.New("missing committer email") - } - if r.BranchName == "" && r.CommitID == "" { - return errors.New("missing branch name") - } - if r.BranchName != "" && r.CommitID != "" { - return errors.New("both branch name and commit ID") - } - if r.UpstreamRevision == "" && r.UpstreamCommitID == "" { - return errors.New("missing upstream revision") - } - if r.UpstreamRevision != "" && r.UpstreamCommitID != "" { - return errors.New("both upstream revision and upstream commit ID") - } - return nil -} - -func (cmd *rebaseSubcommand) rebase(ctx context.Context, request *git2go.RebaseCommand) (string, error) { - if err := cmd.verify(ctx, request); err != nil { - return "", err - } - - repo, err := git2goutil.OpenRepository(request.Repository) - if err != nil { - return "", fmt.Errorf("open repository: %w", err) - } - - opts, err := git.DefaultRebaseOptions() - if err != nil { - return "", fmt.Errorf("get rebase options: %w", err) - } - opts.InMemory = 1 - opts.CommitCreateCallback = git2goutil.NewCommitSubmitter(repo, request.SigningKey).Commit - - var commit *git.AnnotatedCommit - if request.BranchName != "" { - commit, err = repo.AnnotatedCommitFromRevspec(fmt.Sprintf("refs/heads/%s", request.BranchName)) - if err != nil { - return "", fmt.Errorf("look up branch %q: %w", request.BranchName, err) - } - } else { - commitOid, err := git.NewOid(request.CommitID.String()) - if err != nil { - return "", fmt.Errorf("parse commit %q: %w", request.CommitID, err) - } - - commit, err = repo.LookupAnnotatedCommit(commitOid) - if err != nil { - return "", fmt.Errorf("look up commit %q: %w", request.CommitID, err) - } - } - - upstreamCommitParam := request.UpstreamRevision - if upstreamCommitParam == "" { - upstreamCommitParam = request.UpstreamCommitID.String() - } - - upstreamCommitOID, err := git.NewOid(upstreamCommitParam) - if err != nil { - return "", fmt.Errorf("parse upstream revision %q: %w", upstreamCommitParam, err) - } - - upstreamCommit, err := repo.LookupAnnotatedCommit(upstreamCommitOID) - if err != nil { - return "", fmt.Errorf("look up upstream revision %q: %w", upstreamCommitParam, err) - } - - mergeBase, err := repo.MergeBase(upstreamCommit.Id(), commit.Id()) - if err != nil { - return "", fmt.Errorf("find merge base: %w", err) - } - - if mergeBase.Equal(upstreamCommit.Id()) { - // Branch is zero commits behind, so do not rebase - return commit.Id().String(), nil - } - - if mergeBase.Equal(commit.Id()) { - // Branch is merged, so fast-forward to upstream - return upstreamCommit.Id().String(), nil - } - - mergeCommit, err := repo.LookupAnnotatedCommit(mergeBase) - if err != nil { - return "", fmt.Errorf("look up merge base: %w", err) - } - - rebase, err := repo.InitRebase(commit, mergeCommit, upstreamCommit, &opts) - if err != nil { - return "", fmt.Errorf("initiate rebase: %w", err) - } - - committer := git.Signature(request.Committer) - var oid *git.Oid - for { - op, err := rebase.Next() - if git.IsErrorCode(err, git.ErrorCodeIterOver) { - break - } else if err != nil { - return "", fmt.Errorf("rebase iterate: %w", err) - } - - commit, err := repo.LookupCommit(op.Id) - if err != nil { - return "", fmt.Errorf("lookup commit: %w", err) - } - - if err := rebase.Commit(op.Id, nil, &committer, commit.Message()); err != nil { - if git.IsErrorCode(err, git.ErrorCodeUnmerged) { - index, err := rebase.InmemoryIndex() - if err != nil { - return "", fmt.Errorf("getting conflicting index: %w", err) - } - - conflictingFiles, err := getConflictingFiles(index) - if err != nil { - return "", fmt.Errorf("getting conflicting files: %w", err) - } - - return "", fmt.Errorf("commit %q: %w", op.Id.String(), git2go.ConflictingFilesError{ - ConflictingFiles: conflictingFiles, - }) - } - - // If the commit has already been applied on the target branch then we can - // skip it if we were told to. - if request.SkipEmptyCommits && git.IsErrorCode(err, git.ErrorCodeApplied) { - continue - } - - return "", fmt.Errorf("commit %q: %w", op.Id.String(), err) - } - - oid = op.Id.Copy() - } - - // When the OID is unset here, then we didn't have to rebase any commits at all. We can - // thus return the upstream commit directly: rebasing nothing onto the upstream commit is - // the same as the upstream commit itself. - if oid == nil { - return upstreamCommit.Id().String(), nil - } - - if err = rebase.Finish(); err != nil { - return "", fmt.Errorf("finish rebase: %w", err) - } - - return oid.String(), nil -} diff --git a/cmd/gitaly-git2go/rebase_test.go b/cmd/gitaly-git2go/rebase_test.go deleted file mode 100644 index 6de901e32..000000000 --- a/cmd/gitaly-git2go/rebase_test.go +++ /dev/null @@ -1,423 +0,0 @@ -//go:build static && system_libgit2 - -package main - -import ( - "fmt" - "testing" - "time" - - "github.com/stretchr/testify/require" - "gitlab.com/gitlab-org/gitaly/v16/cmd/gitaly-git2go/git2goutil" - "gitlab.com/gitlab-org/gitaly/v16/internal/git" - "gitlab.com/gitlab-org/gitaly/v16/internal/git/gittest" - "gitlab.com/gitlab-org/gitaly/v16/internal/git2go" - "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper" - "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper/testcfg" -) - -var masterRevision = "1e292f8fedd741b75372e19097c76d327140c312" - -func TestRebase_validation(t *testing.T) { - gittest.SkipWithSHA256(t) - - ctx := testhelper.Context(t) - cfg := testcfg.Build(t) - - testcfg.BuildGitalyGit2Go(t, cfg) - - repo, repoPath := gittest.CreateRepository(t, ctx, cfg, gittest.CreateRepositoryConfig{ - SkipCreationViaService: true, - }) - - committer := git2go.NewSignature("Foo", "foo@example.com", time.Now()) - executor := buildExecutor(t, cfg) - - testcases := []struct { - desc string - request git2go.RebaseCommand - expectedErr string - }{ - { - desc: "no arguments", - expectedErr: "rebase: missing repository", - }, - { - desc: "missing repository", - request: git2go.RebaseCommand{Committer: committer, BranchName: "feature", UpstreamRevision: masterRevision}, - expectedErr: "rebase: missing repository", - }, - { - desc: "missing committer name", - request: git2go.RebaseCommand{Repository: repoPath, Committer: git2go.Signature{Email: "foo@example.com"}, BranchName: "feature", UpstreamRevision: masterRevision}, - expectedErr: "rebase: missing committer name", - }, - { - desc: "missing committer email", - request: git2go.RebaseCommand{Repository: repoPath, Committer: git2go.Signature{Name: "Foo"}, BranchName: "feature", UpstreamRevision: masterRevision}, - expectedErr: "rebase: missing committer email", - }, - { - desc: "missing branch name", - request: git2go.RebaseCommand{Repository: repoPath, Committer: committer, UpstreamRevision: masterRevision}, - expectedErr: "rebase: missing branch name", - }, - { - desc: "missing upstream branch", - request: git2go.RebaseCommand{Repository: repoPath, Committer: committer, BranchName: "feature"}, - expectedErr: "rebase: missing upstream revision", - }, - { - desc: "both branch name and commit ID", - request: git2go.RebaseCommand{Repository: repoPath, Committer: committer, BranchName: "feature", CommitID: "a"}, - expectedErr: "rebase: both branch name and commit ID", - }, - { - desc: "both upstream revision and upstream commit ID", - request: git2go.RebaseCommand{Repository: repoPath, Committer: committer, BranchName: "feature", UpstreamRevision: "a", UpstreamCommitID: "a"}, - expectedErr: "rebase: both upstream revision and upstream commit ID", - }, - } - for _, tc := range testcases { - t.Run(tc.desc, func(t *testing.T) { - _, err := executor.Rebase(ctx, repo, tc.request) - require.EqualError(t, err, tc.expectedErr) - }) - } -} - -func TestRebase_rebase(t *testing.T) { - gittest.SkipWithSHA256(t) - - t.Parallel() - - ctx := testhelper.Context(t) - cfg := testcfg.Build(t) - testcfg.BuildGitalyGit2Go(t, cfg) - executor := buildExecutor(t, cfg) - - committer := git2go.NewSignature( - string(gittest.TestUser.Name), - string(gittest.TestUser.Email), - time.Date(2021, 3, 1, 13, 45, 50, 0, time.FixedZone("", +2*60*60)), - ) - - type setup struct { - base, upstream, downstream git.ObjectID - expecetedCommitsAhead int - expectedObjectID git.ObjectID - expectedErr string - } - - testcases := []struct { - desc string - setup func(testing.TB, string) setup - }{ - { - desc: "Single commit rebase", - setup: func(tb testing.TB, repoPath string) setup { - base := gittest.WriteCommit(t, cfg, repoPath, gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "a\nb\nc\nd\ne\nf\ng\n"}, - )) - upstream := gittest.WriteCommit(t, cfg, repoPath, gittest.WithMessage("upstream"), gittest.WithParents(base), gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "upstream\nb\nc\nd\ne\nf\ng\n"}, - )) - downstream := gittest.WriteCommit(t, cfg, repoPath, gittest.WithMessage("downstream"), gittest.WithParents(base), gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "a\nb\nc\nd\ne\nf\ndownstream\n"}, - )) - - return setup{ - base: base, - upstream: upstream, - downstream: downstream, - expectedObjectID: "ef018adb419cd97453a0624c28271fafe622b83e", - expecetedCommitsAhead: 1, - } - }, - }, - { - desc: "Multiple commits", - setup: func(tb testing.TB, repoPath string) setup { - base := gittest.WriteCommit(t, cfg, repoPath, gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "a\nb\nc\nd\ne\nf\ng\n"}, - )) - upstream := gittest.WriteCommit(t, cfg, repoPath, gittest.WithMessage("upstream"), gittest.WithParents(base), gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "upstream\nb\nc\nd\ne\nf\ng\n"}, - )) - downstream1 := gittest.WriteCommit(t, cfg, repoPath, gittest.WithMessage("downstream-1"), gittest.WithParents(base), gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "a\nb\nc\nd\ne\nf\ndownstream-1\n"}, - )) - downstream2 := gittest.WriteCommit(t, cfg, repoPath, gittest.WithMessage("downstream-2"), gittest.WithParents(downstream1), gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "a\nb\nc\nd\ne\nf\ndownstream-2\n"}, - )) - downstream3 := gittest.WriteCommit(t, cfg, repoPath, gittest.WithMessage("downstream-3"), gittest.WithParents(downstream2), gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "a\nb\nc\nd\ne\nf\ndownstream-3\n"}, - )) - - return setup{ - base: base, - upstream: upstream, - downstream: downstream3, - expectedObjectID: "d3c737fdb3a0c4da3a371fc01de6df4cbb5bc3e4", - expecetedCommitsAhead: 3, - } - }, - }, - { - desc: "Branch zero commits behind", - setup: func(tb testing.TB, repoPath string) setup { - base := gittest.WriteCommit(t, cfg, repoPath, gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "a\nb\nc\nd\ne\nf\ng\n"}, - )) - downstream := gittest.WriteCommit(t, cfg, repoPath, gittest.WithMessage("downstream"), gittest.WithParents(base), gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "a\nb\nc\nd\ne\nf\ndownstream\n"}, - )) - - return setup{ - base: base, - upstream: base, - downstream: downstream, - expectedObjectID: downstream, - expecetedCommitsAhead: 1, - } - }, - }, - { - desc: "Merged branch", - setup: func(tb testing.TB, repoPath string) setup { - base := gittest.WriteCommit(t, cfg, repoPath, gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "a\nb\nc\nd\ne\nf\ng\n"}, - )) - downstream := gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(base), gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "a\nb\nc\nd\ne\nf\ndownstream\n"}, - )) - merge := gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(base, downstream), gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "a\nb\nc\nd\ne\nf\ndownstream\n"}, - )) - - return setup{ - base: base, upstream: merge, downstream: downstream, - expectedObjectID: merge, - } - }, - }, - { - desc: "Partially merged branch", - setup: func(tb testing.TB, repoPath string) setup { - base := gittest.WriteCommit(t, cfg, repoPath, gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "a\nb\nc\nd\ne\nf\ng\n"}, - )) - downstream1 := gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(base), gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "a\nb\nc\nd\ne\nf\ndownstream-1\n"}, - )) - downstream2 := gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(downstream1), gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "a\nb\nc\nd\ne\nf\ndownstream-2\n"}, - )) - merge := gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(base, downstream1), gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "a\nb\nc\nd\ne\nf\ndownstream-1\n"}, - )) - - return setup{ - base: base, - upstream: merge, - downstream: downstream2, - expectedObjectID: "721e8bd36a394a7cc243b8c3960b44c5520c6246", - expecetedCommitsAhead: 1, - } - }, - }, - { - desc: "With upstream merged into", - setup: func(tb testing.TB, repoPath string) setup { - base := gittest.WriteCommit(t, cfg, repoPath, gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "a\nb\nc\nd\ne\nf\ng\n"}, - )) - upstream := gittest.WriteCommit(t, cfg, repoPath, gittest.WithMessage("upstream"), gittest.WithParents(base), gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "upstream\nb\nc\nd\ne\nf\ng\n"}, - )) - downstream := gittest.WriteCommit(t, cfg, repoPath, gittest.WithMessage("downstream"), gittest.WithParents(base), gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "a\nb\nc\nd\ne\nf\ndownstream\n"}, - )) - downstreamMerge := gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(downstream, upstream), gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "upstream\nb\nc\nd\ne\nf\ndownstream\n"}, - )) - - return setup{ - base: base, - upstream: upstream, - downstream: downstreamMerge, - expectedObjectID: "aa375bc059fa8830d9489d89af1278632722407d", - expecetedCommitsAhead: 2, - } - }, - }, - { - desc: "Rebase with conflict", - setup: func(tb testing.TB, repoPath string) setup { - base := gittest.WriteCommit(t, cfg, repoPath, gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "base\n"}, - )) - upstream := gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(base), gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "upstream\n"}, - )) - downstream := gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(base), gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "downstream\n"}, - )) - - return setup{ - upstream: upstream, - downstream: downstream, - expectedErr: fmt.Sprintf("rebase: commit %q: there are conflicting files", downstream), - } - }, - }, - { - desc: "Orphaned branch", - setup: func(tb testing.TB, repoPath string) setup { - upstream := gittest.WriteCommit(t, cfg, repoPath, gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "upstream\n"}, - )) - downstream := gittest.WriteCommit(t, cfg, repoPath, gittest.WithTreeEntries( - gittest.TreeEntry{Path: "path", Mode: "100644", Content: "downstream\n"}, - )) - - return setup{ - upstream: upstream, - downstream: downstream, - expectedErr: "rebase: find merge base: no merge base found", - } - }, - }, - } - - for _, tc := range testcases { - tc := tc - - t.Run(tc.desc, func(t *testing.T) { - t.Parallel() - - repoProto, repoPath := gittest.CreateRepository(t, ctx, cfg, gittest.CreateRepositoryConfig{ - SkipCreationViaService: true, - }) - setup := tc.setup(t, repoPath) - - gittest.WriteRef(t, cfg, repoPath, "refs/heads/upstream", setup.upstream) - gittest.WriteRef(t, cfg, repoPath, "refs/heads/downstream", setup.downstream) - - repo, err := git2goutil.OpenRepository(repoPath) - require.NoError(t, err) - - for desc, request := range map[string]git2go.RebaseCommand{ - "with branch and upstream": { - Repository: repoPath, - Committer: committer, - BranchName: "downstream", - UpstreamRevision: setup.upstream.String(), - }, - "with branch and upstream commit ID": { - Repository: repoPath, - Committer: committer, - BranchName: "downstream", - UpstreamCommitID: setup.upstream, - }, - "with commit ID and upstream": { - Repository: repoPath, - Committer: committer, - CommitID: setup.downstream, - UpstreamRevision: setup.upstream.String(), - }, - "with commit ID and upstream commit ID": { - Repository: repoPath, - Committer: committer, - CommitID: setup.downstream, - UpstreamCommitID: setup.upstream, - }, - } { - t.Run(desc, func(t *testing.T) { - response, err := executor.Rebase(ctx, repoProto, request) - if setup.expectedErr != "" { - require.EqualError(t, err, setup.expectedErr) - } else { - require.NoError(t, err) - require.Equal(t, setup.expectedObjectID, response) - - commit, err := lookupCommit(repo, response.String()) - require.NoError(t, err) - - for i := setup.expecetedCommitsAhead; i > 0; i-- { - commit = commit.Parent(0) - } - baseCommit, err := lookupCommit(repo, setup.base.String()) - require.NoError(t, err) - require.Equal(t, baseCommit, commit) - } - }) - } - }) - } -} - -func TestRebase_skipEmptyCommit(t *testing.T) { - gittest.SkipWithSHA256(t) - - ctx := testhelper.Context(t) - cfg := testcfg.Build(t) - - testcfg.BuildGitalyGit2Go(t, cfg) - - repoProto, repoPath := gittest.CreateRepository(t, ctx, cfg, gittest.CreateRepositoryConfig{ - SkipCreationViaService: true, - }) - - // Set up history with two diverging lines of branches, where both sides have implemented - // the same changes. During rebase, the diff will thus become empty. - base := gittest.WriteCommit(t, cfg, repoPath, - gittest.WithTreeEntries(gittest.TreeEntry{ - Path: "a", Content: "base", Mode: "100644", - }), - ) - theirs := gittest.WriteCommit(t, cfg, repoPath, gittest.WithMessage("theirs"), - gittest.WithParents(base), gittest.WithTreeEntries(gittest.TreeEntry{ - Path: "a", Content: "changed", Mode: "100644", - }), - ) - ours := gittest.WriteCommit(t, cfg, repoPath, gittest.WithMessage("ours"), - gittest.WithParents(base), gittest.WithTreeEntries(gittest.TreeEntry{ - Path: "a", Content: "changed", Mode: "100644", - }), - ) - - for _, tc := range []struct { - desc string - skipEmptyCommits bool - expectedErr string - expectedResponse git.ObjectID - }{ - { - desc: "do not skip empty commit", - skipEmptyCommits: false, - expectedErr: fmt.Sprintf("rebase: commit %q: this patch has already been applied", ours), - }, - { - desc: "skip empty commit", - skipEmptyCommits: true, - expectedResponse: theirs, - }, - } { - t.Run(tc.desc, func(t *testing.T) { - response, err := buildExecutor(t, cfg).Rebase(ctx, repoProto, git2go.RebaseCommand{ - Repository: repoPath, - Committer: git2go.NewSignature("Foo", "foo@example.com", time.Now()), - CommitID: ours, - UpstreamCommitID: theirs, - SkipEmptyCommits: tc.skipEmptyCommits, - }) - if tc.expectedErr == "" { - require.NoError(t, err) - } else { - require.EqualError(t, err, tc.expectedErr) - } - require.Equal(t, tc.expectedResponse, response) - }) - } -} diff --git a/cmd/gitaly-git2go/testhelper_test.go b/cmd/gitaly-git2go/testhelper_test.go deleted file mode 100644 index 0d72bec57..000000000 --- a/cmd/gitaly-git2go/testhelper_test.go +++ /dev/null @@ -1,44 +0,0 @@ -//go:build static && system_libgit2 - -package main - -import ( - "fmt" - "testing" - - git "github.com/libgit2/git2go/v34" - "gitlab.com/gitlab-org/gitaly/v16/internal/git/gittest" - "gitlab.com/gitlab-org/gitaly/v16/internal/git2go" - "gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/config" - "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper" -) - -// DefaultAuthor is the author used by BuildCommit -var DefaultAuthor = git.Signature{ - Name: gittest.DefaultCommitterName, - Email: gittest.DefaultCommitterMail, - When: gittest.DefaultCommitTime, -} - -func TestMain(m *testing.M) { - testhelper.Run(m, testhelper.WithSetup(func() error { - // We use Git2go to access repositories in our tests, so we must tell it to ignore - // any configuration files that happen to exist. We do the same in `main()`, so - // this is not only specific to tests. - for _, configLevel := range []git.ConfigLevel{ - git.ConfigLevelSystem, - git.ConfigLevelXDG, - git.ConfigLevelGlobal, - } { - if err := git.SetSearchPath(configLevel, "/dev/null"); err != nil { - return fmt.Errorf("setting Git2go search path: %w", err) - } - } - - return nil - })) -} - -func buildExecutor(tb testing.TB, cfg config.Cfg) *git2go.Executor { - return git2go.NewExecutor(cfg, gittest.NewCommandFactory(tb, cfg), config.NewLocator(cfg), testhelper.SharedLogger(tb)) -} diff --git a/cmd/gitaly-git2go/util.go b/cmd/gitaly-git2go/util.go deleted file mode 100644 index c76b9370c..000000000 --- a/cmd/gitaly-git2go/util.go +++ /dev/null @@ -1,32 +0,0 @@ -//go:build static && system_libgit2 - -package main - -import ( - "fmt" - - git "github.com/libgit2/git2go/v34" - "gitlab.com/gitlab-org/gitaly/v16/internal/git2go" -) - -func lookupCommit(repo *git.Repository, ref string) (*git.Commit, error) { - object, err := repo.RevparseSingle(ref) - switch { - case git.IsErrorCode(err, git.ErrorCodeNotFound): - return nil, git2go.CommitNotFoundError{Revision: ref} - case err != nil: - return nil, fmt.Errorf("lookup commit %q: %w", ref, err) - } - - peeled, err := object.Peel(git.ObjectCommit) - if err != nil { - return nil, fmt.Errorf("lookup commit %q: peel: %w", ref, err) - } - - commit, err := peeled.AsCommit() - if err != nil { - return nil, fmt.Errorf("lookup commit %q: as commit: %w", ref, err) - } - - return commit, nil -} |