[API] Extend contents with dates (#9464)

* extend CommitTree func

* make sure Date NOT nil

* spell corection

Co-Authored-By: zeripath <art27@cantab.net>

* add TEST

Co-authored-by: zeripath <art27@cantab.net>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
6543 2019-12-24 03:33:52 +01:00 committed by Lunny Xiao
parent 017f314b5a
commit 40e99ea010
9 changed files with 114 additions and 7 deletions

View file

@ -11,6 +11,7 @@ import (
"net/url" "net/url"
"path/filepath" "path/filepath"
"testing" "testing"
"time"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
@ -37,6 +38,10 @@ func getCreateFileOptions() api.CreateFileOptions {
Name: "John Doe", Name: "John Doe",
Email: "johndoe@example.com", Email: "johndoe@example.com",
}, },
Dates: api.CommitDateOptions{
Author: time.Unix(946684810, 0),
Committer: time.Unix(978307190, 0),
},
}, },
Content: contentEncoded, Content: contentEncoded,
} }
@ -80,12 +85,14 @@ func getExpectedFileResponseForCreate(commitID, treePath string) *api.FileRespon
Name: "Anne Doe", Name: "Anne Doe",
Email: "annedoe@example.com", Email: "annedoe@example.com",
}, },
Date: "2000-01-01T00:00:10Z",
}, },
Committer: &api.CommitUser{ Committer: &api.CommitUser{
Identity: api.Identity{ Identity: api.Identity{
Name: "John Doe", Name: "John Doe",
Email: "johndoe@example.com", Email: "johndoe@example.com",
}, },
Date: "2000-12-31T23:59:50Z",
}, },
Message: "Updates README.md\n", 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.HTMLURL, fileResponse.Commit.HTMLURL)
assert.EqualValues(t, expectedFileResponse.Commit.Author.Email, fileResponse.Commit.Author.Email) 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.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() gitRepo.Close()
} }

View file

@ -23,6 +23,7 @@ type DeleteRepoFileOptions struct {
SHA string SHA string
Author *IdentityOptions Author *IdentityOptions
Committer *IdentityOptions Committer *IdentityOptions
Dates *CommitDateOptions
} }
// DeleteRepoFile deletes a file in the given repository // 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 // 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 { if err != nil {
return nil, err return nil, err
} }

View file

