mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 20:36:07 +01:00 
			
		
		
		
	Support choose email when creating a commit via web UI (#33432)
Initial PR for #24469
This commit is contained in:
		@@ -542,3 +542,13 @@ func IsEmailDomainAllowed(email string) bool {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return validation.IsEmailDomainListed(setting.Service.EmailDomainAllowList, email)
 | 
						return validation.IsEmailDomainListed(setting.Service.EmailDomainAllowList, email)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetActivatedEmailAddresses(ctx context.Context, uid int64) ([]string, error) {
 | 
				
			||||||
 | 
						emails := make([]string, 0, 2)
 | 
				
			||||||
 | 
						if err := db.GetEngine(ctx).Table("email_address").Select("email").
 | 
				
			||||||
 | 
							Where("uid=? AND is_activated=?", uid, true).Asc("id").
 | 
				
			||||||
 | 
							Find(&emails); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return emails, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -214,7 +214,7 @@ func (u *User) GetPlaceholderEmail() string {
 | 
				
			|||||||
	return fmt.Sprintf("%s@%s", u.LowerName, setting.Service.NoReplyAddress)
 | 
						return fmt.Sprintf("%s@%s", u.LowerName, setting.Service.NoReplyAddress)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetEmail returns an noreply email, if the user has set to keep his
 | 
					// GetEmail returns a noreply email, if the user has set to keep his
 | 
				
			||||||
// email address private, otherwise the primary email address.
 | 
					// email address private, otherwise the primary email address.
 | 
				
			||||||
func (u *User) GetEmail() string {
 | 
					func (u *User) GetEmail() string {
 | 
				
			||||||
	if u.KeepEmailPrivate {
 | 
						if u.KeepEmailPrivate {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1345,6 +1345,8 @@ editor.new_branch_name_desc = New branch name…
 | 
				
			|||||||
editor.cancel = Cancel
 | 
					editor.cancel = Cancel
 | 
				
			||||||
editor.filename_cannot_be_empty = The filename cannot be empty.
 | 
					editor.filename_cannot_be_empty = The filename cannot be empty.
 | 
				
			||||||
editor.filename_is_invalid = The filename is invalid: "%s".
 | 
					editor.filename_is_invalid = The filename is invalid: "%s".
 | 
				
			||||||
 | 
					editor.commit_email = Commit email
 | 
				
			||||||
 | 
					editor.invalid_commit_email = The email for the commit is invalid.
 | 
				
			||||||
editor.branch_does_not_exist = Branch "%s" does not exist in this repository.
 | 
					editor.branch_does_not_exist = Branch "%s" does not exist in this repository.
 | 
				
			||||||
editor.branch_already_exists = Branch "%s" already exists in this repository.
 | 
					editor.branch_already_exists = Branch "%s" already exists in this repository.
 | 
				
			||||||
editor.directory_is_a_file = Directory name "%s" is already used as a filename in this repository.
 | 
					editor.directory_is_a_file = Directory name "%s" is already used as a filename in this repository.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -489,12 +489,12 @@ func ChangeFiles(ctx *context.APIContext) {
 | 
				
			|||||||
		OldBranch: apiOpts.BranchName,
 | 
							OldBranch: apiOpts.BranchName,
 | 
				
			||||||
		NewBranch: apiOpts.NewBranchName,
 | 
							NewBranch: apiOpts.NewBranchName,
 | 
				
			||||||
		Committer: &files_service.IdentityOptions{
 | 
							Committer: &files_service.IdentityOptions{
 | 
				
			||||||
			Name:  apiOpts.Committer.Name,
 | 
								GitUserName:  apiOpts.Committer.Name,
 | 
				
			||||||
			Email: apiOpts.Committer.Email,
 | 
								GitUserEmail: apiOpts.Committer.Email,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Author: &files_service.IdentityOptions{
 | 
							Author: &files_service.IdentityOptions{
 | 
				
			||||||
			Name:  apiOpts.Author.Name,
 | 
								GitUserName:  apiOpts.Author.Name,
 | 
				
			||||||
			Email: apiOpts.Author.Email,
 | 
								GitUserEmail: apiOpts.Author.Email,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Dates: &files_service.CommitDateOptions{
 | 
							Dates: &files_service.CommitDateOptions{
 | 
				
			||||||
			Author:    apiOpts.Dates.Author,
 | 
								Author:    apiOpts.Dates.Author,
 | 
				
			||||||
@@ -586,12 +586,12 @@ func CreateFile(ctx *context.APIContext) {
 | 
				
			|||||||
		OldBranch: apiOpts.BranchName,
 | 
							OldBranch: apiOpts.BranchName,
 | 
				
			||||||
		NewBranch: apiOpts.NewBranchName,
 | 
							NewBranch: apiOpts.NewBranchName,
 | 
				
			||||||
		Committer: &files_service.IdentityOptions{
 | 
							Committer: &files_service.IdentityOptions{
 | 
				
			||||||
			Name:  apiOpts.Committer.Name,
 | 
								GitUserName:  apiOpts.Committer.Name,
 | 
				
			||||||
			Email: apiOpts.Committer.Email,
 | 
								GitUserEmail: apiOpts.Committer.Email,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Author: &files_service.IdentityOptions{
 | 
							Author: &files_service.IdentityOptions{
 | 
				
			||||||
			Name:  apiOpts.Author.Name,
 | 
								GitUserName:  apiOpts.Author.Name,
 | 
				
			||||||
			Email: apiOpts.Author.Email,
 | 
								GitUserEmail: apiOpts.Author.Email,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Dates: &files_service.CommitDateOptions{
 | 
							Dates: &files_service.CommitDateOptions{
 | 
				
			||||||
			Author:    apiOpts.Dates.Author,
 | 
								Author:    apiOpts.Dates.Author,
 | 
				
			||||||
@@ -689,12 +689,12 @@ func UpdateFile(ctx *context.APIContext) {
 | 
				
			|||||||
		OldBranch: apiOpts.BranchName,
 | 
							OldBranch: apiOpts.BranchName,
 | 
				
			||||||
		NewBranch: apiOpts.NewBranchName,
 | 
							NewBranch: apiOpts.NewBranchName,
 | 
				
			||||||
		Committer: &files_service.IdentityOptions{
 | 
							Committer: &files_service.IdentityOptions{
 | 
				
			||||||
			Name:  apiOpts.Committer.Name,
 | 
								GitUserName:  apiOpts.Committer.Name,
 | 
				
			||||||
			Email: apiOpts.Committer.Email,
 | 
								GitUserEmail: apiOpts.Committer.Email,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Author: &files_service.IdentityOptions{
 | 
							Author: &files_service.IdentityOptions{
 | 
				
			||||||
			Name:  apiOpts.Author.Name,
 | 
								GitUserName:  apiOpts.Author.Name,
 | 
				
			||||||
			Email: apiOpts.Author.Email,
 | 
								GitUserEmail: apiOpts.Author.Email,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Dates: &files_service.CommitDateOptions{
 | 
							Dates: &files_service.CommitDateOptions{
 | 
				
			||||||
			Author:    apiOpts.Dates.Author,
 | 
								Author:    apiOpts.Dates.Author,
 | 
				
			||||||
@@ -848,12 +848,12 @@ func DeleteFile(ctx *context.APIContext) {
 | 
				
			|||||||
		OldBranch: apiOpts.BranchName,
 | 
							OldBranch: apiOpts.BranchName,
 | 
				
			||||||
		NewBranch: apiOpts.NewBranchName,
 | 
							NewBranch: apiOpts.NewBranchName,
 | 
				
			||||||
		Committer: &files_service.IdentityOptions{
 | 
							Committer: &files_service.IdentityOptions{
 | 
				
			||||||
			Name:  apiOpts.Committer.Name,
 | 
								GitUserName:  apiOpts.Committer.Name,
 | 
				
			||||||
			Email: apiOpts.Committer.Email,
 | 
								GitUserEmail: apiOpts.Committer.Email,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Author: &files_service.IdentityOptions{
 | 
							Author: &files_service.IdentityOptions{
 | 
				
			||||||
			Name:  apiOpts.Author.Name,
 | 
								GitUserName:  apiOpts.Author.Name,
 | 
				
			||||||
			Email: apiOpts.Author.Email,
 | 
								GitUserEmail: apiOpts.Author.Email,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Dates: &files_service.CommitDateOptions{
 | 
							Dates: &files_service.CommitDateOptions{
 | 
				
			||||||
			Author:    apiOpts.Dates.Author,
 | 
								Author:    apiOpts.Dates.Author,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -58,12 +58,12 @@ func ApplyDiffPatch(ctx *context.APIContext) {
 | 
				
			|||||||
		OldBranch: apiOpts.BranchName,
 | 
							OldBranch: apiOpts.BranchName,
 | 
				
			||||||
		NewBranch: apiOpts.NewBranchName,
 | 
							NewBranch: apiOpts.NewBranchName,
 | 
				
			||||||
		Committer: &files.IdentityOptions{
 | 
							Committer: &files.IdentityOptions{
 | 
				
			||||||
			Name:  apiOpts.Committer.Name,
 | 
								GitUserName:  apiOpts.Committer.Name,
 | 
				
			||||||
			Email: apiOpts.Committer.Email,
 | 
								GitUserEmail: apiOpts.Committer.Email,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Author: &files.IdentityOptions{
 | 
							Author: &files.IdentityOptions{
 | 
				
			||||||
			Name:  apiOpts.Author.Name,
 | 
								GitUserName:  apiOpts.Author.Name,
 | 
				
			||||||
			Email: apiOpts.Author.Email,
 | 
								GitUserEmail: apiOpts.Author.Email,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Dates: &files.CommitDateOptions{
 | 
							Dates: &files.CommitDateOptions{
 | 
				
			||||||
			Author:    apiOpts.Dates.Author,
 | 
								Author:    apiOpts.Dates.Author,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,7 @@ import (
 | 
				
			|||||||
	git_model "code.gitea.io/gitea/models/git"
 | 
						git_model "code.gitea.io/gitea/models/git"
 | 
				
			||||||
	repo_model "code.gitea.io/gitea/models/repo"
 | 
						repo_model "code.gitea.io/gitea/models/repo"
 | 
				
			||||||
	"code.gitea.io/gitea/models/unit"
 | 
						"code.gitea.io/gitea/models/unit"
 | 
				
			||||||
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/charset"
 | 
						"code.gitea.io/gitea/modules/charset"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/json"
 | 
						"code.gitea.io/gitea/modules/json"
 | 
				
			||||||
@@ -102,10 +103,32 @@ func getParentTreeFields(treePath string) (treeNames, treePaths []string) {
 | 
				
			|||||||
	return treeNames, treePaths
 | 
						return treeNames, treePaths
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func editFile(ctx *context.Context, isNewFile bool) {
 | 
					func getCandidateEmailAddresses(ctx *context.Context) []string {
 | 
				
			||||||
	ctx.Data["PageIsViewCode"] = true
 | 
						emails, err := user_model.GetActivatedEmailAddresses(ctx, ctx.Doer.ID)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Error("getCandidateEmailAddresses: GetActivatedEmailAddresses: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ctx.Doer.KeepEmailPrivate {
 | 
				
			||||||
 | 
							emails = append([]string{ctx.Doer.GetPlaceholderEmail()}, emails...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return emails
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func editFileCommon(ctx *context.Context, isNewFile bool) {
 | 
				
			||||||
	ctx.Data["PageIsEdit"] = true
 | 
						ctx.Data["PageIsEdit"] = true
 | 
				
			||||||
	ctx.Data["IsNewFile"] = isNewFile
 | 
						ctx.Data["IsNewFile"] = isNewFile
 | 
				
			||||||
 | 
						ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
 | 
				
			||||||
 | 
						ctx.Data["PreviewableExtensions"] = strings.Join(markup.PreviewableExtensions(), ",")
 | 
				
			||||||
 | 
						ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",")
 | 
				
			||||||
 | 
						ctx.Data["IsEditingFileOnly"] = ctx.FormString("return_uri") != ""
 | 
				
			||||||
 | 
						ctx.Data["ReturnURI"] = ctx.FormString("return_uri")
 | 
				
			||||||
 | 
						ctx.Data["CommitCandidateEmails"] = getCandidateEmailAddresses(ctx)
 | 
				
			||||||
 | 
						ctx.Data["CommitDefaultEmail"] = ctx.Doer.GetEmail()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func editFile(ctx *context.Context, isNewFile bool) {
 | 
				
			||||||
 | 
						editFileCommon(ctx, isNewFile)
 | 
				
			||||||
	canCommit := renderCommitRights(ctx)
 | 
						canCommit := renderCommitRights(ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	treePath := cleanUploadFileName(ctx.Repo.TreePath)
 | 
						treePath := cleanUploadFileName(ctx.Repo.TreePath)
 | 
				
			||||||
@@ -174,28 +197,19 @@ func editFile(ctx *context.Context, isNewFile bool) {
 | 
				
			|||||||
			ctx.Data["FileContent"] = content
 | 
								ctx.Data["FileContent"] = content
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		// Append filename from query, or empty string to allow user name the new file.
 | 
							// Append filename from query, or empty string to allow username the new file.
 | 
				
			||||||
		treeNames = append(treeNames, fileName)
 | 
							treeNames = append(treeNames, fileName)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Data["TreeNames"] = treeNames
 | 
						ctx.Data["TreeNames"] = treeNames
 | 
				
			||||||
	ctx.Data["TreePaths"] = treePaths
 | 
						ctx.Data["TreePaths"] = treePaths
 | 
				
			||||||
	ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
 | 
					 | 
				
			||||||
	ctx.Data["commit_summary"] = ""
 | 
						ctx.Data["commit_summary"] = ""
 | 
				
			||||||
	ctx.Data["commit_message"] = ""
 | 
						ctx.Data["commit_message"] = ""
 | 
				
			||||||
	if canCommit {
 | 
						ctx.Data["commit_choice"] = util.Iif(canCommit, frmCommitChoiceDirect, frmCommitChoiceNewBranch)
 | 
				
			||||||
		ctx.Data["commit_choice"] = frmCommitChoiceDirect
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		ctx.Data["commit_choice"] = frmCommitChoiceNewBranch
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	ctx.Data["new_branch_name"] = GetUniquePatchBranchName(ctx)
 | 
						ctx.Data["new_branch_name"] = GetUniquePatchBranchName(ctx)
 | 
				
			||||||
	ctx.Data["last_commit"] = ctx.Repo.CommitID
 | 
						ctx.Data["last_commit"] = ctx.Repo.CommitID
 | 
				
			||||||
	ctx.Data["PreviewableExtensions"] = strings.Join(markup.PreviewableExtensions(), ",")
 | 
					 | 
				
			||||||
	ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",")
 | 
					 | 
				
			||||||
	ctx.Data["EditorconfigJson"] = GetEditorConfig(ctx, treePath)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Data["IsEditingFileOnly"] = ctx.FormString("return_uri") != ""
 | 
						ctx.Data["EditorconfigJson"] = GetEditorConfig(ctx, treePath)
 | 
				
			||||||
	ctx.Data["ReturnURI"] = ctx.FormString("return_uri")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.HTML(http.StatusOK, tplEditFile)
 | 
						ctx.HTML(http.StatusOK, tplEditFile)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -224,6 +238,9 @@ func NewFile(ctx *context.Context) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile bool) {
 | 
					func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile bool) {
 | 
				
			||||||
 | 
						editFileCommon(ctx, isNewFile)
 | 
				
			||||||
 | 
						ctx.Data["PageHasPosted"] = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	canCommit := renderCommitRights(ctx)
 | 
						canCommit := renderCommitRights(ctx)
 | 
				
			||||||
	treeNames, treePaths := getParentTreeFields(form.TreePath)
 | 
						treeNames, treePaths := getParentTreeFields(form.TreePath)
 | 
				
			||||||
	branchName := ctx.Repo.BranchName
 | 
						branchName := ctx.Repo.BranchName
 | 
				
			||||||
@@ -231,21 +248,15 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
 | 
				
			|||||||
		branchName = form.NewBranchName
 | 
							branchName = form.NewBranchName
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Data["PageIsEdit"] = true
 | 
					 | 
				
			||||||
	ctx.Data["PageHasPosted"] = true
 | 
					 | 
				
			||||||
	ctx.Data["IsNewFile"] = isNewFile
 | 
					 | 
				
			||||||
	ctx.Data["TreePath"] = form.TreePath
 | 
						ctx.Data["TreePath"] = form.TreePath
 | 
				
			||||||
	ctx.Data["TreeNames"] = treeNames
 | 
						ctx.Data["TreeNames"] = treeNames
 | 
				
			||||||
	ctx.Data["TreePaths"] = treePaths
 | 
						ctx.Data["TreePaths"] = treePaths
 | 
				
			||||||
	ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(ctx.Repo.BranchName)
 | 
					 | 
				
			||||||
	ctx.Data["FileContent"] = form.Content
 | 
						ctx.Data["FileContent"] = form.Content
 | 
				
			||||||
	ctx.Data["commit_summary"] = form.CommitSummary
 | 
						ctx.Data["commit_summary"] = form.CommitSummary
 | 
				
			||||||
	ctx.Data["commit_message"] = form.CommitMessage
 | 
						ctx.Data["commit_message"] = form.CommitMessage
 | 
				
			||||||
	ctx.Data["commit_choice"] = form.CommitChoice
 | 
						ctx.Data["commit_choice"] = form.CommitChoice
 | 
				
			||||||
	ctx.Data["new_branch_name"] = form.NewBranchName
 | 
						ctx.Data["new_branch_name"] = form.NewBranchName
 | 
				
			||||||
	ctx.Data["last_commit"] = ctx.Repo.CommitID
 | 
						ctx.Data["last_commit"] = ctx.Repo.CommitID
 | 
				
			||||||
	ctx.Data["PreviewableExtensions"] = strings.Join(markup.PreviewableExtensions(), ",")
 | 
					 | 
				
			||||||
	ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",")
 | 
					 | 
				
			||||||
	ctx.Data["EditorconfigJson"] = GetEditorConfig(ctx, form.TreePath)
 | 
						ctx.Data["EditorconfigJson"] = GetEditorConfig(ctx, form.TreePath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ctx.HasError() {
 | 
						if ctx.HasError() {
 | 
				
			||||||
@@ -253,7 +264,7 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Cannot commit to a an existing branch if user doesn't have rights
 | 
						// Cannot commit to an existing branch if user doesn't have rights
 | 
				
			||||||
	if branchName == ctx.Repo.BranchName && !canCommit {
 | 
						if branchName == ctx.Repo.BranchName && !canCommit {
 | 
				
			||||||
		ctx.Data["Err_NewBranchName"] = true
 | 
							ctx.Data["Err_NewBranchName"] = true
 | 
				
			||||||
		ctx.Data["commit_choice"] = frmCommitChoiceNewBranch
 | 
							ctx.Data["commit_choice"] = frmCommitChoiceNewBranch
 | 
				
			||||||
@@ -276,6 +287,17 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
 | 
				
			|||||||
		message += "\n\n" + form.CommitMessage
 | 
							message += "\n\n" + form.CommitMessage
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gitCommitter := &files_service.IdentityOptions{}
 | 
				
			||||||
 | 
						if form.CommitEmail != "" {
 | 
				
			||||||
 | 
							if util.SliceContainsString(getCandidateEmailAddresses(ctx), form.CommitEmail, true) {
 | 
				
			||||||
 | 
								gitCommitter.GitUserEmail = form.CommitEmail
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								ctx.Data["Err_CommitEmail"] = true
 | 
				
			||||||
 | 
								ctx.RenderWithErr(ctx.Tr("repo.editor.invalid_commit_email"), tplEditFile, &form)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	operation := "update"
 | 
						operation := "update"
 | 
				
			||||||
	if isNewFile {
 | 
						if isNewFile {
 | 
				
			||||||
		operation = "create"
 | 
							operation = "create"
 | 
				
			||||||
@@ -294,7 +316,9 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
 | 
				
			|||||||
				ContentReader: strings.NewReader(strings.ReplaceAll(form.Content, "\r", "")),
 | 
									ContentReader: strings.NewReader(strings.ReplaceAll(form.Content, "\r", "")),
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Signoff: form.Signoff,
 | 
							Signoff:   form.Signoff,
 | 
				
			||||||
 | 
							Author:    gitCommitter,
 | 
				
			||||||
 | 
							Committer: gitCommitter,
 | 
				
			||||||
	}); err != nil {
 | 
						}); err != nil {
 | 
				
			||||||
		// This is where we handle all the errors thrown by files_service.ChangeRepoFiles
 | 
							// This is where we handle all the errors thrown by files_service.ChangeRepoFiles
 | 
				
			||||||
		if git.IsErrNotExist(err) {
 | 
							if git.IsErrNotExist(err) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -720,6 +720,7 @@ type EditRepoFileForm struct {
 | 
				
			|||||||
	NewBranchName string `binding:"GitRefName;MaxSize(100)"`
 | 
						NewBranchName string `binding:"GitRefName;MaxSize(100)"`
 | 
				
			||||||
	LastCommit    string
 | 
						LastCommit    string
 | 
				
			||||||
	Signoff       bool
 | 
						Signoff       bool
 | 
				
			||||||
 | 
						CommitEmail   string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,6 @@ import (
 | 
				
			|||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	packages_model "code.gitea.io/gitea/models/packages"
 | 
						packages_model "code.gitea.io/gitea/models/packages"
 | 
				
			||||||
	repo_model "code.gitea.io/gitea/models/repo"
 | 
						repo_model "code.gitea.io/gitea/models/repo"
 | 
				
			||||||
@@ -296,8 +295,13 @@ func alterRepositoryContent(ctx context.Context, doer *user_model.User, repo *re
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	now := time.Now()
 | 
						commitOpts := &files_service.CommitTreeUserOptions{
 | 
				
			||||||
	commitHash, err := t.CommitTreeWithDate(lastCommitID, doer, doer, treeHash, commitMessage, false, now, now)
 | 
							ParentCommitID: lastCommitID,
 | 
				
			||||||
 | 
							TreeHash:       treeHash,
 | 
				
			||||||
 | 
							CommitMessage:  commitMessage,
 | 
				
			||||||
 | 
							DoerUser:       doer,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						commitHash, err := t.CommitTree(commitOpts)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,15 +32,13 @@ func (err ErrCommitIDDoesNotMatch) Error() string {
 | 
				
			|||||||
	return fmt.Sprintf("file CommitID does not match [given: %s, expected: %s]", err.GivenCommitID, err.CurrentCommitID)
 | 
						return fmt.Sprintf("file CommitID does not match [given: %s, expected: %s]", err.GivenCommitID, err.CurrentCommitID)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CherryPick cherrypicks or reverts a commit to the given repository
 | 
					// CherryPick cherry-picks or reverts a commit to the given repository
 | 
				
			||||||
func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_model.User, revert bool, opts *ApplyDiffPatchOptions) (*structs.FileResponse, error) {
 | 
					func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_model.User, revert bool, opts *ApplyDiffPatchOptions) (*structs.FileResponse, error) {
 | 
				
			||||||
	if err := opts.Validate(ctx, repo, doer); err != nil {
 | 
						if err := opts.Validate(ctx, repo, doer); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	message := strings.TrimSpace(opts.Message)
 | 
						message := strings.TrimSpace(opts.Message)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	author, committer := GetAuthorAndCommitterUsers(opts.Author, opts.Committer, doer)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	t, err := NewTemporaryUploadRepository(ctx, repo)
 | 
						t, err := NewTemporaryUploadRepository(ctx, repo)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Error("NewTemporaryUploadRepository failed: %v", err)
 | 
							log.Error("NewTemporaryUploadRepository failed: %v", err)
 | 
				
			||||||
@@ -112,12 +110,21 @@ func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_mod
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Now commit the tree
 | 
						// Now commit the tree
 | 
				
			||||||
	var commitHash string
 | 
						commitOpts := &CommitTreeUserOptions{
 | 
				
			||||||
	if opts.Dates != nil {
 | 
							ParentCommitID:    "HEAD",
 | 
				
			||||||
		commitHash, err = t.CommitTreeWithDate("HEAD", author, committer, treeHash, message, opts.Signoff, opts.Dates.Author, opts.Dates.Committer)
 | 
							TreeHash:          treeHash,
 | 
				
			||||||
	} else {
 | 
							CommitMessage:     message,
 | 
				
			||||||
		commitHash, err = t.CommitTree("HEAD", author, committer, treeHash, message, opts.Signoff)
 | 
							SignOff:           opts.Signoff,
 | 
				
			||||||
 | 
							DoerUser:          doer,
 | 
				
			||||||
 | 
							AuthorIdentity:    opts.Author,
 | 
				
			||||||
 | 
							AuthorTime:        nil,
 | 
				
			||||||
 | 
							CommitterIdentity: opts.Committer,
 | 
				
			||||||
 | 
							CommitterTime:     nil,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if opts.Dates != nil {
 | 
				
			||||||
 | 
							commitOpts.AuthorTime, commitOpts.CommitterTime = &opts.Dates.Author, &opts.Dates.Committer
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						commitHash, err := t.CommitTree(commitOpts)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,6 @@ import (
 | 
				
			|||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	repo_model "code.gitea.io/gitea/models/repo"
 | 
						repo_model "code.gitea.io/gitea/models/repo"
 | 
				
			||||||
	user_model "code.gitea.io/gitea/models/user"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	api "code.gitea.io/gitea/modules/structs"
 | 
						api "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
@@ -111,51 +110,6 @@ func GetFileCommitResponse(repo *repo_model.Repository, commit *git.Commit) (*ap
 | 
				
			|||||||
	return fileCommit, nil
 | 
						return fileCommit, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetAuthorAndCommitterUsers Gets the author and committer user objects from the IdentityOptions
 | 
					 | 
				
			||||||
func GetAuthorAndCommitterUsers(author, committer *IdentityOptions, doer *user_model.User) (authorUser, committerUser *user_model.User) {
 | 
					 | 
				
			||||||
	// Committer and author are optional. If they are not the doer (not same email address)
 | 
					 | 
				
			||||||
	// then we use bogus User objects for them to store their FullName and Email.
 | 
					 | 
				
			||||||
	// If only one of the two are provided, we set both of them to it.
 | 
					 | 
				
			||||||
	// If neither are provided, both are the doer.
 | 
					 | 
				
			||||||
	if committer != nil && committer.Email != "" {
 | 
					 | 
				
			||||||
		if doer != nil && strings.EqualFold(doer.Email, committer.Email) {
 | 
					 | 
				
			||||||
			committerUser = doer // the committer is the doer, so will use their user object
 | 
					 | 
				
			||||||
			if committer.Name != "" {
 | 
					 | 
				
			||||||
				committerUser.FullName = committer.Name
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			committerUser = &user_model.User{
 | 
					 | 
				
			||||||
				FullName: committer.Name,
 | 
					 | 
				
			||||||
				Email:    committer.Email,
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if author != nil && author.Email != "" {
 | 
					 | 
				
			||||||
		if doer != nil && strings.EqualFold(doer.Email, author.Email) {
 | 
					 | 
				
			||||||
			authorUser = doer // the author is the doer, so will use their user object
 | 
					 | 
				
			||||||
			if authorUser.Name != "" {
 | 
					 | 
				
			||||||
				authorUser.FullName = author.Name
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			authorUser = &user_model.User{
 | 
					 | 
				
			||||||
				FullName: author.Name,
 | 
					 | 
				
			||||||
				Email:    author.Email,
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if authorUser == nil {
 | 
					 | 
				
			||||||
		if committerUser != nil {
 | 
					 | 
				
			||||||
			authorUser = committerUser // No valid author was given so use the committer
 | 
					 | 
				
			||||||
		} else if doer != nil {
 | 
					 | 
				
			||||||
			authorUser = doer // No valid author was given and no valid committer so use the doer
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if committerUser == nil {
 | 
					 | 
				
			||||||
		committerUser = authorUser // No valid committer so use the author as the committer (was set to a valid user above)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return authorUser, committerUser
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ErrFilenameInvalid represents a "FilenameInvalid" kind of error.
 | 
					// ErrFilenameInvalid represents a "FilenameInvalid" kind of error.
 | 
				
			||||||
type ErrFilenameInvalid struct {
 | 
					type ErrFilenameInvalid struct {
 | 
				
			||||||
	Path string
 | 
						Path string
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -126,8 +126,6 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	message := strings.TrimSpace(opts.Message)
 | 
						message := strings.TrimSpace(opts.Message)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	author, committer := GetAuthorAndCommitterUsers(opts.Author, opts.Committer, doer)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	t, err := NewTemporaryUploadRepository(ctx, repo)
 | 
						t, err := NewTemporaryUploadRepository(ctx, repo)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Error("NewTemporaryUploadRepository failed: %v", err)
 | 
							log.Error("NewTemporaryUploadRepository failed: %v", err)
 | 
				
			||||||
@@ -187,12 +185,21 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Now commit the tree
 | 
						// Now commit the tree
 | 
				
			||||||
	var commitHash string
 | 
						commitOpts := &CommitTreeUserOptions{
 | 
				
			||||||
	if opts.Dates != nil {
 | 
							ParentCommitID:    "HEAD",
 | 
				
			||||||
		commitHash, err = t.CommitTreeWithDate("HEAD", author, committer, treeHash, message, opts.Signoff, opts.Dates.Author, opts.Dates.Committer)
 | 
							TreeHash:          treeHash,
 | 
				
			||||||
	} else {
 | 
							CommitMessage:     message,
 | 
				
			||||||
		commitHash, err = t.CommitTree("HEAD", author, committer, treeHash, message, opts.Signoff)
 | 
							SignOff:           opts.Signoff,
 | 
				
			||||||
 | 
							DoerUser:          doer,
 | 
				
			||||||
 | 
							AuthorIdentity:    opts.Author,
 | 
				
			||||||
 | 
							AuthorTime:        nil,
 | 
				
			||||||
 | 
							CommitterIdentity: opts.Committer,
 | 
				
			||||||
 | 
							CommitterTime:     nil,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if opts.Dates != nil {
 | 
				
			||||||
 | 
							commitOpts.AuthorTime, commitOpts.CommitterTime = &opts.Dates.Author, &opts.Dates.Committer
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						commitHash, err := t.CommitTree(commitOpts)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	repo_module "code.gitea.io/gitea/modules/repository"
 | 
						repo_module "code.gitea.io/gitea/modules/repository"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
	asymkey_service "code.gitea.io/gitea/services/asymkey"
 | 
						asymkey_service "code.gitea.io/gitea/services/asymkey"
 | 
				
			||||||
	"code.gitea.io/gitea/services/gitdiff"
 | 
						"code.gitea.io/gitea/services/gitdiff"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -225,15 +226,53 @@ func (t *TemporaryUploadRepository) GetLastCommitByRef(ref string) (string, erro
 | 
				
			|||||||
	return strings.TrimSpace(stdout), nil
 | 
						return strings.TrimSpace(stdout), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CommitTree creates a commit from a given tree for the user with provided message
 | 
					type CommitTreeUserOptions struct {
 | 
				
			||||||
func (t *TemporaryUploadRepository) CommitTree(parent string, author, committer *user_model.User, treeHash, message string, signoff bool) (string, error) {
 | 
						ParentCommitID string
 | 
				
			||||||
	return t.CommitTreeWithDate(parent, author, committer, treeHash, message, signoff, time.Now(), time.Now())
 | 
						TreeHash       string
 | 
				
			||||||
 | 
						CommitMessage  string
 | 
				
			||||||
 | 
						SignOff        bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DoerUser *user_model.User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						AuthorIdentity    *IdentityOptions // if nil, use doer
 | 
				
			||||||
 | 
						AuthorTime        *time.Time       // if nil, use now
 | 
				
			||||||
 | 
						CommitterIdentity *IdentityOptions
 | 
				
			||||||
 | 
						CommitterTime     *time.Time
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CommitTreeWithDate creates a commit from a given tree for the user with provided message
 | 
					func makeGitUserSignature(doer *user_model.User, identity, other *IdentityOptions) *git.Signature {
 | 
				
			||||||
func (t *TemporaryUploadRepository) CommitTreeWithDate(parent string, author, committer *user_model.User, treeHash, message string, signoff bool, authorDate, committerDate time.Time) (string, error) {
 | 
						gitSig := &git.Signature{}
 | 
				
			||||||
	authorSig := author.NewGitSig()
 | 
						if identity != nil {
 | 
				
			||||||
	committerSig := committer.NewGitSig()
 | 
							gitSig.Name, gitSig.Email = identity.GitUserName, identity.GitUserEmail
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if other != nil {
 | 
				
			||||||
 | 
							gitSig.Name = util.IfZero(gitSig.Name, other.GitUserName)
 | 
				
			||||||
 | 
							gitSig.Email = util.IfZero(gitSig.Email, other.GitUserEmail)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if gitSig.Name == "" {
 | 
				
			||||||
 | 
							gitSig.Name = doer.GitName()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if gitSig.Email == "" {
 | 
				
			||||||
 | 
							gitSig.Email = doer.GetEmail()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return gitSig
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CommitTree creates a commit from a given tree for the user with provided message
 | 
				
			||||||
 | 
					func (t *TemporaryUploadRepository) CommitTree(opts *CommitTreeUserOptions) (string, error) {
 | 
				
			||||||
 | 
						authorSig := makeGitUserSignature(opts.DoerUser, opts.AuthorIdentity, opts.CommitterIdentity)
 | 
				
			||||||
 | 
						committerSig := makeGitUserSignature(opts.DoerUser, opts.CommitterIdentity, opts.AuthorIdentity)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						authorDate := opts.AuthorTime
 | 
				
			||||||
 | 
						committerDate := opts.CommitterTime
 | 
				
			||||||
 | 
						if authorDate == nil && committerDate == nil {
 | 
				
			||||||
 | 
							authorDate = util.ToPointer(time.Now())
 | 
				
			||||||
 | 
							committerDate = authorDate
 | 
				
			||||||
 | 
						} else if authorDate == nil {
 | 
				
			||||||
 | 
							authorDate = committerDate
 | 
				
			||||||
 | 
						} else if committerDate == nil {
 | 
				
			||||||
 | 
							committerDate = authorDate
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Because this may call hooks we should pass in the environment
 | 
						// Because this may call hooks we should pass in the environment
 | 
				
			||||||
	env := append(os.Environ(),
 | 
						env := append(os.Environ(),
 | 
				
			||||||
@@ -244,21 +283,21 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(parent string, author, co
 | 
				
			|||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	messageBytes := new(bytes.Buffer)
 | 
						messageBytes := new(bytes.Buffer)
 | 
				
			||||||
	_, _ = messageBytes.WriteString(message)
 | 
						_, _ = messageBytes.WriteString(opts.CommitMessage)
 | 
				
			||||||
	_, _ = messageBytes.WriteString("\n")
 | 
						_, _ = messageBytes.WriteString("\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cmdCommitTree := git.NewCommand(t.ctx, "commit-tree").AddDynamicArguments(treeHash)
 | 
						cmdCommitTree := git.NewCommand(t.ctx, "commit-tree").AddDynamicArguments(opts.TreeHash)
 | 
				
			||||||
	if parent != "" {
 | 
						if opts.ParentCommitID != "" {
 | 
				
			||||||
		cmdCommitTree.AddOptionValues("-p", parent)
 | 
							cmdCommitTree.AddOptionValues("-p", opts.ParentCommitID)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var sign bool
 | 
						var sign bool
 | 
				
			||||||
	var keyID string
 | 
						var keyID string
 | 
				
			||||||
	var signer *git.Signature
 | 
						var signer *git.Signature
 | 
				
			||||||
	if parent != "" {
 | 
						if opts.ParentCommitID != "" {
 | 
				
			||||||
		sign, keyID, signer, _ = asymkey_service.SignCRUDAction(t.ctx, t.repo.RepoPath(), author, t.basePath, parent)
 | 
							sign, keyID, signer, _ = asymkey_service.SignCRUDAction(t.ctx, t.repo.RepoPath(), opts.DoerUser, t.basePath, opts.ParentCommitID)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		sign, keyID, signer, _ = asymkey_service.SignInitialCommit(t.ctx, t.repo.RepoPath(), author)
 | 
							sign, keyID, signer, _ = asymkey_service.SignInitialCommit(t.ctx, t.repo.RepoPath(), opts.DoerUser)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if sign {
 | 
						if sign {
 | 
				
			||||||
		cmdCommitTree.AddOptionFormat("-S%s", keyID)
 | 
							cmdCommitTree.AddOptionFormat("-S%s", keyID)
 | 
				
			||||||
@@ -279,7 +318,7 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(parent string, author, co
 | 
				
			|||||||
		cmdCommitTree.AddArguments("--no-gpg-sign")
 | 
							cmdCommitTree.AddArguments("--no-gpg-sign")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if signoff {
 | 
						if opts.SignOff {
 | 
				
			||||||
		// Signed-off-by
 | 
							// Signed-off-by
 | 
				
			||||||
		_, _ = messageBytes.WriteString("\n")
 | 
							_, _ = messageBytes.WriteString("\n")
 | 
				
			||||||
		_, _ = messageBytes.WriteString("Signed-off-by: ")
 | 
							_, _ = messageBytes.WriteString("Signed-off-by: ")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,8 +27,8 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// IdentityOptions for a person's identity like an author or committer
 | 
					// IdentityOptions for a person's identity like an author or committer
 | 
				
			||||||
type IdentityOptions struct {
 | 
					type IdentityOptions struct {
 | 
				
			||||||
	Name  string
 | 
						GitUserName  string // to match "git config user.name"
 | 
				
			||||||
	Email string
 | 
						GitUserEmail string // to match "git config user.email"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CommitDateOptions store dates for GIT_AUTHOR_DATE and GIT_COMMITTER_DATE
 | 
					// CommitDateOptions store dates for GIT_AUTHOR_DATE and GIT_COMMITTER_DATE
 | 
				
			||||||
@@ -160,8 +160,6 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	message := strings.TrimSpace(opts.Message)
 | 
						message := strings.TrimSpace(opts.Message)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	author, committer := GetAuthorAndCommitterUsers(opts.Author, opts.Committer, doer)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	t, err := NewTemporaryUploadRepository(ctx, repo)
 | 
						t, err := NewTemporaryUploadRepository(ctx, repo)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Error("NewTemporaryUploadRepository failed: %v", err)
 | 
							log.Error("NewTemporaryUploadRepository failed: %v", err)
 | 
				
			||||||
@@ -262,12 +260,21 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Now commit the tree
 | 
						// Now commit the tree
 | 
				
			||||||
	var commitHash string
 | 
						commitOpts := &CommitTreeUserOptions{
 | 
				
			||||||
	if opts.Dates != nil {
 | 
							ParentCommitID:    opts.LastCommitID,
 | 
				
			||||||
		commitHash, err = t.CommitTreeWithDate(opts.LastCommitID, author, committer, treeHash, message, opts.Signoff, opts.Dates.Author, opts.Dates.Committer)
 | 
							TreeHash:          treeHash,
 | 
				
			||||||
	} else {
 | 
							CommitMessage:     message,
 | 
				
			||||||
		commitHash, err = t.CommitTree(opts.LastCommitID, author, committer, treeHash, message, opts.Signoff)
 | 
							SignOff:           opts.Signoff,
 | 
				
			||||||
 | 
							DoerUser:          doer,
 | 
				
			||||||
 | 
							AuthorIdentity:    opts.Author,
 | 
				
			||||||
 | 
							AuthorTime:        nil,
 | 
				
			||||||
 | 
							CommitterIdentity: opts.Committer,
 | 
				
			||||||
 | 
							CommitterTime:     nil,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if opts.Dates != nil {
 | 
				
			||||||
 | 
							commitOpts.AuthorTime, commitOpts.CommitterTime = &opts.Dates.Author, &opts.Dates.Committer
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						commitHash, err := t.CommitTree(commitOpts)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -128,12 +128,15 @@ func UploadRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// make author and committer the doer
 | 
					 | 
				
			||||||
	author := doer
 | 
					 | 
				
			||||||
	committer := doer
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Now commit the tree
 | 
						// Now commit the tree
 | 
				
			||||||
	commitHash, err := t.CommitTree(opts.LastCommitID, author, committer, treeHash, opts.Message, opts.Signoff)
 | 
						commitOpts := &CommitTreeUserOptions{
 | 
				
			||||||
 | 
							ParentCommitID: opts.LastCommitID,
 | 
				
			||||||
 | 
							TreeHash:       treeHash,
 | 
				
			||||||
 | 
							CommitMessage:  opts.Message,
 | 
				
			||||||
 | 
							SignOff:        opts.Signoff,
 | 
				
			||||||
 | 
							DoerUser:       doer,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						commitHash, err := t.CommitTree(commitOpts)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -66,6 +66,16 @@
 | 
				
			|||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			{{end}}
 | 
								{{end}}
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
 | 
							{{if and .CommitCandidateEmails (gt (len .CommitCandidateEmails) 1)}}
 | 
				
			||||||
 | 
								<div class="field {{if .Err_CommitEmail}}error{{end}}">
 | 
				
			||||||
 | 
									<label>{{ctx.Locale.Tr "repo.editor.commit_email"}}</label>
 | 
				
			||||||
 | 
									<select class="ui selection dropdown" name="commit_email">
 | 
				
			||||||
 | 
										{{- range $email := .CommitCandidateEmails -}}
 | 
				
			||||||
 | 
											<option {{if eq $email $.CommitDefaultEmail}}selected{{end}} value="{{$email}}">{{$email}}</option>
 | 
				
			||||||
 | 
										{{- end -}}
 | 
				
			||||||
 | 
									</select>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							{{end}}
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
	<button id="commit-button" type="submit" class="ui primary button">
 | 
						<button id="commit-button" type="submit" class="ui primary button">
 | 
				
			||||||
		{{if eq .commit_choice "commit-to-new-branch"}}{{ctx.Locale.Tr "repo.editor.propose_file_change"}}{{else}}{{ctx.Locale.Tr "repo.editor.commit_changes"}}{{end}}
 | 
							{{if eq .commit_choice "commit-to-new-branch"}}{{ctx.Locale.Tr "repo.editor.propose_file_change"}}{{else}}{{ctx.Locale.Tr "repo.editor.commit_changes"}}{{end}}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -81,12 +81,12 @@ func TestPullRequestTargetEvent(t *testing.T) {
 | 
				
			|||||||
			OldBranch: "main",
 | 
								OldBranch: "main",
 | 
				
			||||||
			NewBranch: "main",
 | 
								NewBranch: "main",
 | 
				
			||||||
			Author: &files_service.IdentityOptions{
 | 
								Author: &files_service.IdentityOptions{
 | 
				
			||||||
				Name:  user2.Name,
 | 
									GitUserName:  user2.Name,
 | 
				
			||||||
				Email: user2.Email,
 | 
									GitUserEmail: user2.Email,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Committer: &files_service.IdentityOptions{
 | 
								Committer: &files_service.IdentityOptions{
 | 
				
			||||||
				Name:  user2.Name,
 | 
									GitUserName:  user2.Name,
 | 
				
			||||||
				Email: user2.Email,
 | 
									GitUserEmail: user2.Email,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Dates: &files_service.CommitDateOptions{
 | 
								Dates: &files_service.CommitDateOptions{
 | 
				
			||||||
				Author:    time.Now(),
 | 
									Author:    time.Now(),
 | 
				
			||||||
@@ -109,12 +109,12 @@ func TestPullRequestTargetEvent(t *testing.T) {
 | 
				
			|||||||
			OldBranch: "main",
 | 
								OldBranch: "main",
 | 
				
			||||||
			NewBranch: "fork-branch-1",
 | 
								NewBranch: "fork-branch-1",
 | 
				
			||||||
			Author: &files_service.IdentityOptions{
 | 
								Author: &files_service.IdentityOptions{
 | 
				
			||||||
				Name:  user4.Name,
 | 
									GitUserName:  user4.Name,
 | 
				
			||||||
				Email: user4.Email,
 | 
									GitUserEmail: user4.Email,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Committer: &files_service.IdentityOptions{
 | 
								Committer: &files_service.IdentityOptions{
 | 
				
			||||||
				Name:  user4.Name,
 | 
									GitUserName:  user4.Name,
 | 
				
			||||||
				Email: user4.Email,
 | 
									GitUserEmail: user4.Email,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Dates: &files_service.CommitDateOptions{
 | 
								Dates: &files_service.CommitDateOptions{
 | 
				
			||||||
				Author:    time.Now(),
 | 
									Author:    time.Now(),
 | 
				
			||||||
@@ -164,12 +164,12 @@ func TestPullRequestTargetEvent(t *testing.T) {
 | 
				
			|||||||
			OldBranch: "main",
 | 
								OldBranch: "main",
 | 
				
			||||||
			NewBranch: "fork-branch-2",
 | 
								NewBranch: "fork-branch-2",
 | 
				
			||||||
			Author: &files_service.IdentityOptions{
 | 
								Author: &files_service.IdentityOptions{
 | 
				
			||||||
				Name:  user4.Name,
 | 
									GitUserName:  user4.Name,
 | 
				
			||||||
				Email: user4.Email,
 | 
									GitUserEmail: user4.Email,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Committer: &files_service.IdentityOptions{
 | 
								Committer: &files_service.IdentityOptions{
 | 
				
			||||||
				Name:  user4.Name,
 | 
									GitUserName:  user4.Name,
 | 
				
			||||||
				Email: user4.Email,
 | 
									GitUserEmail: user4.Email,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Dates: &files_service.CommitDateOptions{
 | 
								Dates: &files_service.CommitDateOptions{
 | 
				
			||||||
				Author:    time.Now(),
 | 
									Author:    time.Now(),
 | 
				
			||||||
@@ -237,12 +237,12 @@ func TestSkipCI(t *testing.T) {
 | 
				
			|||||||
			OldBranch: "master",
 | 
								OldBranch: "master",
 | 
				
			||||||
			NewBranch: "master",
 | 
								NewBranch: "master",
 | 
				
			||||||
			Author: &files_service.IdentityOptions{
 | 
								Author: &files_service.IdentityOptions{
 | 
				
			||||||
				Name:  user2.Name,
 | 
									GitUserName:  user2.Name,
 | 
				
			||||||
				Email: user2.Email,
 | 
									GitUserEmail: user2.Email,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Committer: &files_service.IdentityOptions{
 | 
								Committer: &files_service.IdentityOptions{
 | 
				
			||||||
				Name:  user2.Name,
 | 
									GitUserName:  user2.Name,
 | 
				
			||||||
				Email: user2.Email,
 | 
									GitUserEmail: user2.Email,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Dates: &files_service.CommitDateOptions{
 | 
								Dates: &files_service.CommitDateOptions{
 | 
				
			||||||
				Author:    time.Now(),
 | 
									Author:    time.Now(),
 | 
				
			||||||
@@ -268,12 +268,12 @@ func TestSkipCI(t *testing.T) {
 | 
				
			|||||||
			OldBranch: "master",
 | 
								OldBranch: "master",
 | 
				
			||||||
			NewBranch: "master",
 | 
								NewBranch: "master",
 | 
				
			||||||
			Author: &files_service.IdentityOptions{
 | 
								Author: &files_service.IdentityOptions{
 | 
				
			||||||
				Name:  user2.Name,
 | 
									GitUserName:  user2.Name,
 | 
				
			||||||
				Email: user2.Email,
 | 
									GitUserEmail: user2.Email,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Committer: &files_service.IdentityOptions{
 | 
								Committer: &files_service.IdentityOptions{
 | 
				
			||||||
				Name:  user2.Name,
 | 
									GitUserName:  user2.Name,
 | 
				
			||||||
				Email: user2.Email,
 | 
									GitUserEmail: user2.Email,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Dates: &files_service.CommitDateOptions{
 | 
								Dates: &files_service.CommitDateOptions{
 | 
				
			||||||
				Author:    time.Now(),
 | 
									Author:    time.Now(),
 | 
				
			||||||
@@ -299,12 +299,12 @@ func TestSkipCI(t *testing.T) {
 | 
				
			|||||||
			OldBranch: "master",
 | 
								OldBranch: "master",
 | 
				
			||||||
			NewBranch: "test-skip-ci",
 | 
								NewBranch: "test-skip-ci",
 | 
				
			||||||
			Author: &files_service.IdentityOptions{
 | 
								Author: &files_service.IdentityOptions{
 | 
				
			||||||
				Name:  user2.Name,
 | 
									GitUserName:  user2.Name,
 | 
				
			||||||
				Email: user2.Email,
 | 
									GitUserEmail: user2.Email,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Committer: &files_service.IdentityOptions{
 | 
								Committer: &files_service.IdentityOptions{
 | 
				
			||||||
				Name:  user2.Name,
 | 
									GitUserName:  user2.Name,
 | 
				
			||||||
				Email: user2.Email,
 | 
									GitUserEmail: user2.Email,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Dates: &files_service.CommitDateOptions{
 | 
								Dates: &files_service.CommitDateOptions{
 | 
				
			||||||
				Author:    time.Now(),
 | 
									Author:    time.Now(),
 | 
				
			||||||
@@ -356,12 +356,12 @@ func TestCreateDeleteRefEvent(t *testing.T) {
 | 
				
			|||||||
			OldBranch: "main",
 | 
								OldBranch: "main",
 | 
				
			||||||
			NewBranch: "main",
 | 
								NewBranch: "main",
 | 
				
			||||||
			Author: &files_service.IdentityOptions{
 | 
								Author: &files_service.IdentityOptions{
 | 
				
			||||||
				Name:  user2.Name,
 | 
									GitUserName:  user2.Name,
 | 
				
			||||||
				Email: user2.Email,
 | 
									GitUserEmail: user2.Email,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Committer: &files_service.IdentityOptions{
 | 
								Committer: &files_service.IdentityOptions{
 | 
				
			||||||
				Name:  user2.Name,
 | 
									GitUserName:  user2.Name,
 | 
				
			||||||
				Email: user2.Email,
 | 
									GitUserEmail: user2.Email,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Dates: &files_service.CommitDateOptions{
 | 
								Dates: &files_service.CommitDateOptions{
 | 
				
			||||||
				Author:    time.Now(),
 | 
									Author:    time.Now(),
 | 
				
			||||||
@@ -470,12 +470,12 @@ func TestPullRequestCommitStatusEvent(t *testing.T) {
 | 
				
			|||||||
			OldBranch: "main",
 | 
								OldBranch: "main",
 | 
				
			||||||
			NewBranch: "main",
 | 
								NewBranch: "main",
 | 
				
			||||||
			Author: &files_service.IdentityOptions{
 | 
								Author: &files_service.IdentityOptions{
 | 
				
			||||||
				Name:  user2.Name,
 | 
									GitUserName:  user2.Name,
 | 
				
			||||||
				Email: user2.Email,
 | 
									GitUserEmail: user2.Email,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Committer: &files_service.IdentityOptions{
 | 
								Committer: &files_service.IdentityOptions{
 | 
				
			||||||
				Name:  user2.Name,
 | 
									GitUserName:  user2.Name,
 | 
				
			||||||
				Email: user2.Email,
 | 
									GitUserEmail: user2.Email,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Dates: &files_service.CommitDateOptions{
 | 
								Dates: &files_service.CommitDateOptions{
 | 
				
			||||||
				Author:    time.Now(),
 | 
									Author:    time.Now(),
 | 
				
			||||||
@@ -576,12 +576,12 @@ func TestPullRequestCommitStatusEvent(t *testing.T) {
 | 
				
			|||||||
			OldBranch: testBranch,
 | 
								OldBranch: testBranch,
 | 
				
			||||||
			NewBranch: testBranch,
 | 
								NewBranch: testBranch,
 | 
				
			||||||
			Author: &files_service.IdentityOptions{
 | 
								Author: &files_service.IdentityOptions{
 | 
				
			||||||
				Name:  user2.Name,
 | 
									GitUserName:  user2.Name,
 | 
				
			||||||
				Email: user2.Email,
 | 
									GitUserEmail: user2.Email,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Committer: &files_service.IdentityOptions{
 | 
								Committer: &files_service.IdentityOptions{
 | 
				
			||||||
				Name:  user2.Name,
 | 
									GitUserName:  user2.Name,
 | 
				
			||||||
				Email: user2.Email,
 | 
									GitUserEmail: user2.Email,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Dates: &files_service.CommitDateOptions{
 | 
								Dates: &files_service.CommitDateOptions{
 | 
				
			||||||
				Author:    time.Now(),
 | 
									Author:    time.Now(),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,9 +11,16 @@ import (
 | 
				
			|||||||
	"path"
 | 
						"path"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						repo_model "code.gitea.io/gitea/models/repo"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/unittest"
 | 
				
			||||||
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/json"
 | 
						"code.gitea.io/gitea/modules/json"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/translation"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/tests"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/require"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestCreateFile(t *testing.T) {
 | 
					func TestCreateFile(t *testing.T) {
 | 
				
			||||||
@@ -173,3 +180,102 @@ func TestEditFileToNewBranch(t *testing.T) {
 | 
				
			|||||||
		testEditFileToNewBranch(t, session, "user2", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited)\n")
 | 
							testEditFileToNewBranch(t, session, "user2", "repo1", "master", "feature/test", "README.md", "Hello, World (Edited)\n")
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestEditFileCommitEmail(t *testing.T) {
 | 
				
			||||||
 | 
						onGiteaRun(t, func(t *testing.T, _ *url.URL) {
 | 
				
			||||||
 | 
							user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 | 
				
			||||||
 | 
							assert.True(t, user.KeepEmailPrivate)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							session := loginUser(t, user.Name)
 | 
				
			||||||
 | 
							link := "/user2/repo1/_edit/master/README.md"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							getLastCommitID := func(t *testing.T) string {
 | 
				
			||||||
 | 
								req := NewRequest(t, "GET", link)
 | 
				
			||||||
 | 
								resp := session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
								htmlDoc := NewHTMLParser(t, resp.Body)
 | 
				
			||||||
 | 
								lastCommit := htmlDoc.GetInputValueByName("last_commit")
 | 
				
			||||||
 | 
								require.NotEmpty(t, lastCommit)
 | 
				
			||||||
 | 
								return lastCommit
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							newReq := func(t *testing.T, session *TestSession, email, content string) *RequestWrapper {
 | 
				
			||||||
 | 
								req := NewRequestWithValues(t, "POST", link, map[string]string{
 | 
				
			||||||
 | 
									"_csrf":         GetUserCSRFToken(t, session),
 | 
				
			||||||
 | 
									"last_commit":   getLastCommitID(t),
 | 
				
			||||||
 | 
									"tree_path":     "README.md",
 | 
				
			||||||
 | 
									"content":       content,
 | 
				
			||||||
 | 
									"commit_choice": "direct",
 | 
				
			||||||
 | 
									"commit_email":  email,
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								return req
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							t.Run("EmailInactive", func(t *testing.T) {
 | 
				
			||||||
 | 
								defer tests.PrintCurrentTest(t)()
 | 
				
			||||||
 | 
								email := unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{ID: 35, UID: user.ID})
 | 
				
			||||||
 | 
								assert.False(t, email.IsActivated)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								req := newReq(t, session, email.Email, "test content")
 | 
				
			||||||
 | 
								resp := session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
								htmlDoc := NewHTMLParser(t, resp.Body)
 | 
				
			||||||
 | 
								assert.Contains(t,
 | 
				
			||||||
 | 
									htmlDoc.doc.Find(".ui.negative.message").Text(),
 | 
				
			||||||
 | 
									translation.NewLocale("en-US").Tr("repo.editor.invalid_commit_email"),
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							t.Run("EmailInvalid", func(t *testing.T) {
 | 
				
			||||||
 | 
								defer tests.PrintCurrentTest(t)()
 | 
				
			||||||
 | 
								email := unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{ID: 1, IsActivated: true})
 | 
				
			||||||
 | 
								assert.NotEqualValues(t, email.UID, user.ID)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								req := newReq(t, session, email.Email, "test content")
 | 
				
			||||||
 | 
								resp := session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
								htmlDoc := NewHTMLParser(t, resp.Body)
 | 
				
			||||||
 | 
								assert.Contains(t,
 | 
				
			||||||
 | 
									htmlDoc.doc.Find(".ui.negative.message").Text(),
 | 
				
			||||||
 | 
									translation.NewLocale("en-US").Tr("repo.editor.invalid_commit_email"),
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
 | 
				
			||||||
 | 
							gitRepo, _ := git.OpenRepository(git.DefaultContext, repo1.RepoPath())
 | 
				
			||||||
 | 
							defer gitRepo.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							t.Run("DefaultEmailKeepPrivate", func(t *testing.T) {
 | 
				
			||||||
 | 
								defer tests.PrintCurrentTest(t)()
 | 
				
			||||||
 | 
								req := newReq(t, session, "", "privacy email")
 | 
				
			||||||
 | 
								session.MakeRequest(t, req, http.StatusSeeOther)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								commit, err := gitRepo.GetCommitByPath("README.md")
 | 
				
			||||||
 | 
								assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								fileContent, err := commit.GetFileContent("README.md", 64)
 | 
				
			||||||
 | 
								assert.NoError(t, err)
 | 
				
			||||||
 | 
								assert.EqualValues(t, "privacy email", fileContent)
 | 
				
			||||||
 | 
								assert.EqualValues(t, "User Two", commit.Author.Name)
 | 
				
			||||||
 | 
								assert.EqualValues(t, "user2@noreply.example.org", commit.Author.Email)
 | 
				
			||||||
 | 
								assert.EqualValues(t, "User Two", commit.Committer.Name)
 | 
				
			||||||
 | 
								assert.EqualValues(t, "user2@noreply.example.org", commit.Committer.Email)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							t.Run("ChooseEmail", func(t *testing.T) {
 | 
				
			||||||
 | 
								defer tests.PrintCurrentTest(t)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								email := unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{ID: 3, UID: user.ID, IsActivated: true})
 | 
				
			||||||
 | 
								req := newReq(t, session, email.Email, "chosen email")
 | 
				
			||||||
 | 
								session.MakeRequest(t, req, http.StatusSeeOther)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								commit, err := gitRepo.GetCommitByPath("README.md")
 | 
				
			||||||
 | 
								assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								fileContent, err := commit.GetFileContent("README.md", 64)
 | 
				
			||||||
 | 
								assert.NoError(t, err)
 | 
				
			||||||
 | 
								assert.EqualValues(t, "chosen email", fileContent)
 | 
				
			||||||
 | 
								assert.EqualValues(t, "User Two", commit.Author.Name)
 | 
				
			||||||
 | 
								assert.EqualValues(t, email.Email, commit.Author.Email)
 | 
				
			||||||
 | 
								assert.EqualValues(t, "User Two", commit.Committer.Name)
 | 
				
			||||||
 | 
								assert.EqualValues(t, email.Email, commit.Committer.Email)
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -115,12 +115,12 @@ func createOutdatedPR(t *testing.T, actor, forkOrg *user_model.User) *issues_mod
 | 
				
			|||||||
		OldBranch: "master",
 | 
							OldBranch: "master",
 | 
				
			||||||
		NewBranch: "master",
 | 
							NewBranch: "master",
 | 
				
			||||||
		Author: &files_service.IdentityOptions{
 | 
							Author: &files_service.IdentityOptions{
 | 
				
			||||||
			Name:  actor.Name,
 | 
								GitUserName:  actor.Name,
 | 
				
			||||||
			Email: actor.Email,
 | 
								GitUserEmail: actor.Email,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Committer: &files_service.IdentityOptions{
 | 
							Committer: &files_service.IdentityOptions{
 | 
				
			||||||
			Name:  actor.Name,
 | 
								GitUserName:  actor.Name,
 | 
				
			||||||
			Email: actor.Email,
 | 
								GitUserEmail: actor.Email,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Dates: &files_service.CommitDateOptions{
 | 
							Dates: &files_service.CommitDateOptions{
 | 
				
			||||||
			Author:    time.Now(),
 | 
								Author:    time.Now(),
 | 
				
			||||||
@@ -142,12 +142,12 @@ func createOutdatedPR(t *testing.T, actor, forkOrg *user_model.User) *issues_mod
 | 
				
			|||||||
		OldBranch: "master",
 | 
							OldBranch: "master",
 | 
				
			||||||
		NewBranch: "newBranch",
 | 
							NewBranch: "newBranch",
 | 
				
			||||||
		Author: &files_service.IdentityOptions{
 | 
							Author: &files_service.IdentityOptions{
 | 
				
			||||||
			Name:  actor.Name,
 | 
								GitUserName:  actor.Name,
 | 
				
			||||||
			Email: actor.Email,
 | 
								GitUserEmail: actor.Email,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Committer: &files_service.IdentityOptions{
 | 
							Committer: &files_service.IdentityOptions{
 | 
				
			||||||
			Name:  actor.Name,
 | 
								GitUserName:  actor.Name,
 | 
				
			||||||
			Email: actor.Email,
 | 
								GitUserEmail: actor.Email,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Dates: &files_service.CommitDateOptions{
 | 
							Dates: &files_service.CommitDateOptions{
 | 
				
			||||||
			Author:    time.Now(),
 | 
								Author:    time.Now(),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -71,8 +71,8 @@ func getDeleteRepoFilesOptions(repo *repo_model.Repository) *files_service.Chang
 | 
				
			|||||||
		NewBranch:    repo.DefaultBranch,
 | 
							NewBranch:    repo.DefaultBranch,
 | 
				
			||||||
		Message:      "Deletes README.md",
 | 
							Message:      "Deletes README.md",
 | 
				
			||||||
		Author: &files_service.IdentityOptions{
 | 
							Author: &files_service.IdentityOptions{
 | 
				
			||||||
			Name:  "Bob Smith",
 | 
								GitUserName:  "Bob Smith",
 | 
				
			||||||
			Email: "bob@smith.com",
 | 
								GitUserEmail: "bob@smith.com",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Committer: nil,
 | 
							Committer: nil,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user