mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00:00 
			
		
		
		
	* Fixes issue #19603 (Not able to merge commit in PR when branches content is same, but different commit id) * fill HeadCommitID in PullRequest * compare real commits ID as check for merging * based on @zeripath patch in #19738
This commit is contained in:
		 Ing. Jaroslav Šafka
					Ing. Jaroslav Šafka
				
			
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			 GitHub
						GitHub
					
				
			
						parent
						
							b7c6ec91ba
						
					
				
				
					commit
					8420c1bf4c
				
			| @@ -105,7 +105,11 @@ func doAPICreateCommitStatus(ctx APITestContext, commitID string, status api.Com | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestPullCreate_EmptyChangesWithCommits(t *testing.T) { | func TestPullCreate_EmptyChangesWithDifferentCommits(t *testing.T) { | ||||||
|  | 	// Merge must continue if commits SHA are different, even if content is same | ||||||
|  | 	// Reason: gitflow and merging master back into develop, where is high possiblity, there are no changes | ||||||
|  | 	// but just commit saying "Merge branch". And this meta commit can be also tagged, | ||||||
|  | 	// so we need to have this meta commit also in develop branch. | ||||||
| 	onGiteaRun(t, func(t *testing.T, u *url.URL) { | 	onGiteaRun(t, func(t *testing.T, u *url.URL) { | ||||||
| 		session := loginUser(t, "user1") | 		session := loginUser(t, "user1") | ||||||
| 		testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | 		testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | ||||||
| @@ -126,6 +130,28 @@ func TestPullCreate_EmptyChangesWithCommits(t *testing.T) { | |||||||
| 		doc := NewHTMLParser(t, resp.Body) | 		doc := NewHTMLParser(t, resp.Body) | ||||||
|  |  | ||||||
| 		text := strings.TrimSpace(doc.doc.Find(".merge-section").Text()) | 		text := strings.TrimSpace(doc.doc.Find(".merge-section").Text()) | ||||||
| 		assert.Contains(t, text, "This branch is equal with the target branch.") | 		assert.Contains(t, text, "This pull request can be merged automatically.") | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestPullCreate_EmptyChangesWithSameCommits(t *testing.T) { | ||||||
|  | 	onGiteaRun(t, func(t *testing.T, u *url.URL) { | ||||||
|  | 		session := loginUser(t, "user1") | ||||||
|  | 		testRepoFork(t, session, "user2", "repo1", "user1", "repo1") | ||||||
|  | 		testCreateBranch(t, session, "user1", "repo1", "branch/master", "status1", http.StatusSeeOther) | ||||||
|  | 		url := path.Join("user1", "repo1", "compare", "master...status1") | ||||||
|  | 		req := NewRequestWithValues(t, "POST", url, | ||||||
|  | 			map[string]string{ | ||||||
|  | 				"_csrf": GetCSRF(t, session, url), | ||||||
|  | 				"title": "pull request from status1", | ||||||
|  | 			}, | ||||||
|  | 		) | ||||||
|  | 		session.MakeRequest(t, req, http.StatusSeeOther) | ||||||
|  | 		req = NewRequest(t, "GET", "/user1/repo1/pulls/1") | ||||||
|  | 		resp := session.MakeRequest(t, req, http.StatusOK) | ||||||
|  | 		doc := NewHTMLParser(t, resp.Body) | ||||||
|  |  | ||||||
|  | 		text := strings.TrimSpace(doc.doc.Find(".merge-section").Text()) | ||||||
|  | 		assert.Contains(t, text, "This branch is already included in the target branch. There is nothing to merge.") | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -122,6 +122,7 @@ const ( | |||||||
| 	PullRequestStatusManuallyMerged | 	PullRequestStatusManuallyMerged | ||||||
| 	PullRequestStatusError | 	PullRequestStatusError | ||||||
| 	PullRequestStatusEmpty | 	PullRequestStatusEmpty | ||||||
|  | 	PullRequestStatusAncestor | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // PullRequestFlow the flow of pull request | // PullRequestFlow the flow of pull request | ||||||
| @@ -423,6 +424,11 @@ func (pr *PullRequest) IsEmpty() bool { | |||||||
| 	return pr.Status == PullRequestStatusEmpty | 	return pr.Status == PullRequestStatusEmpty | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // IsAncestor returns true if the Head Commit of this PR is an ancestor of the Base Commit | ||||||
|  | func (pr *PullRequest) IsAncestor() bool { | ||||||
|  | 	return pr.Status == PullRequestStatusAncestor | ||||||
|  | } | ||||||
|  |  | ||||||
| // SetMerged sets a pull request to merged and closes the corresponding issue | // SetMerged sets a pull request to merged and closes the corresponding issue | ||||||
| func (pr *PullRequest) SetMerged(ctx context.Context) (bool, error) { | func (pr *PullRequest) SetMerged(ctx context.Context) (bool, error) { | ||||||
| 	if pr.HasMerged { | 	if pr.HasMerged { | ||||||
|   | |||||||
| @@ -1532,7 +1532,8 @@ pulls.remove_prefix = Remove <strong>%s</strong> prefix | |||||||
| pulls.data_broken = This pull request is broken due to missing fork information. | pulls.data_broken = This pull request is broken due to missing fork information. | ||||||
| pulls.files_conflicted = This pull request has changes conflicting with the target branch. | pulls.files_conflicted = This pull request has changes conflicting with the target branch. | ||||||
| pulls.is_checking = "Merge conflict checking is in progress. Try again in few moments." | pulls.is_checking = "Merge conflict checking is in progress. Try again in few moments." | ||||||
| pulls.is_empty = "This branch is equal with the target branch." | pulls.is_ancestor = "This branch is already included in the target branch. There is nothing to merge." | ||||||
|  | pulls.is_empty = "The changes on this branch are already on the target branch. This will be an empty commit." | ||||||
| pulls.required_status_check_failed = Some required checks were not successful. | pulls.required_status_check_failed = Some required checks were not successful. | ||||||
| pulls.required_status_check_missing = Some required checks are missing. | pulls.required_status_check_missing = Some required checks are missing. | ||||||
| pulls.required_status_check_administrator = As an administrator, you may still merge this pull request. | pulls.required_status_check_administrator = As an administrator, you may still merge this pull request. | ||||||
|   | |||||||
| @@ -89,7 +89,7 @@ func CheckPullMergable(stdCtx context.Context, doer *user_model.User, perm *acce | |||||||
| 			return ErrIsWorkInProgress | 			return ErrIsWorkInProgress | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if !pr.CanAutoMerge() { | 		if !pr.CanAutoMerge() && !pr.IsEmpty() { | ||||||
| 			return ErrNotMergableState | 			return ErrNotMergableState | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -87,6 +87,14 @@ func TestPatch(pr *issues_model.PullRequest) error { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	pr.MergeBase = strings.TrimSpace(pr.MergeBase) | 	pr.MergeBase = strings.TrimSpace(pr.MergeBase) | ||||||
|  | 	if pr.HeadCommitID, err = gitRepo.GetRefCommitID(git.BranchPrefix + "tracking"); err != nil { | ||||||
|  | 		return fmt.Errorf("GetBranchCommitID: can't find commit ID for head: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if pr.HeadCommitID == pr.MergeBase { | ||||||
|  | 		pr.Status = issues_model.PullRequestStatusAncestor | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// 2. Check for conflicts | 	// 2. Check for conflicts | ||||||
| 	if conflicts, err := checkConflicts(ctx, pr, gitRepo, tmpBasePath); err != nil || conflicts || pr.Status == issues_model.PullRequestStatusEmpty { | 	if conflicts, err := checkConflicts(ctx, pr, gitRepo, tmpBasePath); err != nil || conflicts || pr.Status == issues_model.PullRequestStatusEmpty { | ||||||
|   | |||||||
| @@ -195,12 +195,12 @@ | |||||||
| 					<i class="icon icon-octicon">{{svg "octicon-sync"}}</i> | 					<i class="icon icon-octicon">{{svg "octicon-sync"}}</i> | ||||||
| 					{{$.locale.Tr "repo.pulls.is_checking"}} | 					{{$.locale.Tr "repo.pulls.is_checking"}} | ||||||
| 				</div> | 				</div> | ||||||
| 			{{else if .Issue.PullRequest.IsEmpty}} | 			{{else if .Issue.PullRequest.IsAncestor}} | ||||||
| 				<div class="item"> | 				<div class="item"> | ||||||
| 					<i class="icon icon-octicon">{{svg "octicon-alert" 16}}</i> | 					<i class="icon icon-octicon">{{svg "octicon-alert" 16}}</i> | ||||||
| 					{{$.locale.Tr "repo.pulls.is_empty"}} | 					{{$.locale.Tr "repo.pulls.is_ancestor"}} | ||||||
| 				</div> | 				</div> | ||||||
| 			{{else if .Issue.PullRequest.CanAutoMerge}} | 			{{else if or .Issue.PullRequest.CanAutoMerge .Issue.PullRequest.IsEmpty}} | ||||||
| 				{{if .IsBlockedByApprovals}} | 				{{if .IsBlockedByApprovals}} | ||||||
| 					<div class="item"> | 					<div class="item"> | ||||||
| 						<i class="icon icon-octicon">{{svg "octicon-x"}}</i> | 						<i class="icon icon-octicon">{{svg "octicon-x"}}</i> | ||||||
| @@ -282,7 +282,6 @@ | |||||||
| 						</div> | 						</div> | ||||||
| 					{{end}} | 					{{end}} | ||||||
| 				{{end}} | 				{{end}} | ||||||
|  |  | ||||||
| 				{{if and (gt .Issue.PullRequest.CommitsBehind 0) (not  .Issue.IsClosed) (not .Issue.PullRequest.IsChecking) (not .IsPullFilesConflicted) (not .IsPullRequestBroken) (not $canAutoMerge)}} | 				{{if and (gt .Issue.PullRequest.CommitsBehind 0) (not  .Issue.IsClosed) (not .Issue.PullRequest.IsChecking) (not .IsPullFilesConflicted) (not .IsPullRequestBroken) (not $canAutoMerge)}} | ||||||
| 					<div class="ui divider"></div> | 					<div class="ui divider"></div> | ||||||
| 					<div class="item item-section"> | 					<div class="item item-section"> | ||||||
| @@ -321,6 +320,14 @@ | |||||||
| 						</div> | 						</div> | ||||||
| 					</div> | 					</div> | ||||||
| 				{{end}} | 				{{end}} | ||||||
|  | 				{{if .Issue.PullRequest.IsEmpty}} | ||||||
|  | 					<div class="ui divider"></div> | ||||||
|  |  | ||||||
|  | 					<div class="item"> | ||||||
|  | 						<i class="icon icon-octicon">{{svg "octicon-alert" 16}}</i> | ||||||
|  | 						{{$.locale.Tr "repo.pulls.is_empty"}} | ||||||
|  | 					</div> | ||||||
|  | 				{{end}} | ||||||
|  |  | ||||||
| 				{{if .AllowMerge}} {{/* user is allowed to merge */}} | 				{{if .AllowMerge}} {{/* user is allowed to merge */}} | ||||||
| 					{{$prUnit := .Repository.MustGetUnit $.UnitTypePullRequests}} | 					{{$prUnit := .Repository.MustGetUnit $.UnitTypePullRequests}} | ||||||
| @@ -348,6 +355,7 @@ | |||||||
|  |  | ||||||
| 									'canMergeNow': {{$canMergeNow}}, | 									'canMergeNow': {{$canMergeNow}}, | ||||||
| 									'allOverridableChecksOk': {{not $notAllOverridableChecksOk}}, | 									'allOverridableChecksOk': {{not $notAllOverridableChecksOk}}, | ||||||
|  | 									'emptyCommit': {{.Issue.PullRequest.IsEmpty}}, | ||||||
| 									'pullHeadCommitID': {{.PullHeadCommitID}}, | 									'pullHeadCommitID': {{.PullHeadCommitID}}, | ||||||
| 									'isPullBranchDeletable': {{.IsPullBranchDeletable}}, | 									'isPullBranchDeletable': {{.IsPullBranchDeletable}}, | ||||||
| 									'defaultDeleteBranchAfterMerge': {{$prUnit.PullRequestsConfig.DefaultDeleteBranchAfterMerge}}, | 									'defaultDeleteBranchAfterMerge': {{$prUnit.PullRequestsConfig.DefaultDeleteBranchAfterMerge}}, | ||||||
|   | |||||||
| @@ -48,7 +48,7 @@ | |||||||
|  |  | ||||||
|     <div v-if="!showActionForm" class="df"> |     <div v-if="!showActionForm" class="df"> | ||||||
|       <!-- the merge button --> |       <!-- the merge button --> | ||||||
|       <div class="ui buttons merge-button" :class="mergeButtonStyleClass" @click="toggleActionForm(true)" > |       <div class="ui buttons merge-button" :class="[mergeForm.emptyCommit ? 'grey' : mergeForm.allOverridableChecksOk ? 'green' : 'red']" @click="toggleActionForm(true)" > | ||||||
|         <button class="ui button"> |         <button class="ui button"> | ||||||
|           <svg-icon name="octicon-git-merge"/> |           <svg-icon name="octicon-git-merge"/> | ||||||
|           <span class="button-text"> |           <span class="button-text"> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user