@ -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 // 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) { 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() authorSig := author.NewGitSig()
committerSig := committer.NewGitSig() committerSig := committer.NewGitSig()
@ -201,10 +205,10 @@ func (t *TemporaryUploadRepository) CommitTree(author, committer *models.User, t
env := append(os.Environ(), env := append(os.Environ(),
"GIT_AUTHOR_NAME="+authorSig.Name, "GIT_AUTHOR_NAME="+authorSig.Name,
"GIT_AUTHOR_EMAIL="+authorSig.Email, "GIT_AUTHOR_EMAIL="+authorSig.Email,
"GIT_AUTHOR_DATE="+commitTimeStr, "GIT_AUTHOR_DATE="+authorDate.Format(time.RFC3339),
"GIT_COMMITTER_NAME="+committerSig.Name, "GIT_COMMITTER_NAME="+committerSig.Name,
"GIT_COMMITTER_EMAIL="+committerSig.Email, "GIT_COMMITTER_EMAIL="+committerSig.Email,
"GIT_COMMITTER_DATE="+commitTimeStr, "GIT_COMMITTER_DATE="+committerDate.Format(time.RFC3339),
) )
messageBytes := new(bytes.Buffer) messageBytes := new(bytes.Buffer)

View file

@ -10,6 +10,7 @@ import (
"fmt" "fmt"
"path" "path"
"strings" "strings"
"time"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/cache"
@ -31,6 +32,12 @@ type IdentityOptions struct {
Email string 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 // UpdateRepoFileOptions holds the repository file update options
type UpdateRepoFileOptions struct { type UpdateRepoFileOptions struct {
LastCommitID string LastCommitID string
@ -44,6 +51,7 @@ type UpdateRepoFileOptions struct {
IsNewFile bool IsNewFile bool
Author *IdentityOptions Author *IdentityOptions
Committer *IdentityOptions Committer *IdentityOptions
Dates *CommitDateOptions
} }
func detectEncodingAndBOM(entry *git.TreeEntry, repo *models.Repository) (string, bool) { 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 // 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 { if err != nil {
return nil, err return nil, err
} }

View file

@ -5,6 +5,10 @@
package structs package structs
import (
"time"
)
// Identity for a person's identity like an author or committer // Identity for a person's identity like an author or committer
type Identity struct { type Identity struct {
Name string `json:"name" binding:"MaxSize(100)"` Name string `json:"name" binding:"MaxSize(100)"`
@ -42,3 +46,11 @@ type Commit struct {
Committer *User `json:"committer"` Committer *User `json:"committer"`
Parents []*CommitMeta `json:"parents"` 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"`
}

View file

@ -16,6 +16,7 @@ type FileOptions struct {
// `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` 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"` Author Identity `json:"author"`
Committer Identity `json:"committer"` Committer Identity `json:"committer"`
Dates CommitDateOptions `json:"dates"`
} }
// CreateFileOptions options for creating files // CreateFileOptions options for creating files

View file

@ -8,6 +8,7 @@ package repo
import ( import (
"encoding/base64" "encoding/base64"
"net/http" "net/http"
"time"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
@ -213,6 +214,16 @@ func CreateFile(ctx *context.APIContext, apiOpts api.CreateFileOptions) {
Name: apiOpts.Author.Name, Name: apiOpts.Author.Name,
Email: apiOpts.Author.Email, 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 == "" { if opts.Message == "" {
@ -277,6 +288,16 @@ func UpdateFile(ctx *context.APIContext, apiOpts api.UpdateFileOptions) {
Name: apiOpts.Author.Name, Name: apiOpts.Author.Name,
Email: apiOpts.Author.Email, 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 == "" { if opts.Message == "" {
@ -364,6 +385,16 @@ func DeleteFile(ctx *context.APIContext, apiOpts api.DeleteFileOptions) {
Name: apiOpts.Author.Name, Name: apiOpts.Author.Name,
Email: apiOpts.Author.Email, 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 == "" { if opts.Message == "" {

View file

@ -118,6 +118,9 @@ type swaggerParameterBodies struct {
// in:body // in:body
DeleteFileOptions api.DeleteFileOptions DeleteFileOptions api.DeleteFileOptions
// in:body
CommitDateOptions api.CommitDateOptions
// in:body // in:body
RepoTopicOptions api.RepoTopicOptions RepoTopicOptions api.RepoTopicOptions
} }

View file

@ -8273,6 +8273,23 @@
}, },
"x-go-package": "code.gitea.io/gitea/modules/structs" "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": { "CommitMeta": {
"type": "object", "type": "object",
"title": "CommitMeta contains meta information of a commit in terms of API.", "title": "CommitMeta contains meta information of a commit in terms of API.",
@ -8414,6 +8431,9 @@
"type": "string", "type": "string",
"x-go-name": "Content" "x-go-name": "Content"
}, },
"dates": {
"$ref": "#/definitions/CommitDateOptions"
},
"message": { "message": {
"description": "message (optional) for the commit of this file. if not supplied, a default message will be used", "description": "message (optional) for the commit of this file. if not supplied, a default message will be used",
"type": "string", "type": "string",
@ -8972,6 +8992,9 @@
"committer": { "committer": {
"$ref": "#/definitions/Identity" "$ref": "#/definitions/Identity"
}, },
"dates": {
"$ref": "#/definitions/CommitDateOptions"
},
"message": { "message": {
"description": "message (optional) for the commit of this file. if not supplied, a default message will be used", "description": "message (optional) for the commit of this file. if not supplied, a default message will be used",
"type": "string", "type": "string",
@ -11303,6 +11326,9 @@
"type": "string", "type": "string",
"x-go-name": "Content" "x-go-name": "Content"
}, },
"dates": {
"$ref": "#/definitions/CommitDateOptions"
},
"from_path": { "from_path": {
"description": "from_path (optional) is the path of the original file which will be moved/renamed to the path in the URL", "description": "from_path (optional) is the path of the original file which will be moved/renamed to the path in the URL",
"type": "string", "type": "string",