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:
authorPatrick Steinhardt <psteinhardt@gitlab.com>2021-07-28 10:41:58 +0300
committerPatrick Steinhardt <psteinhardt@gitlab.com>2021-07-28 15:55:56 +0300
commit007b43e561f7851cbaa96dfa75a21eabd2508539 (patch)
treeb4d2c816608fc4433aa2f1f3a2fcb6587bf19e04
parent61d8dca512acd8432924c6913c2c7dc651b82e0d (diff)
operations: Support worktreeless squashing for non-fast-forward merges
With 640b4de09 (operations: Implement squashing without worktrees, 2021-07-19), we have changed the implementation of UserSquash to support worktreeless squashing. The way this is implemented is that we simply take the tree of the end commit and the parents of the start commit, where the result is then the squashed commit. The initial assumption was that we're always going to have only fast-foward mergeable histories where the end commit is a direct descendant of the start commit. As it turned out, this assumption was wrong, which is why this implementation was special-cased in 0cbd9335b (operations: Squash via patches if commits are not directly related, 2021-07-20) to only apply for fast-forward mergeable commits. With this change in place, we now only use worktreeless squashes in a subset of cases. We can generalize the current implementation though: if start and end commit do not have a direct parent-child relationship, then we must somehow update the child to be on top of the parent. This is exactly what rebases do: by rebasing the end commit on top of the start commit, we can use the above implementation for non-fast-forward mergeable histories. And luckily, we already have in-memory-rebases which do not require worktrees via git2go. This commit thus generalizes worktreeless squashing by always doing a rebase of the end commit on top of the start commit. Changelog: performance
-rw-r--r--internal/gitaly/service/operations/squash.go47
1 files changed, 29 insertions, 18 deletions
diff --git a/internal/gitaly/service/operations/squash.go b/internal/gitaly/service/operations/squash.go
index 1218fd52a..6b9470fb1 100644
--- a/internal/gitaly/service/operations/squash.go
+++ b/internal/gitaly/service/operations/squash.go
@@ -16,6 +16,7 @@ import (
"gitlab.com/gitlab-org/gitaly/v14/internal/command"
"gitlab.com/gitlab-org/gitaly/v14/internal/git"
"gitlab.com/gitlab-org/gitaly/v14/internal/git/alternates"
+ "gitlab.com/gitlab-org/gitaly/v14/internal/git2go"
"gitlab.com/gitlab-org/gitaly/v14/internal/helper"
"gitlab.com/gitlab-org/gitaly/v14/internal/helper/text"
"gitlab.com/gitlab-org/gitaly/v14/internal/metadata/featureflag"
@@ -25,6 +26,7 @@ import (
const (
squashWorktreePrefix = "squash"
gitlabWorktreesSubDir = "gitlab-worktree"
+ ambiguousArgumentFmt = "fatal: ambiguous argument '%s...%s': unknown revision or path not in the working tree.\nUse '--' to separate paths from revisions, like this:\n'git <command> [<revision>...] -- [<file>...]'\n"
)
func (s *Server) UserSquash(ctx context.Context, req *gitalypb.UserSquashRequest) (*gitalypb.UserSquashResponse, error) {
@@ -120,22 +122,12 @@ func (er gitError) Error() string {
}
func (s *Server) userSquash(ctx context.Context, req *gitalypb.UserSquashRequest, env []string, repoPath string) (string, error) {
- // In case there is an error, we silently ignore it and assume that the commits do not have
- // a simple parent-child relationship. Ignoring this error is fine igven that we then fall
- // back to doing a squash with a worktree.
- err := s.localrepo(req.GetRepository()).ExecAndWait(ctx, git.SubCmd{
- Name: "merge-base",
- Flags: []git.Option{
- git.Flag{Name: "--is-ancestor"},
- },
- Args: []string{
- req.GetStartSha(),
- req.GetEndSha(),
- },
- })
-
- if err == nil && featureflag.UserSquashWithoutWorktree.IsEnabled(ctx) {
+ if featureflag.UserSquashWithoutWorktree.IsEnabled(ctx) {
repo := s.localrepo(req.GetRepository())
+ repoPath, err := s.locator.GetRepoPath(repo)
+ if err != nil {
+ return "", helper.ErrInternalf("cannot resolve repo path: %w", err)
+ }
// We need to retrieve the start commit such that we can create the new commit with
// all parents of the start commit.
@@ -145,18 +137,18 @@ func (s *Server) userSquash(ctx context.Context, req *gitalypb.UserSquashRequest
// This error is simply for backwards compatibility. We should just
// return the plain error eventually.
Err: err,
- ErrMsg: fmt.Sprintf("fatal: ambiguous argument '%s...%s'", req.GetStartSha(), req.GetEndSha()),
+ ErrMsg: fmt.Sprintf(ambiguousArgumentFmt, req.GetStartSha(), req.GetEndSha()),
})
}
// And we need to take the tree of the end commit. This tree already is the result
- treeID, err := repo.ResolveRevision(ctx, git.Revision(req.GetEndSha()+"^{tree}"))
+ endCommit, err := repo.ResolveRevision(ctx, git.Revision(req.GetEndSha()+"^{commit}"))
if err != nil {
return "", fmt.Errorf("cannot resolve end commit's tree: %w", gitError{
// This error is simply for backwards compatibility. We should just
// return the plain error eventually.
Err: err,
- ErrMsg: fmt.Sprintf("fatal: ambiguous argument '%s...%s'", req.GetStartSha(), req.GetEndSha()),
+ ErrMsg: fmt.Sprintf(ambiguousArgumentFmt, req.GetStartSha(), req.GetEndSha()),
})
}
@@ -165,6 +157,25 @@ func (s *Server) userSquash(ctx context.Context, req *gitalypb.UserSquashRequest
return "", helper.ErrInvalidArgument(err)
}
+ // We're now rebasing the end commit on top of the start commit. The resulting tree
+ // is then going to be the tree of the squashed commit.
+ rebasedCommitID, err := s.git2go.Rebase(ctx, repo, git2go.RebaseCommand{
+ Repository: repoPath,
+ Committer: git2go.NewSignature(
+ string(req.GetUser().Name), string(req.GetUser().Email), commitDate,
+ ),
+ CommitID: endCommit,
+ UpstreamCommitID: startCommit,
+ })
+ if err != nil {
+ return "", fmt.Errorf("rebasing end onto start commit: %w", err)
+ }
+
+ treeID, err := repo.ResolveRevision(ctx, rebasedCommitID.Revision()+"^{tree}")
+ if err != nil {
+ return "", fmt.Errorf("cannot resolve rebased tree: %w", err)
+ }
+
commitEnv := []string{
"GIT_COMMITTER_NAME=" + string(req.GetUser().Name),
"GIT_COMMITTER_EMAIL=" + string(req.GetUser().Email),