新增: 批量更改文件函数定义
This commit is contained in:
parent
93de827180
commit
63ef5921d1
|
@ -300,7 +300,7 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up
|
|||
} else if changed {
|
||||
return nil, models.ErrCommitIDDoesNotMatch{
|
||||
GivenCommitID: opts.LastCommitID,
|
||||
CurrentCommitID: opts.LastCommitID,
|
||||
CurrentCommitID: commit.ID.String(),
|
||||
}
|
||||
}
|
||||
// The file wasn't modified, so we are good to delete it
|
||||
|
@ -504,8 +504,329 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up
|
|||
return file, nil
|
||||
}
|
||||
|
||||
func CreateOrUpdateOrDeleteRepofiles(repo *models.Repository, doer *models.User, opts *BatchUpdateFileOptions) ([]*structs.FileResponse, error) {
|
||||
var responses []*structs.FileResponse
|
||||
func CreateOrUpdateOrDeleteRepofiles(repo *models.Repository, doer *models.User, opts *BatchUpdateFileOptions) (*structs.FileResponse, error) {
|
||||
|
||||
return responses, nil
|
||||
if opts.OldBranch == "" {
|
||||
opts.OldBranch = repo.DefaultBranch
|
||||
}
|
||||
|
||||
if opts.NewBranch == "" {
|
||||
opts.NewBranch = opts.OldBranch
|
||||
}
|
||||
|
||||
// oldBranch must exist for this operation
|
||||
if _, err := repo_module.GetBranch(repo, opts.OldBranch); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if opts.NewBranch != opts.OldBranch {
|
||||
existingBranch, err := repo_module.GetBranch(repo, opts.NewBranch)
|
||||
if existingBranch != nil {
|
||||
return nil, models.ErrBranchAlreadyExists{
|
||||
BranchName: opts.NewBranch,
|
||||
}
|
||||
}
|
||||
if err != nil && !git.IsErrBranchNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
protectedBranch, err := repo.GetBranchProtection(opts.OldBranch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if protectedBranch != nil {
|
||||
if !protectedBranch.CanUserPush(doer.ID) {
|
||||
return nil, models.ErrUserCannotCommit{
|
||||
UserName: doer.LowerName,
|
||||
}
|
||||
}
|
||||
if protectedBranch.RequireSignedCommits {
|
||||
_, _, _, err := repo.SignCRUDAction(doer, repo.RepoPath(), opts.OldBranch)
|
||||
if err != nil {
|
||||
if !models.IsErrWontSign(err) {
|
||||
return nil, err
|
||||
}
|
||||
return nil, models.ErrUserCannotCommit{
|
||||
UserName: doer.LowerName,
|
||||
}
|
||||
}
|
||||
}
|
||||
patterns := protectedBranch.GetProtectedFilePatterns()
|
||||
for _, pat := range patterns {
|
||||
for _, file := range opts.Files {
|
||||
if pat.Match(strings.ToLower(file.TreePath)) {
|
||||
return nil, models.ErrFilePathProtected{
|
||||
Path: file.TreePath,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
optTreePath := opts.Files[0].TreePath
|
||||
optFromTreePath := opts.Files[0].FromTreePath
|
||||
optActionType := opts.Files[0].ActionType
|
||||
optContent := opts.Files[0].Content
|
||||
|
||||
if optTreePath != "" && optFromTreePath == "" {
|
||||
optFromTreePath = optTreePath
|
||||
}
|
||||
|
||||
treePath := CleanUploadFileName(optTreePath)
|
||||
if treePath == "" {
|
||||
return nil, models.ErrFilenameInvalid{
|
||||
Path: optTreePath,
|
||||
}
|
||||
}
|
||||
|
||||
fromTreePath := CleanUploadFileName(optFromTreePath)
|
||||
if fromTreePath == "" && optFromTreePath != "" {
|
||||
return nil, models.ErrFilenameInvalid{
|
||||
Path: optFromTreePath,
|
||||
}
|
||||
}
|
||||
|
||||
message := strings.TrimSpace(opts.Message)
|
||||
author, commiter := GetAuthorAndCommitterUsers(opts.Author, opts.Commiter, doer)
|
||||
|
||||
t, err := NewTemporaryUploadRepository(repo)
|
||||
if err != nil {
|
||||
log.Error("%v", err)
|
||||
}
|
||||
defer t.Close()
|
||||
if err := t.Clone(opts.OldBranch); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := t.SetDefaultIndex(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get the commit of the original branch
|
||||
commit, err := t.GetBranchCommit(opts.OldBranch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if opts.LastCommitID == "" {
|
||||
opts.LastCommitID = commit.ID.String()
|
||||
} else {
|
||||
lastCommitID, err := t.gitRepo.ConvertToSHA1(opts.LastCommitID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ConvertToSHA1: Invalid last commit ID: %v", err)
|
||||
}
|
||||
opts.LastCommitID = lastCommitID.String()
|
||||
}
|
||||
|
||||
encoding := "UTF-8"
|
||||
bom := false
|
||||
executable := false
|
||||
|
||||
switch optActionType {
|
||||
case ActionTypeCreate:
|
||||
case ActionTypeDelete:
|
||||
default:
|
||||
fromEntry, err := commit.GetTreeEntryByPath(fromTreePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if opts.SHA != "" {
|
||||
if opts.SHA != fromEntry.ID.String() {
|
||||
return nil, models.ErrSHADoesNotMatch{
|
||||
Path: optTreePath,
|
||||
GivenSHA: opts.SHA,
|
||||
CurrentSHA: fromEntry.ID.String(),
|
||||
}
|
||||
}
|
||||
} else if opts.LastCommitID != "" {
|
||||
if commit.ID.String() != opts.LastCommitID && opts.OldBranch == opts.NewBranch {
|
||||
if changed, err := commit.FileChangedSinceCommit(treePath, opts.LastCommitID); err != nil {
|
||||
return nil, err
|
||||
} else if changed {
|
||||
return nil, models.ErrCommitIDDoesNotMatch{
|
||||
GivenCommitID: opts.LastCommitID,
|
||||
CurrentCommitID: opts.LastCommitID,
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return nil, models.ErrSHAOrCommitIDNotProvided{}
|
||||
}
|
||||
encoding, bom = detectEncodingAndBOM(fromEntry, repo)
|
||||
executable = fromEntry.IsExecutable()
|
||||
}
|
||||
|
||||
treePathParts := strings.Split(treePath, "/")
|
||||
subTreePath := ""
|
||||
for index, part := range treePathParts {
|
||||
subTreePath = path.Join(subTreePath, part)
|
||||
entry, err := commit.GetTreeEntryByPath(subTreePath)
|
||||
if err != nil {
|
||||
if git.IsErrNotExist(err) {
|
||||
break
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if index < len(treePathParts)-1 {
|
||||
if !entry.IsDir() {
|
||||
return nil, models.ErrFilePathInvalid{
|
||||
Message: fmt.Sprintf("a file exists where you’re trying to create a subdirectory [path: %s]", subTreePath),
|
||||
Path: subTreePath,
|
||||
Name: part,
|
||||
Type: git.EntryModeBlob,
|
||||
}
|
||||
}
|
||||
} else if entry.IsLink() {
|
||||
return nil, models.ErrFilePathInvalid{
|
||||
Message: fmt.Sprintf("a symbolic link exists where you’re trying to create a subdirectory [path: %s]", subTreePath),
|
||||
Path: subTreePath,
|
||||
Name: part,
|
||||
Type: git.EntryModeSymlink,
|
||||
}
|
||||
} else if entry.IsDir() {
|
||||
return nil, models.ErrFilePathInvalid{
|
||||
Message: fmt.Sprintf("a directory exists where you’re trying to create a file [path: %s]", subTreePath),
|
||||
Path: subTreePath,
|
||||
Name: part,
|
||||
Type: git.EntryModeTree,
|
||||
}
|
||||
} else if fromTreePath != treePath || optActionType == ActionTypeCreate {
|
||||
return nil, models.ErrRepoFileAlreadyExists{
|
||||
Path: treePath,
|
||||
}
|
||||
}
|
||||
}
|
||||
// Get the two paths (might be the same if not moving) from the index if they exist
|
||||
filesInIndex, err := t.LsFiles(optTreePath, optFromTreePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("UpdateRepoFile: %v", err)
|
||||
}
|
||||
|
||||
if optActionType == ActionTypeCreate {
|
||||
for _, file := range filesInIndex {
|
||||
if file == optTreePath {
|
||||
return nil, models.ErrRepoFileAlreadyExists{
|
||||
Path: optTreePath,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the old path from the tree
|
||||
if fromTreePath != treePath && len(filesInIndex) > 0 {
|
||||
for _, file := range filesInIndex {
|
||||
if file == fromTreePath {
|
||||
if err := t.RemoveFilesFromIndex(optFromTreePath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
content := optContent
|
||||
if bom {
|
||||
content = string(charset.UTF8BOM) + content
|
||||
}
|
||||
if encoding != "UTF-8" {
|
||||
charsetEncoding, _ := stdcharset.Lookup(encoding)
|
||||
if charsetEncoding != nil {
|
||||
result, _, err := transform.String(charsetEncoding.NewEncoder(), content)
|
||||
if err != nil {
|
||||
log.Error("Error re-encoding %s (%s) as %s - will stay as UTF-8: %v", optTreePath, optFromTreePath, encoding, err)
|
||||
result = content
|
||||
}
|
||||
content = result
|
||||
} else {
|
||||
log.Error("Unknown encoding: %s", encoding)
|
||||
}
|
||||
}
|
||||
|
||||
optContent = content
|
||||
var lfsMetaObject *models.LFSMetaObject
|
||||
if setting.LFS.StartServer {
|
||||
filename2attribute2info, err := t.gitRepo.CheckAttribute(git.CheckAttributeOpts{
|
||||
Attributes: []string{"filter"},
|
||||
Filenames: []string{treePath},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if filename2attribute2info[treePath] != nil && filename2attribute2info[treePath]["filter"] == "lfs" {
|
||||
pointer, err := lfs.GeneratePointer(strings.NewReader(optContent))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lfsMetaObject = &models.LFSMetaObject{Pointer: pointer, RepositoryID: repo.ID}
|
||||
content = pointer.StringContent()
|
||||
}
|
||||
}
|
||||
|
||||
objectHash, err := t.HashObject(strings.NewReader(content))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if executable {
|
||||
if err := t.AddObjectToIndex("100755", objectHash, treePath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err := t.AddObjectToIndex("100644", objectHash, treePath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
treeHash, err := t.WriteTree()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var commitHash string
|
||||
if opts.Dates != nil {
|
||||
commitHash, err = t.CommitTreeWithDate(author, commiter, treeHash, message, opts.Signoff, opts.Dates.Author, opts.Dates.Committer)
|
||||
} else {
|
||||
commitHash, err = t.CommitTree(author, commiter, treeHash, message, opts.Signoff)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if lfsMetaObject != nil {
|
||||
lfsMetaObject, err = models.NewLFSMetaObject(lfsMetaObject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
contentStore := lfs.NewContentStore()
|
||||
exist, err := contentStore.Exists(lfsMetaObject.Pointer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !exist {
|
||||
if err := contentStore.Put(lfsMetaObject.Pointer, strings.NewReader(optContent)); err != nil {
|
||||
if _, err2 := repo.RemoveLFSMetaObjectByOid(lfsMetaObject.Oid); err2 != nil {
|
||||
return nil, fmt.Errorf("Error whilst removing failed inserted LFS object %s: %v (Prev Error: %v)", lfsMetaObject.Oid, err2, err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := t.Push(doer, commitHash, opts.NewBranch); err != nil {
|
||||
log.Error("%T %v", err, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
commit, err = t.GetCommit(commitHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
file, err := GetFileResponseFromCommit(repo, commit, opts.NewBranch, treePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
|
|
@ -455,7 +455,7 @@ func handleCreateOrUpdateFileError(ctx *context.APIContext, err error) {
|
|||
ctx.Error(http.StatusInternalServerError, "UpdateFile", err)
|
||||
}
|
||||
|
||||
func createOrUpdateOrDeleteFiles(ctx *context.APIContext, apiBatchOpts *api.BatchChangeFileOptions) ([]*api.FileResponse, error) {
|
||||
func createOrUpdateOrDeleteFiles(ctx *context.APIContext, apiBatchOpts *api.BatchChangeFileOptions) (*api.FileResponse, error) {
|
||||
if !canWriteFiles(ctx.Repo) {
|
||||
return nil, models.ErrUserDoesNotHaveAccessToRepo{
|
||||
UserID: ctx.User.ID,
|
||||
|
|
Loading…
Reference in New Issue