diff options
author | Richard Mahn <richmahn@users.noreply.github.com> | 2019-04-17 10:06:35 -0600 |
---|---|---|
committer | techknowlogick <matti@mdranta.net> | 2019-04-17 12:06:35 -0400 |
commit | 2262811e407facea09047e94aa1850c192511587 (patch) | |
tree | a478624613dc6cf095784d629f9627b197de15e8 /modules/uploader | |
parent | 059195b127848d96a4690257af19d8c97c6d2363 (diff) | |
download | gitea-2262811e407facea09047e94aa1850c192511587.tar.gz gitea-2262811e407facea09047e94aa1850c192511587.zip |
Fixes 4762 - Content API for Creating, Updating, Deleting Files (#6314)
Diffstat (limited to 'modules/uploader')
-rw-r--r-- | modules/uploader/delete.go | 100 | ||||
-rw-r--r-- | modules/uploader/diff.go | 38 | ||||
-rw-r--r-- | modules/uploader/repo.go | 359 | ||||
-rw-r--r-- | modules/uploader/update.go | 159 | ||||
-rw-r--r-- | modules/uploader/upload.go | 205 |
5 files changed, 0 insertions, 861 deletions
diff --git a/modules/uploader/delete.go b/modules/uploader/delete.go deleted file mode 100644 index 2353f18c46..0000000000 --- a/modules/uploader/delete.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2019 The Gitea Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package uploader - -import ( - "fmt" - - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/git" -) - -// DeleteRepoFileOptions holds the repository delete file options -type DeleteRepoFileOptions struct { - LastCommitID string - OldBranch string - NewBranch string - TreePath string - Message string -} - -// DeleteRepoFile deletes a file in the given repository -func DeleteRepoFile(repo *models.Repository, doer *models.User, opts *DeleteRepoFileOptions) error { - t, err := NewTemporaryUploadRepository(repo) - defer t.Close() - if err != nil { - return err - } - if err := t.Clone(opts.OldBranch); err != nil { - return err - } - if err := t.SetDefaultIndex(); err != nil { - return err - } - - filesInIndex, err := t.LsFiles(opts.TreePath) - if err != nil { - return fmt.Errorf("UpdateRepoFile: %v", err) - } - - inFilelist := false - for _, file := range filesInIndex { - if file == opts.TreePath { - inFilelist = true - } - } - if !inFilelist { - return git.ErrNotExist{RelPath: opts.TreePath} - } - - if err := t.RemoveFilesFromIndex(opts.TreePath); err != nil { - return err - } - - // Now write the tree - treeHash, err := t.WriteTree() - if err != nil { - return err - } - - // Now commit the tree - commitHash, err := t.CommitTree(doer, treeHash, opts.Message) - if err != nil { - return err - } - - // Then push this tree to NewBranch - if err := t.Push(doer, commitHash, opts.NewBranch); err != nil { - return err - } - - // Simulate push event. - oldCommitID := opts.LastCommitID - if opts.NewBranch != opts.OldBranch { - oldCommitID = git.EmptySHA - } - - if err = repo.GetOwner(); err != nil { - return fmt.Errorf("GetOwner: %v", err) - } - err = models.PushUpdate( - opts.NewBranch, - models.PushUpdateOptions{ - PusherID: doer.ID, - PusherName: doer.Name, - RepoUserName: repo.Owner.Name, - RepoName: repo.Name, - RefFullName: git.BranchPrefix + opts.NewBranch, - OldCommitID: oldCommitID, - NewCommitID: commitHash, - }, - ) - if err != nil { - return fmt.Errorf("PushUpdate: %v", err) - } - - // FIXME: Should we UpdateRepoIndexer(repo) here? - return nil -} diff --git a/modules/uploader/diff.go b/modules/uploader/diff.go deleted file mode 100644 index e01947ea61..0000000000 --- a/modules/uploader/diff.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2019 The Gitea Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package uploader - -import ( - "strings" - - "code.gitea.io/gitea/models" -) - -// GetDiffPreview produces and returns diff result of a file which is not yet committed. -func GetDiffPreview(repo *models.Repository, branch, treePath, content string) (*models.Diff, error) { - t, err := NewTemporaryUploadRepository(repo) - defer t.Close() - if err != nil { - return nil, err - } - if err := t.Clone(branch); err != nil { - return nil, err - } - if err := t.SetDefaultIndex(); err != nil { - return nil, err - } - - // Add the object to the database - objectHash, err := t.HashObject(strings.NewReader(content)) - if err != nil { - return nil, err - } - - // Add the object to the index - if err := t.AddObjectToIndex("100644", objectHash, treePath); err != nil { - return nil, err - } - return t.DiffIndex() -} diff --git a/modules/uploader/repo.go b/modules/uploader/repo.go deleted file mode 100644 index 33cc160ca9..0000000000 --- a/modules/uploader/repo.go +++ /dev/null @@ -1,359 +0,0 @@ -// Copyright 2019 The Gitea Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package uploader - -import ( - "bytes" - "context" - "fmt" - "io" - "os" - "os/exec" - "path" - "strings" - "time" - - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/process" - "code.gitea.io/gitea/modules/setting" - - "github.com/Unknwon/com" -) - -// TemporaryUploadRepository is a type to wrap our upload repositories -type TemporaryUploadRepository struct { - repo *models.Repository - basePath string -} - -// NewTemporaryUploadRepository creates a new temporary upload repository -func NewTemporaryUploadRepository(repo *models.Repository) (*TemporaryUploadRepository, error) { - timeStr := com.ToStr(time.Now().Nanosecond()) // SHOULD USE SOMETHING UNIQUE - basePath := path.Join(models.LocalCopyPath(), "upload-"+timeStr+".git") - if err := os.MkdirAll(path.Dir(basePath), os.ModePerm); err != nil { - return nil, fmt.Errorf("Failed to create dir %s: %v", basePath, err) - } - t := &TemporaryUploadRepository{repo: repo, basePath: basePath} - return t, nil -} - -// Close the repository cleaning up all files -func (t *TemporaryUploadRepository) Close() { - if _, err := os.Stat(t.basePath); !os.IsNotExist(err) { - os.RemoveAll(t.basePath) - } -} - -// Clone the base repository to our path and set branch as the HEAD -func (t *TemporaryUploadRepository) Clone(branch string) error { - if _, stderr, err := process.GetManager().ExecTimeout(5*time.Minute, - fmt.Sprintf("Clone (git clone -s --bare): %s", t.basePath), - "git", "clone", "-s", "--bare", "-b", branch, t.repo.RepoPath(), t.basePath); err != nil { - return fmt.Errorf("Clone: %v %s", err, stderr) - } - return nil -} - -// SetDefaultIndex sets the git index to our HEAD -func (t *TemporaryUploadRepository) SetDefaultIndex() error { - if _, stderr, err := process.GetManager().ExecDir(5*time.Minute, - t.basePath, - fmt.Sprintf("SetDefaultIndex (git read-tree HEAD): %s", t.basePath), - "git", "read-tree", "HEAD"); err != nil { - return fmt.Errorf("SetDefaultIndex: %v %s", err, stderr) - } - return nil -} - -// LsFiles checks if the given filename arguments are in the index -func (t *TemporaryUploadRepository) LsFiles(filenames ...string) ([]string, error) { - stdOut := new(bytes.Buffer) - stdErr := new(bytes.Buffer) - - timeout := 5 * time.Minute - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - cmdArgs := []string{"ls-files", "-z", "--"} - for _, arg := range filenames { - if arg != "" { - cmdArgs = append(cmdArgs, arg) - } - } - - cmd := exec.CommandContext(ctx, "git", cmdArgs...) - desc := fmt.Sprintf("lsFiles: (git ls-files) %v", cmdArgs) - cmd.Dir = t.basePath - cmd.Stdout = stdOut - cmd.Stderr = stdErr - - if err := cmd.Start(); err != nil { - return nil, fmt.Errorf("exec(%s) failed: %v(%v)", desc, err, ctx.Err()) - } - - pid := process.GetManager().Add(desc, cmd) - err := cmd.Wait() - process.GetManager().Remove(pid) - - if err != nil { - err = fmt.Errorf("exec(%d:%s) failed: %v(%v) stdout: %v stderr: %v", pid, desc, err, ctx.Err(), stdOut, stdErr) - return nil, err - } - - filelist := make([]string, len(filenames)) - for _, line := range bytes.Split(stdOut.Bytes(), []byte{'\000'}) { - filelist = append(filelist, string(line)) - } - - return filelist, err -} - -// RemoveFilesFromIndex removes the given files from the index -func (t *TemporaryUploadRepository) RemoveFilesFromIndex(filenames ...string) error { - stdOut := new(bytes.Buffer) - stdErr := new(bytes.Buffer) - stdIn := new(bytes.Buffer) - for _, file := range filenames { - if file != "" { - stdIn.WriteString("0 0000000000000000000000000000000000000000\t") - stdIn.WriteString(file) - stdIn.WriteByte('\000') - } - } - - timeout := 5 * time.Minute - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - cmdArgs := []string{"update-index", "--remove", "-z", "--index-info"} - cmd := exec.CommandContext(ctx, "git", cmdArgs...) - desc := fmt.Sprintf("removeFilesFromIndex: (git update-index) %v", filenames) - cmd.Dir = t.basePath - cmd.Stdout = stdOut - cmd.Stderr = stdErr - cmd.Stdin = bytes.NewReader(stdIn.Bytes()) - - if err := cmd.Start(); err != nil { - return fmt.Errorf("exec(%s) failed: %v(%v)", desc, err, ctx.Err()) - } - - pid := process.GetManager().Add(desc, cmd) - err := cmd.Wait() - process.GetManager().Remove(pid) - - if err != nil { - err = fmt.Errorf("exec(%d:%s) failed: %v(%v) stdout: %v stderr: %v", pid, desc, err, ctx.Err(), stdOut, stdErr) - } - - return err -} - -// HashObject writes the provided content to the object db and returns its hash -func (t *TemporaryUploadRepository) HashObject(content io.Reader) (string, error) { - timeout := 5 * time.Minute - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - hashCmd := exec.CommandContext(ctx, "git", "hash-object", "-w", "--stdin") - hashCmd.Dir = t.basePath - hashCmd.Stdin = content - stdOutBuffer := new(bytes.Buffer) - stdErrBuffer := new(bytes.Buffer) - hashCmd.Stdout = stdOutBuffer - hashCmd.Stderr = stdErrBuffer - desc := fmt.Sprintf("hashObject: (git hash-object)") - if err := hashCmd.Start(); err != nil { - return "", fmt.Errorf("git hash-object: %s", err) - } - - pid := process.GetManager().Add(desc, hashCmd) - err := hashCmd.Wait() - process.GetManager().Remove(pid) - - if err != nil { - err = fmt.Errorf("exec(%d:%s) failed: %v(%v) stdout: %v stderr: %v", pid, desc, err, ctx.Err(), stdOutBuffer, stdErrBuffer) - return "", err - } - - return strings.TrimSpace(stdOutBuffer.String()), nil -} - -// AddObjectToIndex adds the provided object hash to the index with the provided mode and path -func (t *TemporaryUploadRepository) AddObjectToIndex(mode, objectHash, objectPath string) error { - if _, stderr, err := process.GetManager().ExecDir(5*time.Minute, - t.basePath, - fmt.Sprintf("addObjectToIndex (git update-index): %s", t.basePath), - "git", "update-index", "--add", "--replace", "--cacheinfo", mode, objectHash, objectPath); err != nil { - return fmt.Errorf("git update-index: %s", stderr) - } - return nil -} - -// WriteTree writes the current index as a tree to the object db and returns its hash -func (t *TemporaryUploadRepository) WriteTree() (string, error) { - treeHash, stderr, err := process.GetManager().ExecDir(5*time.Minute, - t.basePath, - fmt.Sprintf("WriteTree (git write-tree): %s", t.basePath), - "git", "write-tree") - if err != nil { - return "", fmt.Errorf("git write-tree: %s", stderr) - } - return strings.TrimSpace(treeHash), nil - -} - -// CommitTree creates a commit from a given tree for the user with provided message -func (t *TemporaryUploadRepository) CommitTree(doer *models.User, treeHash string, message string) (string, error) { - commitTimeStr := time.Now().Format(time.UnixDate) - sig := doer.NewGitSig() - - // FIXME: Should we add SSH_ORIGINAL_COMMAND to this - // Because this may call hooks we should pass in the environment - env := append(os.Environ(), - "GIT_AUTHOR_NAME="+sig.Name, - "GIT_AUTHOR_EMAIL="+sig.Email, - "GIT_AUTHOR_DATE="+commitTimeStr, - "GIT_COMMITTER_NAME="+sig.Name, - "GIT_COMMITTER_EMAIL="+sig.Email, - "GIT_COMMITTER_DATE="+commitTimeStr, - ) - commitHash, stderr, err := process.GetManager().ExecDirEnv(5*time.Minute, - t.basePath, - fmt.Sprintf("commitTree (git commit-tree): %s", t.basePath), - env, - "git", "commit-tree", treeHash, "-p", "HEAD", "-m", message) - if err != nil { - return "", fmt.Errorf("git commit-tree: %s", stderr) - } - return strings.TrimSpace(commitHash), nil -} - -// Push the provided commitHash to the repository branch by the provided user -func (t *TemporaryUploadRepository) Push(doer *models.User, commitHash string, branch string) error { - isWiki := "false" - if strings.HasSuffix(t.repo.Name, ".wiki") { - isWiki = "true" - } - - sig := doer.NewGitSig() - - // FIXME: Should we add SSH_ORIGINAL_COMMAND to this - // Because calls hooks we need to pass in the environment - env := append(os.Environ(), - "GIT_AUTHOR_NAME="+sig.Name, - "GIT_AUTHOR_EMAIL="+sig.Email, - "GIT_COMMITTER_NAME="+sig.Name, - "GIT_COMMITTER_EMAIL="+sig.Email, - models.EnvRepoName+"="+t.repo.Name, - models.EnvRepoUsername+"="+t.repo.OwnerName, - models.EnvRepoIsWiki+"="+isWiki, - models.EnvPusherName+"="+doer.Name, - models.EnvPusherID+"="+fmt.Sprintf("%d", doer.ID), - models.ProtectedBranchRepoID+"="+fmt.Sprintf("%d", t.repo.ID), - ) - - if _, stderr, err := process.GetManager().ExecDirEnv(5*time.Minute, - t.basePath, - fmt.Sprintf("actuallyPush (git push): %s", t.basePath), - env, - "git", "push", t.repo.RepoPath(), strings.TrimSpace(commitHash)+":refs/heads/"+strings.TrimSpace(branch)); err != nil { - return fmt.Errorf("git push: %s", stderr) - } - return nil -} - -// DiffIndex returns a Diff of the current index to the head -func (t *TemporaryUploadRepository) DiffIndex() (diff *models.Diff, err error) { - timeout := 5 * time.Minute - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - stdErr := new(bytes.Buffer) - - cmd := exec.CommandContext(ctx, "git", "diff-index", "--cached", "-p", "HEAD") - cmd.Dir = t.basePath - cmd.Stderr = stdErr - - stdout, err := cmd.StdoutPipe() - if err != nil { - return nil, fmt.Errorf("StdoutPipe: %v stderr %s", err, stdErr.String()) - } - - if err = cmd.Start(); err != nil { - return nil, fmt.Errorf("Start: %v stderr %s", err, stdErr.String()) - } - - pid := process.GetManager().Add(fmt.Sprintf("diffIndex [repo_path: %s]", t.repo.RepoPath()), cmd) - defer process.GetManager().Remove(pid) - - diff, err = models.ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdout) - if err != nil { - return nil, fmt.Errorf("ParsePatch: %v", err) - } - - if err = cmd.Wait(); err != nil { - return nil, fmt.Errorf("Wait: %v", err) - } - - return diff, nil -} - -// CheckAttribute checks the given attribute of the provided files -func (t *TemporaryUploadRepository) CheckAttribute(attribute string, args ...string) (map[string]map[string]string, error) { - stdOut := new(bytes.Buffer) - stdErr := new(bytes.Buffer) - - timeout := 5 * time.Minute - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - cmdArgs := []string{"check-attr", "-z", attribute, "--cached", "--"} - for _, arg := range args { - if arg != "" { - cmdArgs = append(cmdArgs, arg) - } - } - - cmd := exec.CommandContext(ctx, "git", cmdArgs...) - desc := fmt.Sprintf("checkAttr: (git check-attr) %s %v", attribute, cmdArgs) - cmd.Dir = t.basePath - cmd.Stdout = stdOut - cmd.Stderr = stdErr - - if err := cmd.Start(); err != nil { - return nil, fmt.Errorf("exec(%s) failed: %v(%v)", desc, err, ctx.Err()) - } - - pid := process.GetManager().Add(desc, cmd) - err := cmd.Wait() - process.GetManager().Remove(pid) - - if err != nil { - err = fmt.Errorf("exec(%d:%s) failed: %v(%v) stdout: %v stderr: %v", pid, desc, err, ctx.Err(), stdOut, stdErr) - return nil, err - } - - fields := bytes.Split(stdOut.Bytes(), []byte{'\000'}) - - if len(fields)%3 != 1 { - return nil, fmt.Errorf("Wrong number of fields in return from check-attr") - } - - var name2attribute2info = make(map[string]map[string]string) - - for i := 0; i < (len(fields) / 3); i++ { - filename := string(fields[3*i]) - attribute := string(fields[3*i+1]) - info := string(fields[3*i+2]) - attribute2info := name2attribute2info[filename] - if attribute2info == nil { - attribute2info = make(map[string]string) - } - attribute2info[attribute] = info - name2attribute2info[filename] = attribute2info - } - - return name2attribute2info, err -} diff --git a/modules/uploader/update.go b/modules/uploader/update.go deleted file mode 100644 index bc543c7ffa..0000000000 --- a/modules/uploader/update.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2019 The Gitea Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package uploader - -import ( - "fmt" - "strings" - - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/lfs" - "code.gitea.io/gitea/modules/setting" -) - -// UpdateRepoFileOptions holds the repository file update options -type UpdateRepoFileOptions struct { - LastCommitID string - OldBranch string - NewBranch string - OldTreeName string - NewTreeName string - Message string - Content string - IsNewFile bool -} - -// UpdateRepoFile adds or updates a file in the given repository -func UpdateRepoFile(repo *models.Repository, doer *models.User, opts *UpdateRepoFileOptions) error { - t, err := NewTemporaryUploadRepository(repo) - defer t.Close() - if err != nil { - return err - } - if err := t.Clone(opts.OldBranch); err != nil { - return err - } - if err := t.SetDefaultIndex(); err != nil { - return err - } - - filesInIndex, err := t.LsFiles(opts.NewTreeName, opts.OldTreeName) - if err != nil { - return fmt.Errorf("UpdateRepoFile: %v", err) - } - - if opts.IsNewFile { - for _, file := range filesInIndex { - if file == opts.NewTreeName { - return models.ErrRepoFileAlreadyExist{FileName: opts.NewTreeName} - } - } - } - - //var stdout string - if opts.OldTreeName != opts.NewTreeName && len(filesInIndex) > 0 { - for _, file := range filesInIndex { - if file == opts.OldTreeName { - if err := t.RemoveFilesFromIndex(opts.OldTreeName); err != nil { - return err - } - } - } - - } - - // Check there is no way this can return multiple infos - filename2attribute2info, err := t.CheckAttribute("filter", opts.NewTreeName) - if err != nil { - return err - } - - content := opts.Content - var lfsMetaObject *models.LFSMetaObject - - if filename2attribute2info[opts.NewTreeName] != nil && filename2attribute2info[opts.NewTreeName]["filter"] == "lfs" { - // OK so we are supposed to LFS this data! - oid, err := models.GenerateLFSOid(strings.NewReader(opts.Content)) - if err != nil { - return err - } - lfsMetaObject = &models.LFSMetaObject{Oid: oid, Size: int64(len(opts.Content)), RepositoryID: repo.ID} - content = lfsMetaObject.Pointer() - } - - // Add the object to the database - objectHash, err := t.HashObject(strings.NewReader(content)) - if err != nil { - return err - } - - // Add the object to the index - if err := t.AddObjectToIndex("100644", objectHash, opts.NewTreeName); err != nil { - return err - } - - // Now write the tree - treeHash, err := t.WriteTree() - if err != nil { - return err - } - - // Now commit the tree - commitHash, err := t.CommitTree(doer, treeHash, opts.Message) - if err != nil { - return err - } - - if lfsMetaObject != nil { - // We have an LFS object - create it - lfsMetaObject, err = models.NewLFSMetaObject(lfsMetaObject) - if err != nil { - return err - } - contentStore := &lfs.ContentStore{BasePath: setting.LFS.ContentPath} - if !contentStore.Exists(lfsMetaObject) { - if err := contentStore.Put(lfsMetaObject, strings.NewReader(opts.Content)); err != nil { - if err2 := repo.RemoveLFSMetaObjectByOid(lfsMetaObject.Oid); err2 != nil { - return fmt.Errorf("Error whilst removing failed inserted LFS object %s: %v (Prev Error: %v)", lfsMetaObject.Oid, err2, err) - } - return err - } - } - } - - // Then push this tree to NewBranch - if err := t.Push(doer, commitHash, opts.NewBranch); err != nil { - return err - } - - // Simulate push event. - oldCommitID := opts.LastCommitID - if opts.NewBranch != opts.OldBranch { - oldCommitID = git.EmptySHA - } - - if err = repo.GetOwner(); err != nil { - return fmt.Errorf("GetOwner: %v", err) - } - err = models.PushUpdate( - opts.NewBranch, - models.PushUpdateOptions{ - PusherID: doer.ID, - PusherName: doer.Name, - RepoUserName: repo.Owner.Name, - RepoName: repo.Name, - RefFullName: git.BranchPrefix + opts.NewBranch, - OldCommitID: oldCommitID, - NewCommitID: commitHash, - }, - ) - if err != nil { - return fmt.Errorf("PushUpdate: %v", err) - } - models.UpdateRepoIndexer(repo) - - return nil -} diff --git a/modules/uploader/upload.go b/modules/uploader/upload.go deleted file mode 100644 index 81d7c3ba20..0000000000 --- a/modules/uploader/upload.go +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright 2019 The Gitea Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package uploader - -import ( - "fmt" - "os" - "path" - "strings" - - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/lfs" - "code.gitea.io/gitea/modules/setting" -) - -// UploadRepoFileOptions contains the uploaded repository file options -type UploadRepoFileOptions struct { - LastCommitID string - OldBranch string - NewBranch string - TreePath string - Message string - Files []string // In UUID format. -} - -type uploadInfo struct { - upload *models.Upload - lfsMetaObject *models.LFSMetaObject -} - -func cleanUpAfterFailure(infos *[]uploadInfo, t *TemporaryUploadRepository, original error) error { - for _, info := range *infos { - if info.lfsMetaObject == nil { - continue - } - if !info.lfsMetaObject.Existing { - if err := t.repo.RemoveLFSMetaObjectByOid(info.lfsMetaObject.Oid); err != nil { - original = fmt.Errorf("%v, %v", original, err) - } - } - } - return original -} - -// UploadRepoFiles uploads files to the given repository -func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRepoFileOptions) error { - if len(opts.Files) == 0 { - return nil - } - - uploads, err := models.GetUploadsByUUIDs(opts.Files) - if err != nil { - return fmt.Errorf("GetUploadsByUUIDs [uuids: %v]: %v", opts.Files, err) - } - - t, err := NewTemporaryUploadRepository(repo) - defer t.Close() - if err != nil { - return err - } - if err := t.Clone(opts.OldBranch); err != nil { - return err - } - if err := t.SetDefaultIndex(); err != nil { - return err - } - - names := make([]string, len(uploads)) - infos := make([]uploadInfo, len(uploads)) - for i, upload := range uploads { - names[i] = upload.Name - infos[i] = uploadInfo{upload: upload} - } - - filename2attribute2info, err := t.CheckAttribute("filter", names...) - if err != nil { - return err - } - - // Copy uploaded files into repository. - for i, uploadInfo := range infos { - file, err := os.Open(uploadInfo.upload.LocalPath()) - if err != nil { - return err - } - defer file.Close() - - var objectHash string - if filename2attribute2info[uploadInfo.upload.Name] != nil && filename2attribute2info[uploadInfo.upload.Name]["filter"] == "lfs" { - // Handle LFS - // FIXME: Inefficient! this should probably happen in models.Upload - oid, err := models.GenerateLFSOid(file) - if err != nil { - return err - } - fileInfo, err := file.Stat() - if err != nil { - return err - } - - uploadInfo.lfsMetaObject = &models.LFSMetaObject{Oid: oid, Size: fileInfo.Size(), RepositoryID: t.repo.ID} - - if objectHash, err = t.HashObject(strings.NewReader(uploadInfo.lfsMetaObject.Pointer())); err != nil { - return err - } - infos[i] = uploadInfo - - } else { - if objectHash, err = t.HashObject(file); err != nil { - return err - } - } - - // Add the object to the index - if err := t.AddObjectToIndex("100644", objectHash, path.Join(opts.TreePath, uploadInfo.upload.Name)); err != nil { - return err - - } - } - - // Now write the tree - treeHash, err := t.WriteTree() - if err != nil { - return err - } - - // Now commit the tree - commitHash, err := t.CommitTree(doer, treeHash, opts.Message) - if err != nil { - return err - } - - // Now deal with LFS objects - for _, uploadInfo := range infos { - if uploadInfo.lfsMetaObject == nil { - continue - } - uploadInfo.lfsMetaObject, err = models.NewLFSMetaObject(uploadInfo.lfsMetaObject) - if err != nil { - // OK Now we need to cleanup - return cleanUpAfterFailure(&infos, t, err) - } - // Don't move the files yet - we need to ensure that - // everything can be inserted first - } - - // OK now we can insert the data into the store - there's no way to clean up the store - // once it's in there, it's in there. - contentStore := &lfs.ContentStore{BasePath: setting.LFS.ContentPath} - for _, uploadInfo := range infos { - if uploadInfo.lfsMetaObject == nil { - continue - } - if !contentStore.Exists(uploadInfo.lfsMetaObject) { - file, err := os.Open(uploadInfo.upload.LocalPath()) - if err != nil { - return cleanUpAfterFailure(&infos, t, err) - } - defer file.Close() - // FIXME: Put regenerates the hash and copies the file over. - // I guess this strictly ensures the soundness of the store but this is inefficient. - if err := contentStore.Put(uploadInfo.lfsMetaObject, file); err != nil { - // OK Now we need to cleanup - // Can't clean up the store, once uploaded there they're there. - return cleanUpAfterFailure(&infos, t, err) - } - } - } - - // Then push this tree to NewBranch - if err := t.Push(doer, commitHash, opts.NewBranch); err != nil { - return err - } - - // Simulate push event. - oldCommitID := opts.LastCommitID - if opts.NewBranch != opts.OldBranch { - oldCommitID = git.EmptySHA - } - - if err = repo.GetOwner(); err != nil { - return fmt.Errorf("GetOwner: %v", err) - } - err = models.PushUpdate( - opts.NewBranch, - models.PushUpdateOptions{ - PusherID: doer.ID, - PusherName: doer.Name, - RepoUserName: repo.Owner.Name, - RepoName: repo.Name, - RefFullName: git.BranchPrefix + opts.NewBranch, - OldCommitID: oldCommitID, - NewCommitID: commitHash, - }, - ) - if err != nil { - return fmt.Errorf("PushUpdate: %v", err) - } - // FIXME: Should we models.UpdateRepoIndexer(repo) here? - - return models.DeleteUploads(uploads...) -} |