From 6f83d8d07b15084f6b9cf046108bea63fb13a084 Mon Sep 17 00:00:00 2001 From: Enver Bisevac Date: Thu, 16 Nov 2023 17:51:08 +0000 Subject: [PATCH] [code-953] diff merge base with annotated tags (#809) --- git/adapter/diff.go | 13 +++- git/adapter/diff_test.go | 158 ++++++++++++++++++++++++++++++++++++++ git/adapter/merge.go | 4 +- git/adapter/merge_test.go | 123 +++++++++++++++++++++++++++++ git/adapter/setup_test.go | 133 ++++++++++++++++++++++++++++++++ 5 files changed, 428 insertions(+), 3 deletions(-) create mode 100644 git/adapter/diff_test.go create mode 100644 git/adapter/merge_test.go create mode 100644 git/adapter/setup_test.go diff --git a/git/adapter/diff.go b/git/adapter/diff.go index f37179c66..a78c7112b 100644 --- a/git/adapter/diff.go +++ b/git/adapter/diff.go @@ -39,6 +39,17 @@ func (a Adapter) RawDiff( if repoPath == "" { return ErrRepositoryPathEmpty } + + baseTag, err := a.GetAnnotatedTag(ctx, repoPath, baseRef) + if err == nil { + baseRef = baseTag.TargetSha + } + + headTag, err := a.GetAnnotatedTag(ctx, repoPath, headRef) + if err == nil { + headRef = headTag.TargetSha + } + args := make([]string, 0, 8) args = append(args, "diff", "-M", "--full-index") if mergeBase { @@ -57,7 +68,7 @@ func (a Adapter) RawDiff( if errbuf.Len() > 0 { err = &runStdError{err: err, stderr: errbuf.String()} } - return processGiteaErrorf(err, "git diff failed between '%s' and '%s' with err: %v", baseRef, headRef, err) + return processGiteaErrorf(err, "git diff failed between '%s' and '%s'", baseRef, headRef) } return nil } diff --git a/git/adapter/diff_test.go b/git/adapter/diff_test.go new file mode 100644 index 000000000..e6e9bc574 --- /dev/null +++ b/git/adapter/diff_test.go @@ -0,0 +1,158 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package adapter_test + +import ( + "bytes" + "context" + "testing" + + "github.com/harness/gitness/git/adapter" +) + +func TestAdapter_RawDiff(t *testing.T) { + git := setupGit(t) + repo, teardown := setupRepo(t, git, "testrawdiff") + defer teardown() + + baseBranch := "main" + // write file to main branch + parentSHA := writeFile(t, repo, "file.txt", "some content", nil) + + err := repo.SetReference("refs/heads/"+baseBranch, parentSHA.String()) + if err != nil { + t.Fatalf("failed updating reference '%s': %v", baseBranch, err) + } + + baseTag := "0.0.1" + err = repo.CreateAnnotatedTag(baseTag, "test tag 1", parentSHA.String()) + if err != nil { + t.Fatalf("error creating annotated tag '%s': %v", baseTag, err) + } + + headBranch := "dev" + + // create branch + err = repo.CreateBranch(headBranch, baseBranch) + if err != nil { + t.Fatalf("failed creating a branch '%s': %v", headBranch, err) + } + + // write file to main branch + sha := writeFile(t, repo, "file.txt", "new content", []string{parentSHA.String()}) + + err = repo.SetReference("refs/heads/"+headBranch, sha.String()) + if err != nil { + t.Fatalf("failed updating reference '%s': %v", headBranch, err) + } + + headTag := "0.0.2" + err = repo.CreateAnnotatedTag(headTag, "test tag 2", sha.String()) + if err != nil { + t.Fatalf("error creating annotated tag '%s': %v", headTag, err) + } + + want := `diff --git a/file.txt b/file.txt +index f0eec86f614944a81f87d879ebdc9a79aea0d7ea..47d2739ba2c34690248c8f91b84bb54e8936899a 100644 +--- a/file.txt ++++ b/file.txt +@@ -1 +1 @@ +-some content +\ No newline at end of file ++new content +\ No newline at end of file +` + + type args struct { + ctx context.Context + repoPath string + baseRef string + headRef string + mergeBase bool + } + tests := []struct { + name string + adapter adapter.Adapter + args args + wantW string + wantErr bool + }{ + { + name: "test branches", + adapter: git, + args: args{ + ctx: context.Background(), + repoPath: repo.Path, + baseRef: baseBranch, + headRef: headBranch, + mergeBase: false, + }, + wantW: want, + wantErr: false, + }, + { + name: "test annotated tag", + adapter: git, + args: args{ + ctx: context.Background(), + repoPath: repo.Path, + baseRef: baseTag, + headRef: headTag, + mergeBase: false, + }, + wantW: want, + wantErr: false, + }, + { + name: "test branches using merge-base", + adapter: git, + args: args{ + ctx: context.Background(), + repoPath: repo.Path, + baseRef: baseBranch, + headRef: headBranch, + mergeBase: true, + }, + wantW: want, + wantErr: false, + }, + { + name: "test annotated tag using merge-base", + adapter: git, + args: args{ + ctx: context.Background(), + repoPath: repo.Path, + baseRef: baseTag, + headRef: headTag, + mergeBase: true, + }, + wantW: want, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + w := &bytes.Buffer{} + err := tt.adapter.RawDiff(tt.args.ctx, tt.args.repoPath, tt.args.baseRef, tt.args.headRef, tt.args.mergeBase, w) + if (err != nil) != tt.wantErr { + t.Errorf("RawDiff() error = %v, wantErr %v", err, tt.wantErr) + return + } + if gotW := w.String(); gotW != tt.wantW { + t.Errorf("RawDiff() gotW = %v, want %v", gotW, tt.wantW) + } + }) + } +} diff --git a/git/adapter/merge.go b/git/adapter/merge.go index 03c659cbe..c37b53c49 100644 --- a/git/adapter/merge.go +++ b/git/adapter/merge.go @@ -547,9 +547,9 @@ func (a Adapter) GetMergeBase( } } - stdout, _, err := git.NewCommand(ctx, "merge-base", "--", base, head).RunStdString(&git.RunOpts{Dir: repoPath}) + stdout, stderr, err := git.NewCommand(ctx, "merge-base", "--", base, head).RunStdString(&git.RunOpts{Dir: repoPath}) if err != nil { - return "", "", processGiteaErrorf(err, "failed to get merge-base") + return "", "", processGiteaErrorf(err, "failed to get merge-base: %v", stderr) } return strings.TrimSpace(stdout), base, nil diff --git a/git/adapter/merge_test.go b/git/adapter/merge_test.go new file mode 100644 index 000000000..c5620a87a --- /dev/null +++ b/git/adapter/merge_test.go @@ -0,0 +1,123 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package adapter_test + +import ( + "context" + "testing" + + "github.com/harness/gitness/git/adapter" +) + +func TestAdapter_GetMergeBase(t *testing.T) { + git := setupGit(t) + repo, teardown := setupRepo(t, git, "testmergebase") + defer teardown() + + baseBranch := "main" + // write file to main branch + parentSHA := writeFile(t, repo, "file1.txt", "some content", nil) + + err := repo.SetReference("refs/heads/"+baseBranch, parentSHA.String()) + if err != nil { + t.Fatalf("failed updating reference '%s': %v", baseBranch, err) + } + + baseTag := "0.0.1" + err = repo.CreateAnnotatedTag(baseTag, "test tag 1", parentSHA.String()) + if err != nil { + t.Fatalf("error creating annotated tag '%s': %v", baseTag, err) + } + + headBranch := "dev" + + // create branch + err = repo.CreateBranch(headBranch, baseBranch) + if err != nil { + t.Fatalf("failed creating a branch '%s': %v", headBranch, err) + } + + // write file to main branch + sha := writeFile(t, repo, "file1.txt", "new content", []string{parentSHA.String()}) + + err = repo.SetReference("refs/heads/"+headBranch, sha.String()) + if err != nil { + t.Fatalf("failed updating reference '%s': %v", headBranch, err) + } + + headTag := "0.0.2" + err = repo.CreateAnnotatedTag(headTag, "test tag 2", sha.String()) + if err != nil { + t.Fatalf("error creating annotated tag '%s': %v", headTag, err) + } + + type args struct { + ctx context.Context + repoPath string + remote string + base string + head string + } + tests := []struct { + name string + git adapter.Adapter + args args + want string + want1 string + wantErr bool + }{ + { + name: "git merge base using branch names", + git: git, + args: args{ + ctx: context.Background(), + repoPath: repo.Path, + remote: "", + base: baseBranch, + head: headBranch, + }, + want: parentSHA.String(), + want1: baseBranch, + }, + { + name: "git merge base using annotated tags", + git: git, + args: args{ + ctx: context.Background(), + repoPath: repo.Path, + remote: "", + base: baseTag, + head: headTag, + }, + want: parentSHA.String(), + want1: baseTag, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, got1, err := tt.git.GetMergeBase(tt.args.ctx, tt.args.repoPath, tt.args.remote, tt.args.base, tt.args.head) + if (err != nil) != tt.wantErr { + t.Errorf("GetMergeBase() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("GetMergeBase() got = %v, want %v", got, tt.want) + } + if got1 != tt.want1 { + t.Errorf("GetMergeBase() got1 = %v, want %v", got1, tt.want1) + } + }) + } +} diff --git a/git/adapter/setup_test.go b/git/adapter/setup_test.go new file mode 100644 index 000000000..7a68e04cc --- /dev/null +++ b/git/adapter/setup_test.go @@ -0,0 +1,133 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package adapter_test + +import ( + "context" + "os" + "path" + "strings" + "testing" + + "github.com/harness/gitness/git/adapter" + "github.com/harness/gitness/types" + + gitea "code.gitea.io/gitea/modules/git" +) + +type teardown func() + +var ( + testAuthor = &gitea.Signature{ + Name: "test", + Email: "test@test.com", + } + + testCommitter = &gitea.Signature{ + Name: "test", + Email: "test@test.com", + } +) + +func setupGit(t *testing.T) adapter.Adapter { + t.Helper() + config := &types.Config{} + gogitProvider := adapter.ProvideGoGitRepoProvider() + git, err := adapter.New( + gogitProvider, + adapter.ProvideLastCommitCache( + config, + nil, + gogitProvider, + ), + ) + if err != nil { + t.Fatalf("error initializing repository: %v", err) + } + return git +} + +func setupRepo(t *testing.T, git adapter.Adapter, name string) (*gitea.Repository, teardown) { + t.Helper() + ctx := context.Background() + + tmpdir := os.TempDir() + + repoPath := path.Join(tmpdir, "test_repos", name) + + err := git.InitRepository(ctx, repoPath, true) + if err != nil { + t.Fatalf("error initializing repository: %v", err) + } + + repo, err := git.OpenRepository(ctx, repoPath) + if err != nil { + t.Fatalf("error opening repository '%s': %v", name, err) + } + + err = repo.SetDefaultBranch("main") + if err != nil { + t.Fatalf("error setting default branch 'main': %v", err) + } + + err = git.Config(ctx, repoPath, "user.email", testCommitter.Email) + if err != nil { + t.Fatalf("error setting config user.email %s: %v", testCommitter.Email, err) + } + + err = git.Config(ctx, repoPath, "user.name", testCommitter.Name) + if err != nil { + t.Fatalf("error setting config user.name %s: %v", testCommitter.Name, err) + } + + return repo, func() { + if err := os.RemoveAll(repoPath); err != nil { + t.Errorf("error while removeng the repository '%s'", repoPath) + } + } +} + +func writeFile( + t *testing.T, + repo *gitea.Repository, + path string, + content string, + parents []string, +) gitea.SHA1 { + t.Helper() + sha, err := repo.HashObject(strings.NewReader(content)) + if err != nil { + t.Fatalf("failed to hash object: %v", err) + } + + err = repo.AddObjectToIndex("100644", sha, path) + if err != nil { + t.Fatalf("failed to add object to index: %v", err) + } + + tree, err := repo.WriteTree() + if err != nil { + t.Fatalf("failed to write tree: %v", err) + } + + sha, err = repo.CommitTree(testAuthor, testCommitter, tree, gitea.CommitTreeOpts{ + Message: "write file operation", + Parents: parents, + }) + if err != nil { + t.Fatalf("failed to commit tree: %v", err) + } + return sha +}