diff --git a/services/agit/agit.go b/services/agit/agit.go index 35bfb33402..4c970ee78a 100644 --- a/services/agit/agit.go +++ b/services/agit/agit.go @@ -101,6 +101,21 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git. return nil, fmt.Errorf("failed to get unmerged AGit flow pull request in repository %q: %w", repo.FullName(), err) } + // Check if the changes are already in the target branch. + stdout, _, gitErr := git.NewCommand(ctx, "branch", "--contains").AddDynamicArguments(opts.NewCommitIDs[i], baseBranchName).RunStdString(&git.RunOpts{Dir: repo.RepoPath()}) + if gitErr != nil { + return nil, fmt.Errorf("failed to check if the target branch already contains the new commit in repository %q: %w", repo.FullName(), err) + } + if len(stdout) > 0 { + results = append(results, private.HookProcReceiveRefResult{ + OriginalRef: opts.RefFullNames[i], + OldOID: opts.OldCommitIDs[i], + NewOID: opts.NewCommitIDs[i], + Err: "The target branch already contains this commit", + }) + continue + } + // Automatically fill out the title and the description from the first commit. shouldGetCommit := len(title) == 0 || len(description) == 0 diff --git a/tests/integration/git_test.go b/tests/integration/git_test.go index d02427ffcf..125950a3a5 100644 --- a/tests/integration/git_test.go +++ b/tests/integration/git_test.go @@ -937,13 +937,13 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB }) }) + upstreamGitRepo, err := git.OpenRepository(git.DefaultContext, filepath.Join(setting.RepoRootPath, ctx.Username, ctx.Reponame+".git")) + require.NoError(t, err) + defer upstreamGitRepo.Close() + t.Run("Force push", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - upstreamGitRepo, err := git.OpenRepository(git.DefaultContext, filepath.Join(setting.RepoRootPath, ctx.Username, ctx.Reponame+".git")) - require.NoError(t, err) - defer upstreamGitRepo.Close() - _, _, gitErr := git.NewCommand(git.DefaultContext, "push", "origin").AddDynamicArguments("HEAD:refs/for/master/" + headBranch + "-force-push").RunStdString(&git.RunOpts{Dir: dstPath}) require.NoError(t, gitErr) @@ -984,6 +984,21 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB }) }) + t.Run("Branch already contains commit", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + branchCommit, err := upstreamGitRepo.GetBranchCommit("master") + require.NoError(t, err) + + _, _, gitErr := git.NewCommand(git.DefaultContext, "reset", "--hard").AddDynamicArguments(branchCommit.ID.String() + "~1").RunStdString(&git.RunOpts{Dir: dstPath}) + require.NoError(t, gitErr) + + _, stdErr, gitErr := git.NewCommand(git.DefaultContext, "push", "origin").AddDynamicArguments("HEAD:refs/for/master/" + headBranch + "-already-contains").RunStdString(&git.RunOpts{Dir: dstPath}) + assert.Error(t, gitErr) + + assert.Contains(t, stdErr, "already contains this commit") + }) + t.Run("Merge", doAPIMergePullRequest(*ctx, ctx.Username, ctx.Reponame, pr1.Index)) t.Run("CheckoutMasterAgain", doGitCheckoutBranch(dstPath, "master")) }