From 40e99ea010b3b0be241be0e1365a70d33777bcf2 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 24 Dec 2019 03:33:52 +0100 Subject: [PATCH] [API] Extend contents with dates (#9464) * extend CommitTree func * make sure Date NOT nil * spell corection Co-Authored-By: zeripath * add TEST Co-authored-by: zeripath Co-authored-by: Lunny Xiao --- integrations/api_repo_file_create_test.go | 11 ++++++++ modules/repofiles/delete.go | 8 +++++- modules/repofiles/temp_repo.go | 10 +++++--- modules/repofiles/update.go | 15 ++++++++++- modules/structs/repo_commit.go | 12 +++++++++ modules/structs/repo_file.go | 5 ++-- routers/api/v1/repo/file.go | 31 +++++++++++++++++++++++ routers/api/v1/swagger/options.go | 3 +++ templates/swagger/v1_json.tmpl | 26 +++++++++++++++++++ 9 files changed, 114 insertions(+), 7 deletions(-) diff --git a/integrations/api_repo_file_create_test.go b/integrations/api_repo_file_create_test.go index 53042b5d0..3c8b50d5d 100644 --- a/integrations/api_repo_file_create_test.go +++ b/integrations/api_repo_file_create_test.go @@ -11,6 +11,7 @@ import ( "net/url" "path/filepath" "testing" + "time" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" @@ -37,6 +38,10 @@ func getCreateFileOptions() api.CreateFileOptions { Name: "John Doe", Email: "johndoe@example.com", }, + Dates: api.CommitDateOptions{ + Author: time.Unix(946684810, 0), + Committer: time.Unix(978307190, 0), + }, }, Content: contentEncoded, } @@ -80,12 +85,14 @@ func getExpectedFileResponseForCreate(commitID, treePath string) *api.FileRespon Name: "Anne Doe", Email: "annedoe@example.com", }, + Date: "2000-01-01T00:00:10Z", }, Committer: &api.CommitUser{ Identity: api.Identity{ Name: "John Doe", Email: "johndoe@example.com", }, + Date: "2000-12-31T23:59:50Z", }, Message: "Updates README.md\n", }, @@ -139,6 +146,10 @@ func TestAPICreateFile(t *testing.T) { assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) assert.EqualValues(t, expectedFileResponse.Commit.Author.Name, fileResponse.Commit.Author.Name) + assert.EqualValues(t, expectedFileResponse.Commit.Author.Date, fileResponse.Commit.Author.Date) + assert.EqualValues(t, expectedFileResponse.Commit.Committer.Email, fileResponse.Commit.Committer.Email) + assert.EqualValues(t, expectedFileResponse.Commit.Committer.Name, fileResponse.Commit.Committer.Name) + assert.EqualValues(t, expectedFileResponse.Commit.Committer.Date, fileResponse.Commit.Committer.Date) gitRepo.Close() } diff --git a/modules/repofiles/delete.go b/modules/repofiles/delete.go index 95b080402..43937c49e 100644 --- a/modules/repofiles/delete.go +++ b/modules/repofiles/delete.go @@ -23,6 +23,7 @@ type DeleteRepoFileOptions struct { SHA string Author *IdentityOptions Committer *IdentityOptions + Dates *CommitDateOptions } // DeleteRepoFile deletes a file in the given repository @@ -168,7 +169,12 @@ func DeleteRepoFile(repo *models.Repository, doer *models.User, opts *DeleteRepo } // Now commit the tree - commitHash, err := t.CommitTree(author, committer, treeHash, message) + var commitHash string + if opts.Dates != nil { + commitHash, err = t.CommitTreeWithDate(author, committer, treeHash, message, opts.Dates.Author, opts.Dates.Committer) + } else { + commitHash, err = t.CommitTree(author, committer, treeHash, message) + } if err != nil { return nil, err } diff --git a/modules/repofiles/temp_repo.go b/modules/repofiles/temp_repo.go index 6bd775d9d..f9ea4ba15 100644 --- a/modules/repofiles/temp_repo.go +++ b/modules/repofiles/temp_repo.go @@ -188,7 +188,11 @@ 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) { - commitTimeStr := time.Now().Format(time.RFC3339) + return t.CommitTreeWithDate(author, committer, treeHash, message, 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) { authorSig := author.NewGitSig() committerSig := committer.NewGitSig() @@ -201,10 +205,10 @@ func (t *TemporaryUploadRepository) CommitTree(author, committer *models.User, t env := append(os.Environ(), "GIT_AUTHOR_NAME="+authorSig.Name, "GIT_AUTHOR_EMAIL="+authorSig.Email, - "GIT_AUTHOR_DATE="+commitTimeStr, + "GIT_AUTHOR_DATE="+authorDate.Format(time.RFC3339), "GIT_COMMITTER_NAME="+committerSig.Name, "GIT_COMMITTER_EMAIL="+committerSig.Email, - "GIT_COMMITTER_DATE="+commitTimeStr, + "GIT_COMMITTER_DATE="+committerDate.Format(time.RFC3339), ) messageBytes := new(bytes.Buffer) diff --git a/modules/repofiles/update.go b/modules/repofiles/update.go index 4d2f1d5f0..8a95b4422 100644 --- a/modules/repofiles/update.go +++ b/modules/repofiles/update.go @@ -10,6 +10,7 @@ import ( "fmt" "path" "strings" + "time" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/cache" @@ -31,6 +32,12 @@ type IdentityOptions struct { Email string } +// CommitDateOptions store dates for GIT_AUTHOR_DATE and GIT_COMMITTER_DATE +type CommitDateOptions struct { + Author time.Time + Committer time.Time +} + // UpdateRepoFileOptions holds the repository file update options type UpdateRepoFileOptions struct { LastCommitID string @@ -44,6 +51,7 @@ type UpdateRepoFileOptions struct { IsNewFile bool Author *IdentityOptions Committer *IdentityOptions + Dates *CommitDateOptions } func detectEncodingAndBOM(entry *git.TreeEntry, repo *models.Repository) (string, bool) { @@ -371,7 +379,12 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up } // Now commit the tree - commitHash, err := t.CommitTree(author, committer, treeHash, message) + var commitHash string + if opts.Dates != nil { + commitHash, err = t.CommitTreeWithDate(author, committer, treeHash, message, opts.Dates.Author, opts.Dates.Committer) + } else { + commitHash, err = t.CommitTree(author, committer, treeHash, message) + } if err != nil { return nil, err } diff --git a/modules/structs/repo_commit.go b/modules/structs/repo_commit.go index 9cde2873d..088ccdf5a 100644 --- a/modules/structs/repo_commit.go +++ b/modules/structs/repo_commit.go @@ -5,6 +5,10 @@ package structs +import ( + "time" +) + // Identity for a person's identity like an author or committer type Identity struct { Name string `json:"name" binding:"MaxSize(100)"` @@ -42,3 +46,11 @@ type Commit struct { Committer *User `json:"committer"` Parents []*CommitMeta `json:"parents"` } + +// CommitDateOptions store dates for GIT_AUTHOR_DATE and GIT_COMMITTER_DATE +type CommitDateOptions struct { + // swagger:strfmt date-time + Author time.Time `json:"author"` + // swagger:strfmt date-time + Committer time.Time `json:"committer"` +} diff --git a/modules/structs/repo_file.go b/modules/structs/repo_file.go index cb836e2e2..c34923e38 100644 --- a/modules/structs/repo_file.go +++ b/modules/structs/repo_file.go @@ -14,8 +14,9 @@ type FileOptions struct { // new_branch (optional) will make a new branch from `branch` before creating the file NewBranchName string `json:"new_branch" binding:"GitRefName;MaxSize(100)"` // `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used) - Author Identity `json:"author"` - Committer Identity `json:"committer"` + Author Identity `json:"author"` + Committer Identity `json:"committer"` + Dates CommitDateOptions `json:"dates"` } // CreateFileOptions options for creating files diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index 8cfe039df..14923984b 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -8,6 +8,7 @@ package repo import ( "encoding/base64" "net/http" + "time" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" @@ -213,6 +214,16 @@ func CreateFile(ctx *context.APIContext, apiOpts api.CreateFileOptions) { Name: apiOpts.Author.Name, Email: apiOpts.Author.Email, }, + Dates: &repofiles.CommitDateOptions{ + Author: apiOpts.Dates.Author, + Committer: apiOpts.Dates.Committer, + }, + } + if opts.Dates.Author.IsZero() { + opts.Dates.Author = time.Now() + } + if opts.Dates.Committer.IsZero() { + opts.Dates.Committer = time.Now() } if opts.Message == "" { @@ -277,6 +288,16 @@ func UpdateFile(ctx *context.APIContext, apiOpts api.UpdateFileOptions) { Name: apiOpts.Author.Name, Email: apiOpts.Author.Email, }, + Dates: &repofiles.CommitDateOptions{ + Author: apiOpts.Dates.Author, + Committer: apiOpts.Dates.Committer, + }, + } + if opts.Dates.Author.IsZero() { + opts.Dates.Author = time.Now() + } + if opts.Dates.Committer.IsZero() { + opts.Dates.Committer = time.Now() } if opts.Message == "" { @@ -364,6 +385,16 @@ func DeleteFile(ctx *context.APIContext, apiOpts api.DeleteFileOptions) { Name: apiOpts.Author.Name, Email: apiOpts.Author.Email, }, + Dates: &repofiles.CommitDateOptions{ + Author: apiOpts.Dates.Author, + Committer: apiOpts.Dates.Committer, + }, + } + if opts.Dates.Author.IsZero() { + opts.Dates.Author = time.Now() + } + if opts.Dates.Committer.IsZero() { + opts.Dates.Committer = time.Now() } if opts.Message == "" { diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go index 80e4bf422..74a475e27 100644 --- a/routers/api/v1/swagger/options.go +++ b/routers/api/v1/swagger/options.go @@ -118,6 +118,9 @@ type swaggerParameterBodies struct { // in:body DeleteFileOptions api.DeleteFileOptions + // in:body + CommitDateOptions api.CommitDateOptions + // in:body RepoTopicOptions api.RepoTopicOptions } diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index dc9dd2395..c4fa1f311 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -8273,6 +8273,23 @@ }, "x-go-package": "code.gitea.io/gitea/modules/structs" }, + "CommitDateOptions": { + "description": "CommitDateOptions store dates for GIT_AUTHOR_DATE and GIT_COMMITTER_DATE", + "type": "object", + "properties": { + "author": { + "type": "string", + "format": "date-time", + "x-go-name": "Author" + }, + "committer": { + "type": "string", + "format": "date-time", + "x-go-name": "Committer" + } + }, + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, "CommitMeta": { "type": "object", "title": "CommitMeta contains meta information of a commit in terms of API.", @@ -8414,6 +8431,9 @@ "type": "string", "x-go-name": "Content" }, + "dates": { + "$ref": "#/definitions/CommitDateOptions" + }, "message": { "description": "message (optional) for the commit of this file. if not supplied, a default message will be used", "type": "string", @@ -8972,6 +8992,9 @@ "committer": { "$ref": "#/definitions/Identity" }, + "dates": { + "$ref": "#/definitions/CommitDateOptions" + }, "message": { "description": "message (optional) for the commit of this file. if not supplied, a default message will be used", "type": "string", @@ -11303,6 +11326,9 @@ "type": "string", "x-go-name": "Content" }, + "dates": { + "$ref": "#/definitions/CommitDateOptions" + }, "from_path": { "description": "from_path (optional) is the path of the original file which will be moved/renamed to the path in the URL", "type": "string",