diff --git a/modules/forms/repo_form.go b/modules/forms/repo_form.go index 4a478c7d3..4d37ab072 100644 --- a/modules/forms/repo_form.go +++ b/modules/forms/repo_form.go @@ -698,6 +698,7 @@ type EditRepoFileForm struct { CommitChoice string `binding:"Required;MaxSize(50)"` NewBranchName string `binding:"GitRefName;MaxSize(100)"` LastCommit string + Signoff bool } // Validate validates the fields @@ -733,6 +734,7 @@ type UploadRepoFileForm struct { CommitChoice string `binding:"Required;MaxSize(50)"` NewBranchName string `binding:"GitRefName;MaxSize(100)"` Files []string + Signoff bool } // Validate validates the fields @@ -766,6 +768,7 @@ type DeleteRepoFileForm struct { CommitChoice string `binding:"Required;MaxSize(50)"` NewBranchName string `binding:"GitRefName;MaxSize(100)"` LastCommit string + Signoff bool } // Validate validates the fields diff --git a/modules/repofiles/delete.go b/modules/repofiles/delete.go index 8343776c4..2b8ddf3cc 100644 --- a/modules/repofiles/delete.go +++ b/modules/repofiles/delete.go @@ -25,6 +25,7 @@ type DeleteRepoFileOptions struct { Author *IdentityOptions Committer *IdentityOptions Dates *CommitDateOptions + Signoff bool } // DeleteRepoFile deletes a file in the given repository @@ -199,9 +200,9 @@ func DeleteRepoFile(repo *models.Repository, doer *models.User, opts *DeleteRepo // Now commit the tree var commitHash string if opts.Dates != nil { - commitHash, err = t.CommitTreeWithDate(author, committer, treeHash, message, opts.Dates.Author, opts.Dates.Committer) + commitHash, err = t.CommitTreeWithDate(author, committer, treeHash, message, opts.Signoff, opts.Dates.Author, opts.Dates.Committer) } else { - commitHash, err = t.CommitTree(author, committer, treeHash, message) + commitHash, err = t.CommitTree(author, committer, treeHash, message, opts.Signoff) } if err != nil { return nil, err diff --git a/modules/repofiles/temp_repo.go b/modules/repofiles/temp_repo.go index a02bba65e..99c62a16c 100644 --- a/modules/repofiles/temp_repo.go +++ b/modules/repofiles/temp_repo.go @@ -185,12 +185,12 @@ func (t *TemporaryUploadRepository) GetLastCommitByRef(ref string) (string, erro } // CommitTree creates a commit from a given tree for the user with provided message -func (t *TemporaryUploadRepository) CommitTree(author, committer *models.User, treeHash string, message string) (string, error) { - return t.CommitTreeWithDate(author, committer, treeHash, message, time.Now(), time.Now()) +func (t *TemporaryUploadRepository) CommitTree(author, committer *models.User, treeHash string, message string, signoff bool) (string, error) { + return t.CommitTreeWithDate(author, committer, treeHash, message, signoff, time.Now(), time.Now()) } // CommitTreeWithDate creates a commit from a given tree for the user with provided message -func (t *TemporaryUploadRepository) CommitTreeWithDate(author, committer *models.User, treeHash string, message string, authorDate, committerDate time.Time) (string, error) { +func (t *TemporaryUploadRepository) CommitTreeWithDate(author, committer *models.User, treeHash string, message string, signoff bool, authorDate, committerDate time.Time) (string, error) { authorSig := author.NewGitSig() committerSig := committer.NewGitSig() @@ -236,6 +236,13 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(author, committer *models } } + if signoff { + // Signed-off-by + _, _ = messageBytes.WriteString("\n") + _, _ = messageBytes.WriteString("Signed-off-by: ") + _, _ = messageBytes.WriteString(committerSig.String()) + } + env = append(env, "GIT_COMMITTER_NAME="+committerSig.Name, "GIT_COMMITTER_EMAIL="+committerSig.Email, diff --git a/modules/repofiles/update.go b/modules/repofiles/update.go index a1a9c624d..0ee1ada34 100644 --- a/modules/repofiles/update.go +++ b/modules/repofiles/update.go @@ -51,6 +51,7 @@ type UpdateRepoFileOptions struct { Author *IdentityOptions Committer *IdentityOptions Dates *CommitDateOptions + Signoff bool } func detectEncodingAndBOM(entry *git.TreeEntry, repo *models.Repository) (string, bool) { @@ -417,9 +418,9 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up // Now commit the tree var commitHash string if opts.Dates != nil { - commitHash, err = t.CommitTreeWithDate(author, committer, treeHash, message, opts.Dates.Author, opts.Dates.Committer) + commitHash, err = t.CommitTreeWithDate(author, committer, treeHash, message, opts.Signoff, opts.Dates.Author, opts.Dates.Committer) } else { - commitHash, err = t.CommitTree(author, committer, treeHash, message) + commitHash, err = t.CommitTree(author, committer, treeHash, message, opts.Signoff) } if err != nil { return nil, err diff --git a/modules/repofiles/upload.go b/modules/repofiles/upload.go index e3ec48ec0..c261e188c 100644 --- a/modules/repofiles/upload.go +++ b/modules/repofiles/upload.go @@ -24,6 +24,7 @@ type UploadRepoFileOptions struct { TreePath string Message string Files []string // In UUID format. + Signoff bool } type uploadInfo struct { @@ -143,7 +144,7 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep committer := doer // Now commit the tree - commitHash, err := t.CommitTree(author, committer, treeHash, opts.Message) + commitHash, err := t.CommitTree(author, committer, treeHash, opts.Message, opts.Signoff) if err != nil { return err } diff --git a/modules/structs/repo_file.go b/modules/structs/repo_file.go index c34923e38..71733c90e 100644 --- a/modules/structs/repo_file.go +++ b/modules/structs/repo_file.go @@ -17,6 +17,8 @@ type FileOptions struct { Author Identity `json:"author"` Committer Identity `json:"committer"` Dates CommitDateOptions `json:"dates"` + // Add a Signed-off-by trailer by the committer at the end of the commit log message. + Signoff bool `json:"signoff"` } // CreateFileOptions options for creating files diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 362b09e29..a7dae87cd 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -874,6 +874,7 @@ editor.add = Add '%s' editor.update = Update '%s' editor.delete = Delete '%s' editor.commit_message_desc = Add an optional extended description… +editor.signoff_desc = Add a Signed-off-by trailer by the committer at the end of the commit log message. editor.commit_directly_to_this_branch = Commit directly to the %s branch. editor.create_new_branch = Create a new branch for this commit and start a pull request. editor.create_new_branch_np = Create a new branch for this commit. diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index 044d0fe56..2bd57f146 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -235,6 +235,7 @@ func CreateFile(ctx *context.APIContext) { Author: apiOpts.Dates.Author, Committer: apiOpts.Dates.Committer, }, + Signoff: apiOpts.Signoff, } if opts.Dates.Author.IsZero() { opts.Dates.Author = time.Now() @@ -323,6 +324,7 @@ func UpdateFile(ctx *context.APIContext) { Author: apiOpts.Dates.Author, Committer: apiOpts.Dates.Committer, }, + Signoff: apiOpts.Signoff, } if opts.Dates.Author.IsZero() { opts.Dates.Author = time.Now() @@ -449,6 +451,7 @@ func DeleteFile(ctx *context.APIContext) { Author: apiOpts.Dates.Author, Committer: apiOpts.Dates.Committer, }, + Signoff: apiOpts.Signoff, } if opts.Dates.Author.IsZero() { opts.Dates.Author = time.Now() diff --git a/routers/repo/editor.go b/routers/repo/editor.go index 619912fef..344174828 100644 --- a/routers/repo/editor.go +++ b/routers/repo/editor.go @@ -240,6 +240,7 @@ func editFilePost(ctx *context.Context, form auth.EditRepoFileForm, isNewFile bo Message: message, Content: strings.ReplaceAll(form.Content, "\r", ""), IsNewFile: isNewFile, + Signoff: form.Signoff, }); err != nil { // This is where we handle all the errors thrown by repofiles.CreateOrUpdateRepoFile if git.IsErrNotExist(err) { @@ -442,6 +443,7 @@ func DeleteFilePost(ctx *context.Context) { NewBranch: branchName, TreePath: ctx.Repo.TreePath, Message: message, + Signoff: form.Signoff, }); err != nil { // This is where we handle all the errors thrown by repofiles.DeleteRepoFile if git.IsErrNotExist(err) || models.IsErrRepoFileDoesNotExist(err) { @@ -650,6 +652,7 @@ func UploadFilePost(ctx *context.Context) { TreePath: form.TreePath, Message: message, Files: form.Files, + Signoff: form.Signoff, }); err != nil { if models.IsErrLFSFileLocked(err) { ctx.Data["Err_TreePath"] = true diff --git a/templates/repo/editor/commit_form.tmpl b/templates/repo/editor/commit_form.tmpl index 494a2113e..cd417b427 100644 --- a/templates/repo/editor/commit_form.tmpl +++ b/templates/repo/editor/commit_form.tmpl @@ -14,6 +14,12 @@
+
+
+ + +
+
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 86f09e2bb..8c2b5948e 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -12023,6 +12023,11 @@ "description": "new_branch (optional) will make a new branch from `branch` before creating the file", "type": "string", "x-go-name": "NewBranchName" + }, + "signoff": { + "description": "Add a Signed-off-by trailer by the committer at the end of the commit log message.", + "type": "boolean", + "x-go-name": "Signoff" } }, "x-go-package": "code.gitea.io/gitea/modules/structs" @@ -12731,6 +12736,11 @@ "description": "sha is the SHA for the file that already exists", "type": "string", "x-go-name": "SHA" + }, + "signoff": { + "description": "Add a Signed-off-by trailer by the committer at the end of the commit log message.", + "type": "boolean", + "x-go-name": "Signoff" } }, "x-go-package": "code.gitea.io/gitea/modules/structs" @@ -15762,6 +15772,11 @@ "description": "sha is the SHA for the file that already exists", "type": "string", "x-go-name": "SHA" + }, + "signoff": { + "description": "Add a Signed-off-by trailer by the committer at the end of the commit log message.", + "type": "boolean", + "x-go-name": "Signoff" } }, "x-go-package": "code.gitea.io/gitea/modules/structs"