* 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>tags/v1.11.0-rc1
"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" | ||||
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, | ||||
} | } | ||||
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", | ||||
}, | }, | ||||
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() | ||||
} | } | ||||
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 | ||||
} | } | ||||
// 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 | ||||
} | } |
// 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() | ||||
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) |
"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" | ||||
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 | ||||
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) { | ||||
} | } | ||||
// 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 | ||||
} | } |
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)"` | ||||
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"` | |||||
} |
// new_branch (optional) will make a new branch from `branch` before creating the file | // new_branch (optional) will make a new branch from `branch` before creating the file | ||||
NewBranchName string `json:"new_branch" binding:"GitRefName;MaxSize(100)"` | 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` 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 | // CreateFileOptions options for creating files |
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" | ||||
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 == "" { | ||||
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 == "" { | ||||
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 == "" { |
// 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 | ||||
} | } |
}, | }, | ||||
"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.", | ||||
"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", | ||||
"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", | ||||
"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", |