Feature for editing, creating, uploading and deleting files

This commit is contained in:
Richard Mahn
2016-07-15 18:35:42 -06:00
parent 3d93532c87
commit fa1b752be2
19 changed files with 2848 additions and 1057 deletions

View File

@@ -404,6 +404,19 @@ func (err ErrReleaseNotExist) Error() string {
return fmt.Sprintf("Release tag does not exist [id: %d, tag_name: %s]", err.ID, err.TagName)
}
type ErrRepoFileAlreadyExist struct {
FileName string
}
func IsErrRepoFileAlreadyExist(err error) bool {
_, ok := err.(ErrRepoFileAlreadyExist)
return ok
}
func (err ErrRepoFileAlreadyExist) Error() string {
return fmt.Sprintf("Repo file already exists [file name: %s]", err.FileName)
}
// __________ .__
// \______ \____________ ____ ____ | |__
// | | _/\_ __ \__ \ / \_/ ___\| | \
@@ -614,3 +627,27 @@ func IsErrTeamAlreadyExist(err error) bool {
func (err ErrTeamAlreadyExist) Error() string {
return fmt.Sprintf("team already exists [org_id: %d, name: %s]", err.OrgID, err.Name)
}
// ____ ___ .__ .___
// | | \______ | | _________ __| _/
// | | /\____ \| | / _ \__ \ / __ |
// | | / | |_> > |_( <_> ) __ \_/ /_/ |
// |______/ | __/|____/\____(____ /\____ |
// |__| \/ \/
//
type ErrUploadNotExist struct {
ID int64
UUID string
UserID int64
RepoID int64
}
func IsErrUploadNotExist(err error) bool {
_, ok := err.(ErrAttachmentNotExist)
return ok
}
func (err ErrUploadNotExist) Error() string {
return fmt.Sprintf("attachment does not exist [id: %d, uuid: %s]", err.ID, err.UUID)
}

View File

@@ -287,7 +287,7 @@ func (pr *PullRequest) testPatch() (err error) {
log.Trace("PullRequest[%d].testPatch (patchPath): %s", pr.ID, patchPath)
if err := pr.BaseRepo.UpdateLocalCopy(); err != nil {
if err := pr.BaseRepo.UpdateLocalCopy(pr.BaseRepo.DefaultBranch); err != nil {
return fmt.Errorf("UpdateLocalCopy: %v", err)
}

View File

@@ -29,12 +29,15 @@ import (
git "github.com/gogits/git-module"
api "github.com/gogits/go-gogs-client"
gouuid "github.com/satori/go.uuid"
"github.com/gogits/gogs/modules/bindata"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/markdown"
"github.com/gogits/gogs/modules/process"
"github.com/gogits/gogs/modules/setting"
"mime/multipart"
"io"
)
const (
@@ -406,16 +409,25 @@ func (repo *Repository) LocalCopyPath() string {
return path.Join(setting.AppDataPath, "tmp/local", com.ToStr(repo.ID))
}
func updateLocalCopy(repoPath, localPath string) error {
func updateLocalCopy(repoPath, localPath, branch string) error {
if !com.IsExist(localPath) {
if err := git.Clone(repoPath, localPath, git.CloneRepoOptions{
Timeout: time.Duration(setting.Git.Timeout.Clone) * time.Second,
Branch: branch,
}); err != nil {
return fmt.Errorf("Clone: %v", err)
}
} else {
if err := git.Checkout(localPath, git.CheckoutOptions{
Branch: branch,
Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second,
}); err != nil {
return fmt.Errorf("Checkout: %v", err)
}
if err := git.Pull(localPath, git.PullRemoteOptions{
All: true,
All: false,
Remote: "origin",
Branch: branch,
Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second,
}); err != nil {
return fmt.Errorf("Pull: %v", err)
@@ -425,8 +437,8 @@ func updateLocalCopy(repoPath, localPath string) error {
}
// UpdateLocalCopy makes sure the local copy of repository is up-to-date.
func (repo *Repository) UpdateLocalCopy() error {
return updateLocalCopy(repo.RepoPath(), repo.LocalCopyPath())
func (repo *Repository) UpdateLocalCopy(branch string) error {
return updateLocalCopy(repo.RepoPath(), repo.LocalCopyPath(), branch)
}
// PatchPath returns corresponding patch file path of repository by given issue ID.
@@ -2099,3 +2111,402 @@ func (repo *Repository) GetForks() ([]*Repository, error) {
forks := make([]*Repository, 0, repo.NumForks)
return forks, x.Find(&forks, &Repository{ForkID: repo.ID})
}
// ___________ .___.__ __ ___________.__.__
// \_ _____/ __| _/|__|/ |_ \_ _____/|__| | ____
// | __)_ / __ | | \ __\ | __) | | | _/ __ \
// | \/ /_/ | | || | | \ | | |_\ ___/
// /_______ /\____ | |__||__| \___ / |__|____/\___ >
// \/ \/ \/ \/
var repoWorkingPool = &workingPool{
pool: make(map[string]*sync.Mutex),
count: make(map[string]int),
}
func (repo *Repository) LocalRepoPath() string {
return path.Join(setting.AppDataPath, "tmp/local-repo", com.ToStr(repo.ID))
}
// UpdateLocalRepo makes sure the local copy of repository is up-to-date.
func (repo *Repository) UpdateLocalRepo(branchName string) error {
return updateLocalCopy(repo.RepoPath(), repo.LocalRepoPath(), branchName)
}
// DiscardLocalRepoChanges makes sure the local copy of repository is the same as the source
func (repo *Repository) DiscardLocalRepoChanges(branchName string) error {
return discardLocalRepoChanges(repo.LocalRepoPath(), branchName)
}
// discardLocalRepoChanges discards local commits make sure
// it is even to remote branch when local copy exists.
func discardLocalRepoChanges(localPath string, branch string) error {
if !com.IsExist(localPath) {
return nil
}
// No need to check if nothing in the repository.
if !git.IsBranchExist(localPath, branch) {
return nil
}
if err := git.ResetHEAD(localPath, true, "origin/"+branch); err != nil {
return fmt.Errorf("ResetHEAD: %v", err)
}
return nil
}
// CheckoutNewBranch checks out a new branch from the given branch name
func (repo *Repository) CheckoutNewBranch(oldBranchName, newBranchName string) error {
return checkoutNewBranch(repo.RepoPath(), repo.LocalRepoPath(), oldBranchName, newBranchName)
}
func checkoutNewBranch(repoPath, localPath, oldBranch, newBranch string) error {
if !com.IsExist(localPath) {
if error := updateLocalCopy(repoPath, localPath, oldBranch); error != nil {
return error
}
}
if err := git.Checkout(localPath, git.CheckoutOptions {
Branch: newBranch,
OldBranch: oldBranch,
Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second,
}); err != nil {
return fmt.Errorf("Checkout New Branch: %v", err)
}
return nil
}
// updateRepoFile adds new file to repository.
func (repo *Repository) UpdateRepoFile(doer *User, oldBranchName, branchName, oldTreeName, treeName, content, message string, isNewFile bool) (err error) {
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
if err = repo.DiscardLocalRepoChanges(oldBranchName); err != nil {
return fmt.Errorf("discardLocalRepoChanges: %s - %v", oldBranchName, err)
} else if err = repo.UpdateLocalRepo(oldBranchName); err != nil {
return fmt.Errorf("UpdateLocalRepo: %s - %v", oldBranchName, err)
}
if( oldBranchName != branchName ){
if err := repo.CheckoutNewBranch(oldBranchName, branchName); err != nil {
return fmt.Errorf("CheckoutNewBranch: %s - %s: %v", oldBranchName, branchName, err)
}
}
localPath := repo.LocalRepoPath()
filePath := path.Join(localPath, treeName)
if len(message) == 0 {
if isNewFile {
message = "Add '" + treeName + "'"
} else {
message = "Update '" + treeName + "'"
}
}
if ! com.IsExist(filepath.Dir(filePath)) {
os.MkdirAll(filepath.Dir(filePath), os.ModePerm)
}
// If new file, make sure it doesn't exist; if old file, move if file name change
if isNewFile {
if com.IsExist(filePath) {
return ErrRepoFileAlreadyExist{filePath}
}
} else if oldTreeName!="" && treeName!="" && treeName != oldTreeName {
if err = git.MoveFile(localPath, oldTreeName, treeName); err != nil {
return fmt.Errorf("MoveFile: %v", err)
}
}
if err = ioutil.WriteFile(filePath, []byte(content), 0666); err != nil {
return fmt.Errorf("WriteFile: %v", err)
}
if err = git.AddChanges(localPath, true); err != nil {
return fmt.Errorf("AddChanges: %v", err)
} else if err = git.CommitChanges(localPath, message, doer.NewGitSig()); err != nil {
return fmt.Errorf("CommitChanges: %v", err)
} else if err = git.Push(localPath, "origin", branchName); err != nil {
return fmt.Errorf("Push: %v", err)
}
return nil
}
func (repo *Repository) GetPreviewDiff(repoPath, branchName, treeName, text string, maxlines, maxchars, maxfiles int) (diff *Diff, err error) {
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
if err = repo.DiscardLocalRepoChanges(branchName); err != nil {
return nil, fmt.Errorf("discardLocalRepoChanges: %s - %v", branchName, err)
} else if err = repo.UpdateLocalRepo(branchName); err != nil {
return nil, fmt.Errorf("UpdateLocalRepo: %s - %v", branchName, err)
}
localPath := repo.LocalRepoPath()
filePath := path.Join(localPath, treeName)
if ! com.IsExist(filepath.Dir(filePath)) {
os.MkdirAll(filepath.Dir(filePath), os.ModePerm)
}
if err = ioutil.WriteFile(filePath, []byte(text), 0666); err != nil {
return nil, fmt.Errorf("WriteFile: %v", err)
}
var cmd *exec.Cmd
cmd = exec.Command("git", "diff", treeName)
cmd.Dir = localPath
cmd.Stderr = os.Stderr
stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, fmt.Errorf("StdoutPipe: %v", err)
}
if err = cmd.Start(); err != nil {
return nil, fmt.Errorf("Start: %v", err)
}
pid := process.Add(fmt.Sprintf("GetDiffRange (%s)", repoPath), cmd)
defer process.Remove(pid)
diff, err = ParsePatch(maxlines, maxchars, maxfiles, 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
}
// ________ .__ __ ___________.__.__
// \______ \ ____ | | _____/ |_ ____ \_ _____/|__| | ____
// | | \_/ __ \| | _/ __ \ __\/ __ \ | __) | | | _/ __ \
// | ` \ ___/| |_\ ___/| | \ ___/ | \ | | |_\ ___/
// /_______ /\___ >____/\___ >__| \___ > \___ / |__|____/\___ >
// \/ \/ \/ \/ \/ \/
//
func (repo *Repository) DeleteRepoFile(doer *User, branch, treeName, message string) (err error) {
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
localPath := repo.LocalRepoPath()
if err = discardLocalRepoChanges(localPath, branch); err != nil {
return fmt.Errorf("discardLocalRepoChanges: %v", err)
} else if err = repo.UpdateLocalRepo(branch); err != nil {
return fmt.Errorf("UpdateLocalRepo: %v", err)
}
filePath := path.Join(localPath, treeName)
os.Remove(filePath)
if len(message) == 0 {
message = "Delete file '" + treeName + "'"
}
if err = git.AddChanges(localPath, true); err != nil {
return fmt.Errorf("AddChanges: %v", err)
} else if err = git.CommitChanges(localPath, message, doer.NewGitSig()); err != nil {
return fmt.Errorf("CommitChanges: %v", err)
} else if err = git.Push(localPath, "origin", branch); err != nil {
return fmt.Errorf("Push: %v", err)
}
return nil
}
// ____ ___ .__ .___ ___________.___.__
// | | \______ | | _________ __| _/ \_ _____/| | | ____ ______
// | | /\____ \| | / _ \__ \ / __ | | __) | | | _/ __ \ / ___/
// | | / | |_> > |_( <_> ) __ \_/ /_/ | | \ | | |_\ ___/ \___ \
// |______/ | __/|____/\____(____ /\____ | \___ / |___|____/\___ >____ >
// |__| \/ \/ \/ \/ \/
//
// uploadRepoFiles uploads new files to repository.
func (repo *Repository) UploadRepoFiles(doer *User, oldBranchName, branchName, treeName, message string, uuids []string) (err error) {
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
localPath := repo.LocalRepoPath()
if err = discardLocalRepoChanges(localPath, oldBranchName); err != nil {
return fmt.Errorf("discardLocalRepoChanges: %v", err)
} else if err = repo.UpdateLocalRepo(oldBranchName); err != nil {
return fmt.Errorf("UpdateLocalRepo: %v", err)
}
if( oldBranchName != branchName ){
repo.CheckoutNewBranch(oldBranchName, branchName)
}
dirPath := path.Join(localPath, treeName)
if ! com.IsExist(dirPath) {
os.MkdirAll(dirPath, os.ModePerm)
}
// Copy uploaded files into repository.
for _, uuid := range uuids {
upload, err := getUpload(uuid, doer.Id, repo.ID)
if err != nil {
if IsErrUploadNotExist(err) {
continue
}
return fmt.Errorf("getUpload[%s]: %v", uuid, err)
}
uuidPath := upload.LocalPath()
filePath := dirPath+"/"+upload.Name
if err := os.Rename(uuidPath, filePath); err != nil {
DeleteUpload(upload, true);
return fmt.Errorf("Rename[%s -> %s]: %v", uuidPath, filePath, err)
}
DeleteUpload(upload, false); // false because we have moved the file
}
if len(message) == 0 {
message = "Add files to '" + treeName + "'"
}
if err = git.AddChanges(localPath, true); err != nil {
return fmt.Errorf("AddChanges: %v", err)
} else if err = git.CommitChanges(localPath, message, doer.NewGitSig()); err != nil {
return fmt.Errorf("CommitChanges: %v", err)
} else if err = git.Push(localPath, "origin", branchName); err != nil {
return fmt.Errorf("Push: %v", err)
}
return nil
}
// Upload represent a uploaded file to a repo to be deleted when moved
type Upload struct {
ID int64 `xorm:"pk autoincr"`
UUID string `xorm:"uuid UNIQUE"`
UID int64 `xorm:"INDEX"`
RepoID int64 `xorm:"INDEX"`
Name string
Created time.Time `xorm:"-"`
CreatedUnix int64
}
func (u *Upload) BeforeInsert() {
u.CreatedUnix = time.Now().UTC().Unix()
}
func (u *Upload) AfterSet(colName string, _ xorm.Cell) {
switch colName {
case "created_unix":
u.Created = time.Unix(u.CreatedUnix, 0).Local()
}
}
// UploadLocalPath returns where uploads is stored in local file system based on given UUID.
func UploadLocalPath(uuid string) string {
return path.Join(setting.UploadTempPath, uuid[0:1], uuid[1:2], uuid)
}
// LocalPath returns where uploads are temporarily stored in local file system.
func (upload *Upload) LocalPath() string {
return UploadLocalPath(upload.UUID)
}
// NewUpload creates a new upload object.
func NewUpload(name string, buf []byte, file multipart.File, userId, repoId int64) (_ *Upload, err error) {
up := &Upload{
UUID: gouuid.NewV4().String(),
Name: name,
UID: userId,
RepoID: repoId,
}
if err = os.MkdirAll(path.Dir(up.LocalPath()), os.ModePerm); err != nil {
return nil, fmt.Errorf("MkdirAll: %v", err)
}
fw, err := os.Create(up.LocalPath())
if err != nil {
return nil, fmt.Errorf("Create: %v", err)
}
defer fw.Close()
if _, err = fw.Write(buf); err != nil {
return nil, fmt.Errorf("Write: %v", err)
} else if _, err = io.Copy(fw, file); err != nil {
return nil, fmt.Errorf("Copy: %v", err)
}
sess := x.NewSession()
defer sessionRelease(sess)
if err := sess.Begin(); err != nil {
return nil, err
}
if _, err := sess.Insert(up); err != nil {
return nil, err
}
return up, sess.Commit()
}
// RemoveUpload removes the file by UUID
func RemoveUpload(uuid string, userId, repoId int64) (err error) {
sess := x.NewSession()
defer sessionRelease(sess)
if err := sess.Begin(); err != nil {
return err
}
upload, err := getUpload(uuid, userId, repoId)
if err != nil {
return fmt.Errorf("getUpload[%s]: %v", uuid, err)
}
if err := DeleteUpload(upload, true); err != nil {
return fmt.Errorf("DeleteUpload[%s]: %v", uuid, err)
}
return nil
}
func getUpload(uuid string, userId, repoId int64) (*Upload, error) {
up := &Upload{UUID: uuid, UID: userId, RepoID: repoId}
has, err := x.Get(up)
if err != nil {
return nil, err
} else if !has {
return nil, ErrUploadNotExist{0, uuid, userId, repoId}
}
return up, nil
}
// GetUpload returns Upload by given UUID.
func GetUpload(uuid string, userId, repoId int64) (*Upload, error) {
return getUpload(uuid, userId, repoId)
}
// DeleteUpload deletes the given upload
func DeleteUpload(u *Upload, remove bool) error {
_, err := DeleteUploads([]*Upload{u}, remove)
return err
}
// DeleteUploads deletes the given uploads
func DeleteUploads(uploads []*Upload, remove bool) (int, error) {
for i, u := range uploads {
if remove {
if err := os.Remove(u.LocalPath()); err != nil {
return i, err
}
}
if _, err := x.Delete(u); err != nil {
return i, err
}
}
return len(uploads), nil
}

View File

@@ -115,7 +115,7 @@ func (repo *Repository) LocalWikiPath() string {
// UpdateLocalWiki makes sure the local copy of repository wiki is up-to-date.
func (repo *Repository) UpdateLocalWiki() error {
return updateLocalCopy(repo.WikiPath(), repo.LocalWikiPath())
return updateLocalCopy(repo.WikiPath(), repo.LocalWikiPath(), repo.DefaultBranch)
}
// discardLocalWikiChanges discards local commits make sure