- [OpenShift](https://github.com/tkisme/gogs-openshift) | - [OpenShift](https://github.com/tkisme/gogs-openshift) | ||||
- [Cloudron](https://cloudron.io/appstore.html#io.gogs.cloudronapp) | - [Cloudron](https://cloudron.io/appstore.html#io.gogs.cloudronapp) | ||||
- [Scaleway](https://www.scaleway.com/imagehub/gogs/) | - [Scaleway](https://www.scaleway.com/imagehub/gogs/) | ||||
- [Portal](https://portaldemo.xyz/cloud/) | |||||
## Acknowledgments | ## Acknowledgments | ||||
"github.com/gogits/gogs/modules/setting" | "github.com/gogits/gogs/modules/setting" | ||||
) | ) | ||||
const APP_VER = "0.6.16.1002 Beta" | |||||
const APP_VER = "0.6.16.1004 Beta" | |||||
func init() { | func init() { | ||||
runtime.GOMAXPROCS(runtime.NumCPU()) | runtime.GOMAXPROCS(runtime.NumCPU()) |
Merger *User `xorm:"-"` | Merger *User `xorm:"-"` | ||||
} | } | ||||
// Note: don't try to get Pull because will end up recursive querying. | |||||
func (pr *PullRequest) AfterSet(colName string, _ xorm.Cell) { | func (pr *PullRequest) AfterSet(colName string, _ xorm.Cell) { | ||||
var err error | var err error | ||||
switch colName { | switch colName { | ||||
case "head_repo_id": | case "head_repo_id": | ||||
// FIXME: shouldn't show error if it's known that head repository has been removed. | |||||
pr.HeadRepo, err = GetRepositoryByID(pr.HeadRepoID) | pr.HeadRepo, err = GetRepositoryByID(pr.HeadRepoID) | ||||
if err != nil { | if err != nil { | ||||
log.Error(3, "GetRepositoryByID[%d]: %v", pr.ID, err) | log.Error(3, "GetRepositoryByID[%d]: %v", pr.ID, err) | ||||
if _, stderr, err = process.ExecDir(-1, tmpBasePath, | if _, stderr, err = process.ExecDir(-1, tmpBasePath, | ||||
fmt.Sprintf("PullRequest.Merge(git pull): %s", tmpBasePath), | fmt.Sprintf("PullRequest.Merge(git pull): %s", tmpBasePath), | ||||
"git", "pull", headRepoPath, pr.HeadBarcnh); err != nil { | "git", "pull", headRepoPath, pr.HeadBarcnh); err != nil { | ||||
return fmt.Errorf("git pull: %s", stderr) | |||||
return fmt.Errorf("git pull[%s / %s -> %s]: %s", headRepoPath, pr.HeadBarcnh, tmpBasePath, stderr) | |||||
} | } | ||||
// Push back to upstream. | // Push back to upstream. | ||||
} | } | ||||
// Test apply patch. | // Test apply patch. | ||||
if err = repo.UpdateLocalCopy(); err != nil { | |||||
return fmt.Errorf("UpdateLocalCopy: %v", err) | |||||
} | |||||
repoPath, err := repo.RepoPath() | repoPath, err := repo.RepoPath() | ||||
if err != nil { | if err != nil { | ||||
return fmt.Errorf("RepoPath: %v", err) | return fmt.Errorf("RepoPath: %v", err) | ||||
} | } | ||||
patchPath := path.Join(repoPath, "pulls", com.ToStr(pr.ID)+".patch") | |||||
patchPath := path.Join(repoPath, "pulls", com.ToStr(pull.ID)+".patch") | |||||
os.MkdirAll(path.Dir(patchPath), os.ModePerm) | os.MkdirAll(path.Dir(patchPath), os.ModePerm) | ||||
if err = ioutil.WriteFile(patchPath, patch, 0644); err != nil { | if err = ioutil.WriteFile(patchPath, patch, 0644); err != nil { | ||||
return fmt.Errorf("save patch: %v", err) | return fmt.Errorf("save patch: %v", err) | ||||
} | } | ||||
defer os.Remove(patchPath) | |||||
stdout, stderr, err := process.ExecDir(-1, repoPath, | |||||
pr.CanAutoMerge = true | |||||
_, stderr, err := process.ExecDir(-1, repo.LocalCopyPath(), | |||||
fmt.Sprintf("NewPullRequest(git apply --check): %d", repo.ID), | fmt.Sprintf("NewPullRequest(git apply --check): %d", repo.ID), | ||||
"git", "apply", "--check", "-v", patchPath) | |||||
"git", "apply", "--check", patchPath) | |||||
if err != nil { | if err != nil { | ||||
if strings.Contains(stderr, "fatal:") { | |||||
if strings.Contains(stderr, "patch does not apply") { | |||||
pr.CanAutoMerge = false | |||||
} else { | |||||
return fmt.Errorf("git apply --check: %v - %s", err, stderr) | return fmt.Errorf("git apply --check: %v - %s", err, stderr) | ||||
} | } | ||||
} | } | ||||
pr.CanAutoMerge = !strings.Contains(stdout, "error: patch failed:") | |||||
pr.PullID = pull.ID | pr.PullID = pull.ID | ||||
pr.PullIndex = pull.Index | pr.PullIndex = pull.Index |
return template.HTML(DescPattern.ReplaceAllStringFunc(base.Sanitizer.Sanitize(repo.Description), sanitize)) | return template.HTML(DescPattern.ReplaceAllStringFunc(base.Sanitizer.Sanitize(repo.Description), sanitize)) | ||||
} | } | ||||
func (repo *Repository) LocalCopyPath() string { | |||||
return path.Join(setting.RepoRootPath, "local", com.ToStr(repo.ID)) | |||||
} | |||||
// UpdateLocalCopy makes sure the local copy of repository is up-to-date. | |||||
func (repo *Repository) UpdateLocalCopy() error { | |||||
repoPath, err := repo.RepoPath() | |||||
if err != nil { | |||||
return err | |||||
} | |||||
localPath := repo.LocalCopyPath() | |||||
if !com.IsExist(localPath) { | |||||
_, stderr, err := process.Exec( | |||||
fmt.Sprintf("UpdateLocalCopy(git clone): %s", repoPath), "git", "clone", repoPath, localPath) | |||||
if err != nil { | |||||
return fmt.Errorf("git clone: %v - %s", err, stderr) | |||||
} | |||||
} else { | |||||
_, stderr, err := process.ExecDir(-1, localPath, | |||||
fmt.Sprintf("UpdateLocalCopy(git pull): %s", repoPath), "git", "pull") | |||||
if err != nil { | |||||
return fmt.Errorf("git pull: %v - %s", err, stderr) | |||||
} | |||||
} | |||||
return nil | |||||
} | |||||
func isRepositoryExist(e Engine, u *User, repoName string) (bool, error) { | func isRepositoryExist(e Engine, u *User, repoName string) (bool, error) { | ||||
has, err := e.Get(&Repository{ | has, err := e.Get(&Repository{ | ||||
OwnerID: u.Id, | OwnerID: u.Id, |
} | } | ||||
prInfo.MergeBase = strings.TrimSpace(stdout) | prInfo.MergeBase = strings.TrimSpace(stdout) | ||||
stdout, stderr, err = com.ExecCmdDir(repo.Path, "git", "log", remoteBranch+"..."+headBranch, prettyLogFormat) | |||||
stdout, stderr, err = com.ExecCmdDir(repo.Path, "git", "log", prInfo.MergeBase+"..."+headBranch, prettyLogFormat) | |||||
if err != nil { | if err != nil { | ||||
return nil, fmt.Errorf("list diff logs: %v", concatenateError(err, stderr)) | return nil, fmt.Errorf("list diff logs: %v", concatenateError(err, stderr)) | ||||
} | } | ||||
} | } | ||||
// GetPatch generates and returns patch data between given branches. | // GetPatch generates and returns patch data between given branches. | ||||
func (repo *Repository) GetPatch(basePath, baseBranch, headBranch string) ([]byte, error) { | |||||
// Add a temporary remote. | |||||
tmpRemote := com.ToStr(time.Now().UnixNano()) | |||||
_, stderr, err := com.ExecCmdDirBytes(repo.Path, "git", "remote", "add", "-f", tmpRemote, basePath) | |||||
if err != nil { | |||||
return nil, fmt.Errorf("add base as remote: %v", concatenateError(err, string(stderr))) | |||||
} | |||||
defer func() { | |||||
com.ExecCmdDir(repo.Path, "git", "remote", "remove", tmpRemote) | |||||
}() | |||||
var stdout []byte | |||||
remoteBranch := "remotes/" + tmpRemote + "/" + baseBranch | |||||
stdout, stderr, err = com.ExecCmdDirBytes(repo.Path, "git", "diff", "-p", remoteBranch, headBranch) | |||||
func (repo *Repository) GetPatch(mergeBase, headBranch string) ([]byte, error) { | |||||
stdout, stderr, err := com.ExecCmdDirBytes(repo.Path, "git", "diff", "-p", mergeBase, headBranch) | |||||
if err != nil { | if err != nil { | ||||
return nil, concatenateError(err, string(stderr)) | return nil, concatenateError(err, string(stderr)) | ||||
} | } |
ctx.Data["HeadTarget"] = pull.HeadUserName + "/" + pull.HeadBarcnh | ctx.Data["HeadTarget"] = pull.HeadUserName + "/" + pull.HeadBarcnh | ||||
ctx.Data["BaseTarget"] = ctx.Repo.Owner.Name + "/" + pull.BaseBranch | ctx.Data["BaseTarget"] = ctx.Repo.Owner.Name + "/" + pull.BaseBranch | ||||
headRepoPath, err := pull.HeadRepo.RepoPath() | |||||
if err != nil { | |||||
ctx.Handle(500, "HeadRepo.RepoPath", err) | |||||
return nil | |||||
} | |||||
var ( | |||||
headGitRepo *git.Repository | |||||
err error | |||||
) | |||||
if pull.HeadRepo != nil { | |||||
headRepoPath, err := pull.HeadRepo.RepoPath() | |||||
if err != nil { | |||||
ctx.Handle(500, "HeadRepo.RepoPath", err) | |||||
return nil | |||||
} | |||||
headGitRepo, err := git.OpenRepository(headRepoPath) | |||||
if err != nil { | |||||
ctx.Handle(500, "OpenRepository", err) | |||||
return nil | |||||
headGitRepo, err = git.OpenRepository(headRepoPath) | |||||
if err != nil { | |||||
ctx.Handle(500, "OpenRepository", err) | |||||
return nil | |||||
} | |||||
} | } | ||||
if pull.HeadRepo == nil || !headGitRepo.IsBranchExist(pull.HeadBarcnh) { | if pull.HeadRepo == nil || !headGitRepo.IsBranchExist(pull.HeadBarcnh) { | ||||
return | return | ||||
} | } | ||||
pr, err := models.GetUnmergedPullRequest(headRepo.ID, ctx.Repo.Repository.ID, headBranch, baseBranch) | |||||
if err != nil { | |||||
if !models.IsErrPullRequestNotExist(err) { | |||||
ctx.Handle(500, "GetUnmergedPullRequest", err) | |||||
return | |||||
} | |||||
} else { | |||||
ctx.Data["HasPullRequest"] = true | |||||
ctx.Data["PullRequest"] = pr | |||||
ctx.HTML(200, COMPARE_PULL) | |||||
return | |||||
} | |||||
// pr, err := models.GetUnmergedPullRequest(headRepo.ID, ctx.Repo.Repository.ID, headBranch, baseBranch) | |||||
// if err != nil { | |||||
// if !models.IsErrPullRequestNotExist(err) { | |||||
// ctx.Handle(500, "GetUnmergedPullRequest", err) | |||||
// return | |||||
// } | |||||
// } else { | |||||
// ctx.Data["HasPullRequest"] = true | |||||
// ctx.Data["PullRequest"] = pr | |||||
// ctx.HTML(200, COMPARE_PULL) | |||||
// return | |||||
// } | |||||
nothingToCompare := PrepareCompareDiff(ctx, headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch) | nothingToCompare := PrepareCompareDiff(ctx, headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch) | ||||
if ctx.Written() { | if ctx.Written() { | ||||
return | return | ||||
} | } | ||||
patch, err := headGitRepo.GetPatch(models.RepoPath(repo.Owner.Name, repo.Name), baseBranch, headBranch) | |||||
patch, err := headGitRepo.GetPatch(prInfo.MergeBase, headBranch) | |||||
if err != nil { | if err != nil { | ||||
ctx.Handle(500, "GetPatch", err) | ctx.Handle(500, "GetPatch", err) | ||||
return | return |
0.6.16.1002 Beta | |||||
0.6.16.1004 Beta |