diff options
author | Patrick Steinhardt <psteinhardt@gitlab.com> | 2021-09-14 08:38:40 +0300 |
---|---|---|
committer | Patrick Steinhardt <psteinhardt@gitlab.com> | 2021-09-14 08:38:40 +0300 |
commit | 07a81867515f925c6108033d2bb05aebfcf14bd0 (patch) | |
tree | 0c24dba206efcf831bf43fe6f99443d1a313043c | |
parent | 29b7edea2d34492f181d6cc6d501cc9ae50abe53 (diff) | |
parent | d9a22a99a362e0a451da42ab6f5b77711f056290 (diff) |
Merge branch 'revert_3817' into 'master'
Revert "Merge branch 'extract_default_branch' into 'master'"
See merge request gitlab-org/gitaly!3868
-rw-r--r-- | internal/git/localrepo/refs.go | 40 | ||||
-rw-r--r-- | internal/git/localrepo/refs_test.go | 82 | ||||
-rw-r--r-- | internal/git/repository.go | 7 | ||||
-rw-r--r-- | internal/gitaly/service/commit/commits_by_message.go | 5 | ||||
-rw-r--r-- | internal/gitaly/service/commit/find_all_commits.go | 1 | ||||
-rw-r--r-- | internal/gitaly/service/commit/find_all_commits_test.go | 1 | ||||
-rw-r--r-- | internal/gitaly/service/commit/find_commits.go | 4 | ||||
-rw-r--r-- | internal/gitaly/service/commit/languages.go | 5 | ||||
-rw-r--r-- | internal/gitaly/service/commit/list_files.go | 13 | ||||
-rw-r--r-- | internal/gitaly/service/commit/list_files_test.go | 12 | ||||
-rw-r--r-- | internal/gitaly/service/commit/server.go | 3 | ||||
-rw-r--r-- | internal/gitaly/service/operations/apply_patch.go | 9 | ||||
-rw-r--r-- | internal/gitaly/service/operations/apply_patch_test.go | 7 | ||||
-rw-r--r-- | internal/gitaly/service/ref/refs.go | 103 | ||||
-rw-r--r-- | internal/gitaly/service/ref/refs_test.go | 117 | ||||
-rw-r--r-- | internal/gitaly/service/remote/fetch_internal_remote.go | 7 | ||||
-rw-r--r-- | internal/gitaly/service/remote/update_remote_mirror.go | 7 |
17 files changed, 251 insertions, 172 deletions
diff --git a/internal/git/localrepo/refs.go b/internal/git/localrepo/refs.go index 264bee4d2..ac9c69fd1 100644 --- a/internal/git/localrepo/refs.go +++ b/internal/git/localrepo/refs.go @@ -266,43 +266,3 @@ func (repo *Repo) GetRemoteReferences(ctx context.Context, remote string, opts . return refs, nil } - -// GetDefaultBranch determines the default branch name -func (repo *Repo) GetDefaultBranch(ctx context.Context) (git.ReferenceName, error) { - var stdout bytes.Buffer - err := repo.ExecAndWait(ctx, - git.SubCmd{ - Name: "rev-parse", - Flags: []git.Option{git.Flag{Name: "--symbolic-full-name"}}, - Args: []string{"HEAD"}, - }, - git.WithStdout(&stdout), - ) - headRef := strings.TrimSpace(stdout.String()) - if err != nil { - // If the ref pointed at by HEAD doesn't exist, the rev-parse fails - // returning the string `"HEAD"` - if headRef == "HEAD" { - ok, err := repo.HasRevision(ctx, git.DefaultRef.Revision()) - if err != nil { - return "", err - } - if ok { - return git.DefaultRef, nil - } - - ok, err = repo.HasRevision(ctx, git.LegacyDefaultRef.Revision()) - if err != nil { - return "", err - } - if ok { - return git.LegacyDefaultRef, nil - } - - return "", git.ErrNoDefaultBranch - } - return "", err - } - - return git.ReferenceName(headRef), nil -} diff --git a/internal/git/localrepo/refs_test.go b/internal/git/localrepo/refs_test.go index 652df1a80..a99384e42 100644 --- a/internal/git/localrepo/refs_test.go +++ b/internal/git/localrepo/refs_test.go @@ -454,85 +454,3 @@ func TestRepo_UpdateRef(t *testing.T) { }) } } - -func TestGetDefaultBranch(t *testing.T) { - const testOID = "1a0b36b3cdad1d2ee32457c102a8c0b7056fa863" - - for _, tc := range []struct { - desc string - repo func(t *testing.T) *Repo - expectedName git.ReferenceName - expectedErr error - }{ - { - desc: "default ref", - repo: func(t *testing.T) *Repo { - repo, repoPath := setupRepo(t, true) - oid := gittest.WriteCommit(t, repo.cfg, repoPath, gittest.WithParents(), gittest.WithBranch("apple")) - gittest.WriteCommit(t, repo.cfg, repoPath, gittest.WithParents(oid), gittest.WithBranch("main")) - return repo - }, - expectedName: git.DefaultRef, - }, - { - desc: "legacy default ref", - repo: func(t *testing.T) *Repo { - repo, repoPath := setupRepo(t, true) - oid := gittest.WriteCommit(t, repo.cfg, repoPath, gittest.WithParents(), gittest.WithBranch("apple")) - gittest.WriteCommit(t, repo.cfg, repoPath, gittest.WithParents(oid), gittest.WithBranch("master")) - return repo - }, - expectedName: git.LegacyDefaultRef, - }, - { - desc: "no branches", - repo: func(t *testing.T) *Repo { - repo, _ := setupRepo(t, true) - return repo - }, - expectedErr: git.ErrNoDefaultBranch, - }, - { - desc: "no default branches", - repo: func(t *testing.T) *Repo { - repo, repoPath := setupRepo(t, true) - oid := gittest.WriteCommit(t, repo.cfg, repoPath, gittest.WithParents(), gittest.WithBranch("apple")) - gittest.WriteCommit(t, repo.cfg, repoPath, gittest.WithParents(oid), gittest.WithBranch("banana")) - return repo - }, - expectedErr: git.ErrNoDefaultBranch, - }, - { - desc: "test repo default", - repo: func(t *testing.T) *Repo { - repo, _ := setupRepo(t, false) - return repo - }, - expectedName: git.LegacyDefaultRef, - }, - { - desc: "test repo HEAD set", - repo: func(t *testing.T) *Repo { - repo, repoPath := setupRepo(t, false) - gittest.Exec(t, repo.cfg, "-C", repoPath, "update-ref", "refs/heads/feature", testOID) - gittest.Exec(t, repo.cfg, "-C", repoPath, "symbolic-ref", "HEAD", "refs/heads/feature") - return repo - }, - expectedName: git.NewReferenceNameFromBranchName("feature"), - }, - } { - t.Run(tc.desc, func(t *testing.T) { - ctx, cancel := testhelper.Context() - defer cancel() - - name, err := tc.repo(t).GetDefaultBranch(ctx) - if tc.expectedErr != nil { - require.Equal(t, tc.expectedErr, err) - return - } - - require.NoError(t, err) - require.Equal(t, tc.expectedName, name) - }) - } -} diff --git a/internal/git/repository.go b/internal/git/repository.go index 3ebe6cec4..e5f5b25d5 100644 --- a/internal/git/repository.go +++ b/internal/git/repository.go @@ -13,12 +13,12 @@ const DefaultBranch = "main" // DefaultRef is the reference that GitLab will use if HEAD of the bare repository // is not found, or other edge cases to detect the default branch. -const DefaultRef = ReferenceName("refs/heads/" + DefaultBranch) +var DefaultRef = []byte("refs/heads/" + DefaultBranch) // LegacyDefaultRef is the reference that used to be the default HEAD of the bare // repository. If the default reference is not found, Gitaly will still test the // legacy default. -const LegacyDefaultRef = ReferenceName("refs/heads/master") +var LegacyDefaultRef = []byte("refs/heads/master") var ( // ErrReferenceNotFound represents an error when a reference was not @@ -32,9 +32,6 @@ var ( ErrAlreadyExists = errors.New("already exists") // ErrNotFound represents an error when the resource can't be found. ErrNotFound = errors.New("not found") - // ErrNoDefaultBranch represents an error when the repository has no - // default branch - ErrNoDefaultBranch = errors.New("no default branch") ) // Repository is the common interface of different repository implementations. diff --git a/internal/gitaly/service/commit/commits_by_message.go b/internal/gitaly/service/commit/commits_by_message.go index 6353cf0ee..63a8898c9 100644 --- a/internal/gitaly/service/commit/commits_by_message.go +++ b/internal/gitaly/service/commit/commits_by_message.go @@ -53,11 +53,12 @@ func (s *server) commitsByMessage(in *gitalypb.CommitsByMessageRequest, stream g revision := in.GetRevision() if len(revision) == 0 { - defaultBranch, err := repo.GetDefaultBranch(ctx) + var err error + + revision, err = defaultBranchName(ctx, repo) if err != nil { return err } - revision = []byte(defaultBranch) } var paths []string diff --git a/internal/gitaly/service/commit/find_all_commits.go b/internal/gitaly/service/commit/find_all_commits.go index 26d13e8dc..796c7185b 100644 --- a/internal/gitaly/service/commit/find_all_commits.go +++ b/internal/gitaly/service/commit/find_all_commits.go @@ -10,7 +10,6 @@ import ( ) // We declare this function in variables so that we can override them in our tests -//nolint:staticcheck // FindBranchNames is deprecated but the tests are difficult to refactor var _findBranchNamesFunc = ref.FindBranchNames func (s *server) FindAllCommits(in *gitalypb.FindAllCommitsRequest, stream gitalypb.CommitService_FindAllCommitsServer) error { diff --git a/internal/gitaly/service/commit/find_all_commits_test.go b/internal/gitaly/service/commit/find_all_commits_test.go index 893b33af8..ba7b7ff06 100644 --- a/internal/gitaly/service/commit/find_all_commits_test.go +++ b/internal/gitaly/service/commit/find_all_commits_test.go @@ -16,7 +16,6 @@ import ( func TestSuccessfulFindAllCommitsRequest(t *testing.T) { defer func() { - //nolint:staticcheck // FindBranchNames is deprecated but the tests are difficult to refactor _findBranchNamesFunc = ref.FindBranchNames }() diff --git a/internal/gitaly/service/commit/find_commits.go b/internal/gitaly/service/commit/find_commits.go index 6f6f63b4d..6367aa35d 100644 --- a/internal/gitaly/service/commit/find_commits.go +++ b/internal/gitaly/service/commit/find_commits.go @@ -29,11 +29,11 @@ func (s *server) FindCommits(req *gitalypb.FindCommitsRequest, stream gitalypb.C // Use Gitaly's default branch lookup function because that is already // migrated. if revision := req.Revision; len(revision) == 0 && !req.GetAll() { - defaultBranch, err := repo.GetDefaultBranch(ctx) + var err error + req.Revision, err = defaultBranchName(ctx, repo) if err != nil { return helper.ErrInternal(fmt.Errorf("defaultBranchName: %v", err)) } - req.Revision = []byte(defaultBranch) } // Clients might send empty paths. That is an error for _, path := range req.Paths { diff --git a/internal/gitaly/service/commit/languages.go b/internal/gitaly/service/commit/languages.go index b9ef474d4..fc65f3aef 100644 --- a/internal/gitaly/service/commit/languages.go +++ b/internal/gitaly/service/commit/languages.go @@ -10,6 +10,7 @@ import ( "strings" "gitlab.com/gitlab-org/gitaly/v14/internal/git" + "gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/service/ref" "gitlab.com/gitlab-org/gitaly/v14/internal/helper" "gitlab.com/gitlab-org/gitaly/v14/internal/helper/text" "gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb" @@ -26,11 +27,11 @@ func (s *server) CommitLanguages(ctx context.Context, req *gitalypb.CommitLangua revision := string(req.Revision) if revision == "" { - defaultBranch, err := repo.GetDefaultBranch(ctx) + defaultBranch, err := ref.DefaultBranchName(ctx, repo) if err != nil { return nil, err } - revision = defaultBranch.String() + revision = string(defaultBranch) } commitID, err := s.lookupRevision(ctx, repo, revision) diff --git a/internal/gitaly/service/commit/list_files.go b/internal/gitaly/service/commit/list_files.go index d06c4db8a..7b1cf8b71 100644 --- a/internal/gitaly/service/commit/list_files.go +++ b/internal/gitaly/service/commit/list_files.go @@ -1,7 +1,6 @@ package commit import ( - "errors" "io" "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus" @@ -30,14 +29,16 @@ func (s *server) ListFiles(in *gitalypb.ListFilesRequest, stream gitalypb.Commit revision := string(in.GetRevision()) if len(revision) == 0 { - defaultBranch, err := repo.GetDefaultBranch(stream.Context()) - if errors.Is(err, git.ErrNoDefaultBranch) { - return helper.ErrFailedPreconditionf("repository does not have a default branch") - } else if err != nil { + defaultBranch, err := defaultBranchName(stream.Context(), repo) + if err != nil { return helper.ErrNotFoundf("revision not found %q", revision) } - revision = defaultBranch.String() + if len(defaultBranch) == 0 { + return helper.ErrFailedPreconditionf("repository does not have a default branch") + } + + revision = string(defaultBranch) } contained, err := s.localrepo(repo).HasRevision(stream.Context(), git.Revision(revision)) diff --git a/internal/gitaly/service/commit/list_files_test.go b/internal/gitaly/service/commit/list_files_test.go index e0f69a92b..f07fd7658 100644 --- a/internal/gitaly/service/commit/list_files_test.go +++ b/internal/gitaly/service/commit/list_files_test.go @@ -1,11 +1,14 @@ package commit import ( + "context" "io" "testing" "github.com/stretchr/testify/require" + "gitlab.com/gitlab-org/gitaly/v14/internal/git" "gitlab.com/gitlab-org/gitaly/v14/internal/git/gittest" + "gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/service/ref" "gitlab.com/gitlab-org/gitaly/v14/internal/testhelper" "gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb" "google.golang.org/grpc/codes" @@ -37,9 +40,14 @@ var defaultFiles = [][]byte{ } func TestListFiles_success(t *testing.T) { - cfg, repo, repoPath, client := setupCommitServiceWithRepo(t, true) + defaultBranchName = func(context.Context, git.RepositoryExecutor) ([]byte, error) { + return []byte("test-do-not-touch"), nil + } + defer func() { + defaultBranchName = ref.DefaultBranchName + }() - gittest.Exec(t, cfg, "-C", repoPath, "symbolic-ref", "HEAD", "refs/heads/test-do-not-touch") + _, repo, _, client := setupCommitServiceWithRepo(t, true) tests := []struct { desc string diff --git a/internal/gitaly/service/commit/server.go b/internal/gitaly/service/commit/server.go index 1ada86a09..6669091e0 100644 --- a/internal/gitaly/service/commit/server.go +++ b/internal/gitaly/service/commit/server.go @@ -7,6 +7,7 @@ import ( "gitlab.com/gitlab-org/gitaly/v14/internal/git/repository" "gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/config" "gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/linguist" + "gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/service/ref" "gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/storage" "gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb" ) @@ -20,6 +21,8 @@ type server struct { catfileCache catfile.Cache } +var defaultBranchName = ref.DefaultBranchName + // NewServer creates a new instance of a grpc CommitServiceServer func NewServer( cfg config.Cfg, diff --git a/internal/gitaly/service/operations/apply_patch.go b/internal/gitaly/service/operations/apply_patch.go index a2f2c0d73..e4370a346 100644 --- a/internal/gitaly/service/operations/apply_patch.go +++ b/internal/gitaly/service/operations/apply_patch.go @@ -11,6 +11,7 @@ import ( "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus" "gitlab.com/gitlab-org/gitaly/v14/internal/git" + "gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/service/ref" "gitlab.com/gitlab-org/gitaly/v14/internal/helper" "gitlab.com/gitlab-org/gitaly/v14/internal/helper/text" "gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb" @@ -19,6 +20,8 @@ import ( "google.golang.org/grpc/status" ) +var errNoDefaultBranch = errors.New("no default branch") + func (s *Server) UserApplyPatch(stream gitalypb.OperationService_UserApplyPatchServer) error { firstRequest, err := stream.Recv() if err != nil { @@ -57,13 +60,15 @@ func (s *Server) userApplyPatch(ctx context.Context, header *gitalypb.UserApplyP return fmt.Errorf("resolve target branch: %w", err) } - defaultBranch, err := repo.GetDefaultBranch(ctx) + defaultBranch, err := ref.DefaultBranchName(ctx, repo) if err != nil { return fmt.Errorf("default branch name: %w", err) + } else if defaultBranch == nil { + return errNoDefaultBranch } branchCreated = true - parentCommitID, err = repo.ResolveRevision(ctx, defaultBranch.Revision()+"^{commit}") + parentCommitID, err = repo.ResolveRevision(ctx, git.Revision(defaultBranch)+"^{commit}") if err != nil { return fmt.Errorf("resolve default branch commit: %w", err) } diff --git a/internal/gitaly/service/operations/apply_patch_test.go b/internal/gitaly/service/operations/apply_patch_test.go index bd35bbb83..da7f23268 100644 --- a/internal/gitaly/service/operations/apply_patch_test.go +++ b/internal/gitaly/service/operations/apply_patch_test.go @@ -117,7 +117,7 @@ To restore the original branch and stop patching, run "git am --abort". createFile("file", "base-content"), }, }, - error: status.Error(codes.Internal, "default branch name: no default branch"), + error: status.Error(codes.Internal, "no default branch"), }, { desc: "creating a new branch from HEAD works", @@ -294,11 +294,6 @@ To restore the original branch and stop patching, run "git am --abort". if baseCommit != "" { require.NoError(t, repo.UpdateRef(ctx, tc.baseReference, baseCommit, git.ZeroOID)) - - // Now that the repo has a ref, setup the default branch - if _, ok := tc.baseReference.Branch(); ok { - gittest.Exec(t, cfg, "-C", repoPath, "symbolic-ref", "HEAD", tc.baseReference.String()) - } } if tc.extraBranches != nil { diff --git a/internal/gitaly/service/ref/refs.go b/internal/gitaly/service/ref/refs.go index c9fd8d56a..3a8e15c0f 100644 --- a/internal/gitaly/service/ref/refs.go +++ b/internal/gitaly/service/ref/refs.go @@ -21,6 +21,13 @@ const ( tagFormat = "%(objectname) %(objecttype) %(refname:lstrip=2)" ) +var ( + // We declare the following functions in variables so that we can override them in our tests + headReference = _headReference + // FindBranchNames is exported to be used in other packages + FindBranchNames = _findBranchNames +) + type findRefsOpts struct { cmdArgs []git.Option delim byte @@ -56,10 +63,7 @@ func (s *server) findRefs(ctx context.Context, writer lines.Sender, repo git.Rep return cmd.Wait() } -// FindBranchNames returns all branch names. -// -// Deprecated: Use localrepo.Repo.GetBranches instead. -func FindBranchNames(ctx context.Context, repo git.RepositoryExecutor) ([][]byte, error) { +func _findBranchNames(ctx context.Context, repo git.RepositoryExecutor) ([][]byte, error) { var names [][]byte cmd, err := repo.Exec(ctx, git.SubCmd{ @@ -87,6 +91,38 @@ func FindBranchNames(ctx context.Context, repo git.RepositoryExecutor) ([][]byte return names, nil } +func _headReference(ctx context.Context, repo git.RepositoryExecutor) ([]byte, error) { + var headRef []byte + + cmd, err := repo.Exec(ctx, git.SubCmd{ + Name: "rev-parse", + Flags: []git.Option{git.Flag{Name: "--symbolic-full-name"}}, + Args: []string{"HEAD"}, + }) + if err != nil { + return nil, err + } + + scanner := bufio.NewScanner(cmd) + scanner.Scan() + if err := scanner.Err(); err != nil { + return nil, err + } + headRef = scanner.Bytes() + + if err := cmd.Wait(); err != nil { + // If the ref pointed at by HEAD doesn't exist, the rev-parse fails + // returning the string `"HEAD"`, so we return `nil` without error. + if bytes.Equal(headRef, []byte("HEAD")) { + return nil, nil + } + + return nil, err + } + + return headRef, nil +} + // SetDefaultBranchRef overwrites the default branch ref for the repository func SetDefaultBranchRef(ctx context.Context, repo git.RepositoryExecutor, ref string, cfg config.Cfg) error { if err := repo.ExecAndWait(ctx, git.SubCmd{ @@ -98,16 +134,65 @@ func SetDefaultBranchRef(ctx context.Context, repo git.RepositoryExecutor, ref s return nil } +// DefaultBranchName looks up the name of the default branch given a repoPath +func DefaultBranchName(ctx context.Context, repo git.RepositoryExecutor) ([]byte, error) { + branches, err := FindBranchNames(ctx, repo) + if err != nil { + return nil, err + } + + // Return empty ref name if there are no branches + if len(branches) == 0 { + return nil, nil + } + + // Return first branch name if there's only one + if len(branches) == 1 { + return branches[0], nil + } + + hasDefaultRef, hasLegacyDefaultRef := false, false + headRef, err := headReference(ctx, repo) + if err != nil { + return nil, err + } + + for _, branch := range branches { + // Return HEAD if it exists and corresponds to a branch + if headRef != nil && bytes.Equal(headRef, branch) { + return headRef, nil + } + + if bytes.Equal(branch, git.DefaultRef) { + hasDefaultRef = true + } + + hasLegacyDefaultRef = hasLegacyDefaultRef || bytes.Equal(branch, git.LegacyDefaultRef) + } + + // Return the default ref if it exists + if hasDefaultRef { + return git.DefaultRef, nil + } + + if hasLegacyDefaultRef { + return git.LegacyDefaultRef, nil + } + + // If all else fails, return the first branch name + return branches[0], nil +} + // FindDefaultBranchName returns the default branch name for the given repository func (s *server) FindDefaultBranchName(ctx context.Context, in *gitalypb.FindDefaultBranchNameRequest) (*gitalypb.FindDefaultBranchNameResponse, error) { repo := s.localrepo(in.GetRepository()) - defaultBranch, err := repo.GetDefaultBranch(ctx) - if err != nil && !errors.Is(err, git.ErrNoDefaultBranch) { + defaultBranchName, err := DefaultBranchName(ctx, repo) + if err != nil { return nil, helper.ErrInternal(err) } - return &gitalypb.FindDefaultBranchNameResponse{Name: []byte(defaultBranch)}, nil + return &gitalypb.FindDefaultBranchNameResponse{Name: defaultBranchName}, nil } func parseSortKey(sortKey gitalypb.FindLocalBranchesRequest_SortBy) string { @@ -171,12 +256,12 @@ func (s *server) findAllBranches(in *gitalypb.FindAllBranchesRequest, stream git patterns := []string{"refs/heads", "refs/remotes"} if in.MergedOnly { - defaultBranch, err := repo.GetDefaultBranch(stream.Context()) + defaultBranchName, err := DefaultBranchName(stream.Context(), repo) if err != nil { return err } - args = append(args, git.Flag{Name: fmt.Sprintf("--merged=%s", defaultBranch.String())}) + args = append(args, git.Flag{Name: fmt.Sprintf("--merged=%s", string(defaultBranchName))}) if len(in.MergedBranches) > 0 { patterns = nil diff --git a/internal/gitaly/service/ref/refs_test.go b/internal/gitaly/service/ref/refs_test.go index d7560ec4c..d83e0d731 100644 --- a/internal/gitaly/service/ref/refs_test.go +++ b/internal/gitaly/service/ref/refs_test.go @@ -3,8 +3,10 @@ package ref import ( "bufio" "bytes" + "context" "fmt" "io" + "io/ioutil" "strings" "testing" @@ -218,6 +220,37 @@ func TestInvalidRepoFindAllTagNamesRequest(t *testing.T) { } } +func TestHeadReference(t *testing.T) { + cfg, repo, _ := testcfg.BuildWithRepo(t) + + ctx, cancel := testhelper.Context() + defer cancel() + + headRef, err := headReference(ctx, localrepo.NewTestRepo(t, cfg, repo)) + require.NoError(t, err) + + require.Equal(t, git.LegacyDefaultRef, headRef) +} + +func TestHeadReferenceWithNonExistingHead(t *testing.T) { + cfg, repo, repoPath := testcfg.BuildWithRepo(t) + + // Write bad HEAD + require.NoError(t, ioutil.WriteFile(repoPath+"/HEAD", []byte("ref: refs/heads/nonexisting"), 0o644)) + defer func() { + // Restore HEAD + require.NoError(t, ioutil.WriteFile(repoPath+"/HEAD", []byte("ref: refs/heads/master"), 0o644)) + }() + + ctx, cancel := testhelper.Context() + defer cancel() + headRef, err := headReference(ctx, localrepo.NewTestRepo(t, cfg, repo)) + require.NoError(t, err) + if headRef != nil { + t.Fatal("Expected HEAD reference to be nil, got '", string(headRef), "'") + } +} + func TestSetDefaultBranchRef(t *testing.T) { cfg, repoProto, _ := testcfg.BuildWithRepo(t) repo := localrepo.NewTestRepo(t, cfg, repoProto) @@ -235,7 +268,7 @@ func TestSetDefaultBranchRef(t *testing.T) { { desc: "unknown ref", ref: "refs/heads/non_existent_ref", - expectedRef: git.LegacyDefaultRef.String(), + expectedRef: string(git.LegacyDefaultRef), }, } @@ -246,14 +279,88 @@ func TestSetDefaultBranchRef(t *testing.T) { require.NoError(t, SetDefaultBranchRef(ctx, repo, tc.ref, cfg)) - newRef, err := repo.GetDefaultBranch(ctx) + newRef, err := DefaultBranchName(ctx, repo) require.NoError(t, err) - require.Equal(t, tc.expectedRef, newRef.String()) + require.Equal(t, tc.expectedRef, string(newRef)) }) } } +func TestDefaultBranchName(t *testing.T) { + // We are going to override these functions during this test. Restore them after we're done + defer func() { + FindBranchNames = _findBranchNames + headReference = _headReference + }() + + cfg, repoProto, _ := testcfg.BuildWithRepo(t) + repo := localrepo.NewTestRepo(t, cfg, repoProto) + + testCases := []struct { + desc string + findBranchNames func(context.Context, git.RepositoryExecutor) ([][]byte, error) + headReference func(context.Context, git.RepositoryExecutor) ([]byte, error) + expected []byte + }{ + { + desc: "Get first branch when only one branch exists", + expected: []byte("refs/heads/foo"), + findBranchNames: func(context.Context, git.RepositoryExecutor) ([][]byte, error) { + return [][]byte{[]byte("refs/heads/foo")}, nil + }, + headReference: func(context.Context, git.RepositoryExecutor) ([]byte, error) { return nil, nil }, + }, + { + desc: "Get empy ref if no branches exists", + expected: nil, + findBranchNames: func(context.Context, git.RepositoryExecutor) ([][]byte, error) { + return [][]byte{}, nil + }, + headReference: func(context.Context, git.RepositoryExecutor) ([]byte, error) { return nil, nil }, + }, + { + desc: "Get the name of the head reference when more than one branch exists", + expected: []byte("refs/heads/bar"), + findBranchNames: func(context.Context, git.RepositoryExecutor) ([][]byte, error) { + return [][]byte{[]byte("refs/heads/foo"), []byte("refs/heads/bar")}, nil + }, + headReference: func(context.Context, git.RepositoryExecutor) ([]byte, error) { + return []byte("refs/heads/bar"), nil + }, + }, + { + desc: "Get `ref/heads/master` when several branches exist", + expected: git.LegacyDefaultRef, + findBranchNames: func(context.Context, git.RepositoryExecutor) ([][]byte, error) { + return [][]byte{[]byte("refs/heads/foo"), []byte("refs/heads/master"), []byte("refs/heads/bar")}, nil + }, + headReference: func(context.Context, git.RepositoryExecutor) ([]byte, error) { return nil, nil }, + }, + { + desc: "Get the name of the first branch when several branches exists and no other conditions are met", + expected: []byte("refs/heads/foo"), + findBranchNames: func(context.Context, git.RepositoryExecutor) ([][]byte, error) { + return [][]byte{[]byte("refs/heads/foo"), []byte("refs/heads/bar"), []byte("refs/heads/baz")}, nil + }, + headReference: func(context.Context, git.RepositoryExecutor) ([]byte, error) { return nil, nil }, + }, + } + + for _, testCase := range testCases { + FindBranchNames = testCase.findBranchNames + headReference = testCase.headReference + + ctx, cancel := testhelper.Context() + defer cancel() + defaultBranch, err := DefaultBranchName(ctx, repo) + require.NoError(t, err) + if !bytes.Equal(defaultBranch, testCase.expected) { + t.Fatalf("%s: expected %s, got %s instead", testCase.desc, testCase.expected, defaultBranch) + } + } +} + func TestSuccessfulFindDefaultBranchName(t *testing.T) { cfg, repo, repoPath, client := setupRefService(t) rpcRequest := &gitalypb.FindDefaultBranchNameRequest{Repository: repo} @@ -268,7 +375,7 @@ func TestSuccessfulFindDefaultBranchName(t *testing.T) { r, err := client.FindDefaultBranchName(ctx, rpcRequest) require.NoError(t, err) - require.Equal(t, r.GetName(), []byte(git.DefaultRef)) + require.Equal(t, r.GetName(), git.DefaultRef) } func TestSuccessfulFindDefaultBranchNameLegacy(t *testing.T) { @@ -280,7 +387,7 @@ func TestSuccessfulFindDefaultBranchNameLegacy(t *testing.T) { r, err := client.FindDefaultBranchName(ctx, rpcRequest) require.NoError(t, err) - require.Equal(t, r.GetName(), []byte(git.LegacyDefaultRef)) + require.Equal(t, r.GetName(), git.LegacyDefaultRef) } func TestEmptyFindDefaultBranchNameRequest(t *testing.T) { diff --git a/internal/gitaly/service/remote/fetch_internal_remote.go b/internal/gitaly/service/remote/fetch_internal_remote.go index c49b530c1..7eafcf23b 100644 --- a/internal/gitaly/service/remote/fetch_internal_remote.go +++ b/internal/gitaly/service/remote/fetch_internal_remote.go @@ -8,7 +8,6 @@ import ( "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus" "gitlab.com/gitlab-org/gitaly/v14/client" - "gitlab.com/gitlab-org/gitaly/v14/internal/git" "gitlab.com/gitlab-org/gitaly/v14/internal/git/localrepo" "gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/config" "gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/service/ref" @@ -62,12 +61,12 @@ func FetchInternalRemote( return status.Errorf(codes.Internal, "FetchInternalRemote: remote default branch: %v", err) } - defaultBranch, err := repo.GetDefaultBranch(ctx) - if err != nil && !errors.Is(err, git.ErrNoDefaultBranch) { + defaultBranch, err := ref.DefaultBranchName(ctx, repo) + if err != nil { return status.Errorf(codes.Internal, "FetchInternalRemote: default branch: %v", err) } - if defaultBranch.String() != string(remoteDefaultBranch) { + if !bytes.Equal(defaultBranch, remoteDefaultBranch) { if err := ref.SetDefaultBranchRef(ctx, repo, string(remoteDefaultBranch), cfg); err != nil { return status.Errorf(codes.Internal, "FetchInternalRemote: set default branch: %v", err) } diff --git a/internal/gitaly/service/remote/update_remote_mirror.go b/internal/gitaly/service/remote/update_remote_mirror.go index 3fd6e5a16..8ab8f4ac9 100644 --- a/internal/gitaly/service/remote/update_remote_mirror.go +++ b/internal/gitaly/service/remote/update_remote_mirror.go @@ -10,6 +10,7 @@ import ( "gitlab.com/gitlab-org/gitaly/v14/internal/git" "gitlab.com/gitlab-org/gitaly/v14/internal/git/localrepo" + "gitlab.com/gitlab-org/gitaly/v14/internal/gitaly/service/ref" "gitlab.com/gitlab-org/gitaly/v14/internal/helper" "gitlab.com/gitlab-org/gitaly/v14/internal/helper/text" "gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb" @@ -102,8 +103,8 @@ func (s *server) updateRemoteMirror(stream gitalypb.RemoteService_UpdateRemoteMi return fmt.Errorf("get local references: %w", err) } - defaultBranch, err := repo.GetDefaultBranch(ctx) - if err != nil && !errors.Is(err, git.ErrNoDefaultBranch) { + defaultBranch, err := ref.DefaultBranchName(ctx, repo) + if err != nil { return fmt.Errorf("get default branch: %w", err) } @@ -194,7 +195,7 @@ func (s *server) updateRemoteMirror(stream gitalypb.RemoteService_UpdateRemoteMi } refspecs = append(refspecs, prefix+reference.String()) - if reference == defaultBranch { + if reference == git.ReferenceName(defaultBranch) { // The default branch needs to be pushed in the first batch of refspecs as some features // depend on it existing in the repository. The default branch may not exist in the repo // yet if this is the first mirroring push. |