// runSync returns true if sync finished without error. func (m *Mirror) runSync() bool { repoPath := m.Repo.RepoPath() wikiPath := m.Repo.WikiPath() timeout := time.Duration(setting.Git.Timeout.Mirror) * time.Second gitArgs := []string{"remote", "update"} if m.EnablePrune { gitArgs = append(gitArgs, "--prune") } if _, stderr, err := process.ExecDir( timeout, repoPath, fmt.Sprintf("Mirror.runSync: %s", repoPath), "git", gitArgs...); err != nil { desc := fmt.Sprintf("Fail to update mirror repository '%s': %s", repoPath, stderr) log.Error(4, desc) if err = CreateRepositoryNotice(desc); err != nil { log.Error(4, "CreateRepositoryNotice: %v", err) } return false } if m.Repo.HasWiki() { if _, stderr, err := process.ExecDir( timeout, wikiPath, fmt.Sprintf("Mirror.runSync: %s", wikiPath), "git", "remote", "update", "--prune"); err != nil { desc := fmt.Sprintf("Fail to update mirror wiki repository '%s': %s", wikiPath, stderr) log.Error(4, desc) if err = CreateRepositoryNotice(desc); err != nil { log.Error(4, "CreateRepositoryNotice: %v", err) } return false } } return true }
// MigrateRepository migrates a existing repository from other project hosting. func MigrateRepository(u *User, name, desc string, private, mirror bool, url string) (*Repository, error) { repo, err := CreateRepository(u, name, desc, "", "", private, mirror, false) if err != nil { return nil, err } // Clone to temprory path and do the init commit. tmpDir := filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond())) os.MkdirAll(tmpDir, os.ModePerm) repoPath := RepoPath(u.Name, name) if u.IsOrganization() { t, err := u.GetOwnerTeam() if err != nil { return nil, err } repo.NumWatches = t.NumMembers } else { repo.NumWatches = 1 } repo.IsBare = false if mirror { if err = MirrorRepository(repo.Id, u.Name, repo.Name, repoPath, url); err != nil { return repo, err } repo.IsMirror = true return repo, UpdateRepository(repo) } // Clone from local repository. _, stderr, err := process.ExecTimeout(10*time.Minute, fmt.Sprintf("MigrateRepository(git clone): %s", repoPath), "git", "clone", repoPath, tmpDir) if err != nil { return repo, errors.New("git clone: " + stderr) } // Add remote and fetch data. if _, stderr, err = process.ExecDir(3*time.Minute, tmpDir, fmt.Sprintf("MigrateRepository(git pull): %s", repoPath), "git", "remote", "add", "-f", "--tags", "upstream", url); err != nil { return repo, errors.New("git remote: " + stderr) } // Push data to local repository. if _, stderr, err = process.ExecDir(3*time.Minute, tmpDir, fmt.Sprintf("MigrateRepository(git push): %s", repoPath), "git", "push", "--tags", "origin", "refs/remotes/upstream/*:refs/heads/*"); err != nil { return repo, errors.New("git push: " + stderr) } return repo, UpdateRepository(repo) }
// testPatch checks if patch can be merged to base repository without conflit. // FIXME: make a mechanism to clean up stable local copies. func (pr *PullRequest) testPatch() (err error) { if pr.BaseRepo == nil { pr.BaseRepo, err = GetRepositoryByID(pr.BaseRepoID) if err != nil { return fmt.Errorf("GetRepositoryByID: %v", err) } } patchPath, err := pr.BaseRepo.PatchPath(pr.Index) if err != nil { return fmt.Errorf("BaseRepo.PatchPath: %v", err) } // Fast fail if patch does not exist, this assumes data is cruppted. if !com.IsFile(patchPath) { log.Trace("PullRequest[%d].testPatch: ignored cruppted data", pr.ID) return nil } log.Trace("PullRequest[%d].testPatch (patchPath): %s", pr.ID, patchPath) if err := pr.BaseRepo.UpdateLocalCopy(); err != nil { return fmt.Errorf("UpdateLocalCopy: %v", err) } // Checkout base branch. _, stderr, err := process.ExecDir(-1, pr.BaseRepo.LocalCopyPath(), fmt.Sprintf("PullRequest.Merge (git checkout): %v", pr.BaseRepo.ID), "git", "checkout", pr.BaseBranch) if err != nil { return fmt.Errorf("git checkout: %s", stderr) } pr.Status = PULL_REQUEST_STATUS_CHECKING _, stderr, err = process.ExecDir(-1, pr.BaseRepo.LocalCopyPath(), fmt.Sprintf("testPatch (git apply --check): %d", pr.BaseRepo.ID), "git", "apply", "--check", patchPath) if err != nil { for i := range patchConflicts { if strings.Contains(stderr, patchConflicts[i]) { log.Trace("PullRequest[%d].testPatch (apply): has conflit", pr.ID) fmt.Println(stderr) pr.Status = PULL_REQUEST_STATUS_CONFLICT return nil } } return fmt.Errorf("git apply --check: %v - %s", err, stderr) } return nil }
// MigrateRepository migrates a existing repository from other project hosting. func MigrateRepository(u *User, name, desc string, private, mirror bool, url string) (*Repository, error) { repo, err := CreateRepository(u, name, desc, "", "", private, mirror, false) if err != nil { return nil, err } // Clone to temprory path and do the init commit. tmpDir := filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond())) os.MkdirAll(tmpDir, os.ModePerm) repoPath := RepoPath(u.Name, name) repo.IsBare = false if mirror { if err = MirrorRepository(repo.Id, u.Name, repo.Name, repoPath, url); err != nil { return repo, err } repo.IsMirror = true return repo, UpdateRepository(repo) } // TODO: need timeout. // Clone from local repository. _, stderr, err := process.Exec( fmt.Sprintf("MigrateRepository(git clone): %s", repoPath), "git", "clone", repoPath, tmpDir) if err != nil { return repo, errors.New("git clone: " + stderr) } // TODO: need timeout. // Pull data from source. if _, stderr, err = process.ExecDir( tmpDir, fmt.Sprintf("MigrateRepository(git pull): %s", repoPath), "git", "pull", url); err != nil { return repo, errors.New("git pull: " + stderr) } // TODO: need timeout. // Push data to local repository. if _, stderr, err = process.ExecDir( tmpDir, fmt.Sprintf("MigrateRepository(git push): %s", repoPath), "git", "push", "origin", "master"); err != nil { return repo, errors.New("git push: " + stderr) } return repo, UpdateRepository(repo) }
// GitFsck calls 'git fsck' to check repository health. func GitFsck() { if isGitFscking { return } isGitFscking = true defer func() { isGitFscking = false }() args := append([]string{"fsck"}, setting.Git.Fsck.Args...) if err := x.Where("id > 0").Iterate(new(Repository), func(idx int, bean interface{}) error { repo := bean.(*Repository) if err := repo.GetOwner(); err != nil { return err } repoPath := RepoPath(repo.Owner.Name, repo.Name) _, _, err := process.ExecDir(-1, repoPath, "Repository health check", "git", args...) if err != nil { desc := fmt.Sprintf("Fail to health check repository(%s)", repoPath) log.Warn(desc) if err = CreateRepositoryNotice(desc); err != nil { log.Error(4, "Fail to add notice: %v", err) } } return nil }); err != nil { log.Error(4, "repo.Fsck: %v", err) } }
func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Repository, err error) { repo := &Repository{ OwnerID: u.Id, Owner: u, Name: name, LowerName: strings.ToLower(name), Description: desc, DefaultBranch: oldRepo.DefaultBranch, IsPrivate: oldRepo.IsPrivate, IsFork: true, ForkID: oldRepo.ID, } sess := x.NewSession() defer sessionRelease(sess) if err = sess.Begin(); err != nil { return nil, err } if err = createRepository(sess, u, repo); err != nil { return nil, err } if _, err = sess.Exec("UPDATE `repository` SET num_forks=num_forks+1 WHERE id=?", oldRepo.ID); err != nil { return nil, err } // else if _, err = sess.Insert(&ForkInfo{ // ForkID: oldRepo.ID, // RepoID: repo.ID, // StartCommitID: "", // }); err != nil { // return nil, fmt.Errorf("insert fork info: %v", err) // } oldRepoPath, err := oldRepo.RepoPath() if err != nil { return nil, fmt.Errorf("get old repository path: %v", err) } repoPath := RepoPath(u.Name, repo.Name) _, stderr, err := process.ExecTimeout(10*time.Minute, fmt.Sprintf("ForkRepository(git clone): %s/%s", u.Name, repo.Name), "git", "clone", "--bare", oldRepoPath, repoPath) if err != nil { return nil, fmt.Errorf("git clone: %v", stderr) } _, stderr, err = process.ExecDir(-1, repoPath, fmt.Sprintf("ForkRepository(git update-server-info): %s", repoPath), "git", "update-server-info") if err != nil { return nil, fmt.Errorf("git update-server-info: %v", err) } if err = createUpdateHook(repoPath); err != nil { return nil, fmt.Errorf("createUpdateHook: %v", err) } return repo, sess.Commit() }
// GitFsck calls 'git fsck' to check repository health. func GitFsck() { if isGitFscking { return } isGitFscking = true defer func() { isGitFscking = false }() log.Trace("Doing: GitFsck") args := append([]string{"fsck"}, setting.Cron.RepoHealthCheck.Args...) if err := x.Where("id>0").Iterate(new(Repository), func(idx int, bean interface{}) error { repo := bean.(*Repository) repoPath, err := repo.RepoPath() if err != nil { return fmt.Errorf("RepoPath: %v", err) } _, _, err = process.ExecDir(-1, repoPath, "Repository health check", "git", args...) if err != nil { desc := fmt.Sprintf("Fail to health check repository(%s)", repoPath) log.Warn(desc) if err = CreateRepositoryNotice(desc); err != nil { log.Error(4, "CreateRepositoryNotice: %v", err) } } return nil }); err != nil { log.Error(4, "GitFsck: %v", err) } }
// DeleteReleaseByID deletes a release and corresponding Git tag by given ID. func DeleteReleaseByID(id int64) error { rel, err := GetReleaseByID(id) if err != nil { return fmt.Errorf("GetReleaseByID: %v", err) } repo, err := GetRepositoryByID(rel.RepoID) if err != nil { return fmt.Errorf("GetRepositoryByID: %v", err) } repoPath, err := repo.RepoPath() if err != nil { return fmt.Errorf("RepoPath: %v", err) } _, stderr, err := process.ExecDir(-1, repoPath, fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID), "git", "tag", "-d", rel.TagName) if err != nil && !strings.Contains(stderr, "not found") { return fmt.Errorf("git tag -d: %v - %s", err, stderr) } if _, err = x.Id(rel.ID).Delete(new(Release)); err != nil { return fmt.Errorf("Delete: %v", err) } return nil }
func SaveStuff(ctx *middleware.Context) { if ctx.Repo.Owner.Name != ctx.User.Name { fmt.Println("not yo repo") ctx.JSON(200, "NOT OK") } else { content := ctx.Query("content") fileDir := ctx.Query("fileDir") fileName := ctx.Query("fileName") fmt.Println(filepath.Join(ctx.Repo.GitRepo.Path+"/"+fileDir, fileName)) if strings.Contains(fileDir, "..") || strings.Contains(fileName, "..") { ctx.JSON(200, "NOT OK WRITE") } else { err := ioutil.WriteFile(filepath.Join(ctx.Repo.GitRepo.Path+"/"+fileDir, fileName), []byte(content), 0644) _, _, _ = process.ExecDir(-1, filepath.Join(ctx.Repo.GitRepo.Path), fmt.Sprintf("git add: %s", filepath.Join(ctx.Repo.GitRepo.Path)), "git", "add", "*.md") sig := ctx.User.NewGitSig() if _, stderr, err2 := process.ExecDir(-1, filepath.Join(ctx.Repo.GitRepo.Path), fmt.Sprintf("git commit: %s", filepath.Join(ctx.Repo.GitRepo.Path)), "git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", "save"); err2 != nil { ctx.JSON(200, "NOT COMMIT") fmt.Println("git commit: %s", err2) fmt.Println("git commit: %s", stderr) } else { if err != nil { ctx.JSON(200, "OK") } else { ctx.JSON(200, "NOT OK") } } } } }
// InitRepository initializes README and .gitignore if needed. func initRepository(e Engine, repoPath string, u *User, repo *Repository, opts CreateRepoOptions) error { // Somehow the directory could exist. if com.IsExist(repoPath) { return fmt.Errorf("initRepository: path already exists: %s", repoPath) } // Init bare new repository. os.MkdirAll(repoPath, os.ModePerm) _, stderr, err := process.ExecDir(-1, repoPath, fmt.Sprintf("initRepository(git init --bare): %s", repoPath), "git", "init", "--bare") if err != nil { return fmt.Errorf("git init --bare: %v - %s", err, stderr) } if err := createUpdateHook(repoPath); err != nil { return err } tmpDir := filepath.Join(os.TempDir(), "gogs", repo.Name, com.ToStr(time.Now().Nanosecond())) // Initialize repository according to user's choice. if opts.AutoInit { os.MkdirAll(tmpDir, os.ModePerm) defer os.RemoveAll(tmpDir) if err = prepareRepoCommit(repo, tmpDir, repoPath, opts); err != nil { return fmt.Errorf("prepareRepoCommit: %v", err) } // Apply changes and commit. if err = initRepoCommit(tmpDir, u.NewGitSig()); err != nil { return fmt.Errorf("initRepoCommit: %v", err) } } // Re-fetch the repository from database before updating it (else it would // override changes that were done earlier with sql) if repo, err = getRepositoryByID(e, repo.ID); err != nil { return fmt.Errorf("getRepositoryByID: %v", err) } if !opts.AutoInit { repo.IsBare = true } repo.DefaultBranch = "master" if err = updateRepository(e, repo, false); err != nil { return fmt.Errorf("updateRepository: %v", err) } return nil }
// initRepoCommit temporarily changes with work directory. func initRepoCommit(tmpPath string, sig *git.Signature) (err error) { var stderr string if _, stderr, err = process.ExecDir(-1, tmpPath, fmt.Sprintf("initRepoCommit(git add): %s", tmpPath), "git", "add", "--all"); err != nil { return errors.New("git add: " + stderr) } if _, stderr, err = process.ExecDir(-1, tmpPath, fmt.Sprintf("initRepoCommit(git commit): %s", tmpPath), "git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", "initial commit"); err != nil { return errors.New("git commit: " + stderr) } if _, stderr, err = process.ExecDir(-1, tmpPath, fmt.Sprintf("initRepoCommit(git push): %s", tmpPath), "git", "push", "origin", "master"); err != nil { return errors.New("git push: " + stderr) } return nil }
// MirrorUpdate checks and updates mirror repositories. func MirrorUpdate() { if isMirrorUpdating { return } isMirrorUpdating = true defer func() { isMirrorUpdating = false }() log.Trace("Doing: MirrorUpdate") mirrors := make([]*Mirror, 0, 10) if err := x.Iterate(new(Mirror), func(idx int, bean interface{}) error { m := bean.(*Mirror) if m.NextUpdate.After(time.Now()) { return nil } if m.Repo == nil { log.Error(4, "Disconnected mirror repository found: %d", m.ID) return nil } repoPath, err := m.Repo.RepoPath() if err != nil { return fmt.Errorf("Repo.RepoPath: %v", err) } if _, stderr, err := process.ExecDir(10*time.Minute, repoPath, fmt.Sprintf("MirrorUpdate: %s", repoPath), "git", "remote", "update", "--prune"); err != nil { desc := fmt.Sprintf("Fail to update mirror repository(%s): %s", repoPath, stderr) log.Error(4, desc) if err = CreateRepositoryNotice(desc); err != nil { log.Error(4, "CreateRepositoryNotice: %v", err) } return nil } m.NextUpdate = time.Now().Add(time.Duration(m.Interval) * time.Hour) mirrors = append(mirrors, m) return nil }); err != nil { log.Error(4, "MirrorUpdate: %v", err) } for i := range mirrors { if err := UpdateMirror(mirrors[i]); err != nil { log.Error(4, "UpdateMirror[%d]: %v", mirrors[i].ID, err) } } }
func GitGcRepos() error { args := append([]string{"gc"}, setting.Git.GcArgs...) return x.Where("id > 0").Iterate(new(Repository), func(idx int, bean interface{}) error { repo := bean.(*Repository) if err := repo.GetOwner(); err != nil { return err } _, stderr, err := process.ExecDir(-1, RepoPath(repo.Owner.Name, repo.Name), "Repository garbage collection", "git", args...) if err != nil { return fmt.Errorf("%v: %v", err, stderr) } return nil }) }
// CreateRepository creates a repository for given user or organization. func CreateRepository(u *User, opts CreateRepoOptions) (_ *Repository, err error) { repo := &Repository{ OwnerID: u.Id, Owner: u, Name: opts.Name, LowerName: strings.ToLower(opts.Name), Description: opts.Description, IsPrivate: opts.IsPrivate, } sess := x.NewSession() defer sessionRelease(sess) if err = sess.Begin(); err != nil { return nil, err } if err = createRepository(sess, u, repo); err != nil { return nil, err } // No need for init mirror. if !opts.IsMirror { repoPath := RepoPath(u.Name, repo.Name) if err = initRepository(sess, repoPath, u, repo, opts); err != nil { if err2 := os.RemoveAll(repoPath); err2 != nil { log.Error(4, "initRepository: %v", err) return nil, fmt.Errorf( "delete repo directory %s/%s failed(2): %v", u.Name, repo.Name, err2) } return nil, fmt.Errorf("initRepository: %v", err) } _, stderr, err := process.ExecDir(-1, repoPath, fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath), "git", "update-server-info") if err != nil { return nil, errors.New("CreateRepository(git update-server-info): " + stderr) } } return repo, sess.Commit() }
// MirrorUpdate checks and updates mirror repositories. func MirrorUpdate() { if err := x.Iterate(new(Mirror), func(idx int, bean interface{}) error { m := bean.(*Mirror) if m.NextUpdate.After(time.Now()) { return nil } repoPath := filepath.Join(setting.RepoRootPath, m.RepoName+".git") if _, stderr, err := process.ExecDir(10*time.Minute, repoPath, fmt.Sprintf("MirrorUpdate: %s", repoPath), "git", "remote", "update"); err != nil { return errors.New("git remote update: " + stderr) } m.NextUpdate = time.Now().Add(time.Duration(m.Interval) * time.Hour) return UpdateMirror(m) }); err != nil { log.Error(4, "repo.MirrorUpdate: %v", err) } }
// MirrorUpdate checks and updates mirror repositories. func MirrorUpdate() { if isMirrorUpdating { return } isMirrorUpdating = true defer func() { isMirrorUpdating = false }() mirrors := make([]*Mirror, 0, 10) if err := x.Iterate(new(Mirror), func(idx int, bean interface{}) error { m := bean.(*Mirror) if m.NextUpdate.After(time.Now()) { return nil } repoPath := filepath.Join(setting.RepoRootPath, m.RepoName+".git") if _, stderr, err := process.ExecDir(10*time.Minute, repoPath, fmt.Sprintf("MirrorUpdate: %s", repoPath), "git", "remote", "update", "--prune"); err != nil { desc := fmt.Sprintf("Fail to update mirror repository(%s): %s", repoPath, stderr) log.Error(4, desc) if err = CreateRepositoryNotice(desc); err != nil { log.Error(4, "Fail to add notice: %v", err) } return nil } m.NextUpdate = time.Now().Add(time.Duration(m.Interval) * time.Hour) mirrors = append(mirrors, m) return nil }); err != nil { log.Error(4, "MirrorUpdate: %v", err) } for i := range mirrors { if err := UpdateMirror(mirrors[i]); err != nil { log.Error(4, "UpdateMirror", fmt.Sprintf("%s: %v", mirrors[i].RepoName, err)) } } }
// Merge merges pull request to base repository. func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error) { sess := x.NewSession() defer sessionRelease(sess) if err = sess.Begin(); err != nil { return err } if err = pr.Issue.changeStatus(sess, doer, true); err != nil { return fmt.Errorf("Pull.changeStatus: %v", err) } headRepoPath := RepoPath(pr.HeadUserName, pr.HeadRepo.Name) headGitRepo, err := git.OpenRepository(headRepoPath) if err != nil { return fmt.Errorf("OpenRepository: %v", err) } pr.MergedCommitID, err = headGitRepo.GetCommitIdOfBranch(pr.HeadBranch) if err != nil { return fmt.Errorf("GetCommitIdOfBranch: %v", err) } if err = mergePullRequestAction(sess, doer, pr.Issue.Repo, pr.Issue); err != nil { return fmt.Errorf("mergePullRequestAction: %v", err) } pr.HasMerged = true pr.Merged = time.Now() pr.MergerID = doer.Id if _, err = sess.Id(pr.ID).AllCols().Update(pr); err != nil { return fmt.Errorf("update pull request: %v", err) } // Clone base repo. tmpBasePath := path.Join("data/tmp/repos", com.ToStr(time.Now().Nanosecond())+".git") os.MkdirAll(path.Dir(tmpBasePath), os.ModePerm) defer os.RemoveAll(path.Dir(tmpBasePath)) var stderr string if _, stderr, err = process.ExecTimeout(5*time.Minute, fmt.Sprintf("PullRequest.Merge(git clone): %s", tmpBasePath), "git", "clone", baseGitRepo.Path, tmpBasePath); err != nil { return fmt.Errorf("git clone: %s", stderr) } // Check out base branch. if _, stderr, err = process.ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge(git checkout): %s", tmpBasePath), "git", "checkout", pr.BaseBranch); err != nil { return fmt.Errorf("git checkout: %s", stderr) } // Pull commits. if _, stderr, err = process.ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge(git pull): %s", tmpBasePath), "git", "pull", headRepoPath, pr.HeadBranch); err != nil { return fmt.Errorf("git pull[%s / %s -> %s]: %s", headRepoPath, pr.HeadBranch, tmpBasePath, stderr) } // Push back to upstream. if _, stderr, err = process.ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge(git push): %s", tmpBasePath), "git", "push", baseGitRepo.Path, pr.BaseBranch); err != nil { return fmt.Errorf("git push: %s", stderr) } return sess.Commit() }
// NewPullRequest creates new pull request with labels for repository. func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []string, pr *PullRequest, patch []byte) (err error) { sess := x.NewSession() defer sessionRelease(sess) if err = sess.Begin(); err != nil { return err } if err = newIssue(sess, repo, pull, labelIDs, uuids, true); err != nil { return fmt.Errorf("newIssue: %v", err) } // Notify watchers. act := &Action{ ActUserID: pull.Poster.Id, ActUserName: pull.Poster.Name, ActEmail: pull.Poster.Email, OpType: CREATE_PULL_REQUEST, Content: fmt.Sprintf("%d|%s", pull.Index, pull.Name), RepoID: repo.ID, RepoUserName: repo.Owner.Name, RepoName: repo.Name, IsPrivate: repo.IsPrivate, } if err = notifyWatchers(sess, act); err != nil { return err } // Test apply patch. if err = repo.UpdateLocalCopy(); err != nil { return fmt.Errorf("UpdateLocalCopy: %v", err) } repoPath, err := repo.RepoPath() if err != nil { return fmt.Errorf("RepoPath: %v", err) } patchPath := path.Join(repoPath, "pulls", com.ToStr(pull.ID)+".patch") os.MkdirAll(path.Dir(patchPath), os.ModePerm) if err = ioutil.WriteFile(patchPath, patch, 0644); err != nil { return fmt.Errorf("save patch: %v", err) } pr.Status = PULL_REQUEST_STATUS_MERGEABLE _, stderr, err := process.ExecDir(-1, repo.LocalCopyPath(), fmt.Sprintf("NewPullRequest(git apply --check): %d", repo.ID), "git", "apply", "--check", patchPath) if err != nil { if strings.Contains(stderr, "patch does not apply") { pr.Status = PULL_REQUEST_STATUS_CONFLICT } else { return fmt.Errorf("git apply --check: %v - %s", err, stderr) } } pr.IssueID = pull.ID pr.Index = pull.Index if _, err = sess.Insert(pr); err != nil { return fmt.Errorf("insert pull repo: %v", err) } return sess.Commit() }
// Merge merges pull request to base repository. func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error) { sess := x.NewSession() defer sessionRelease(sess) if err = sess.Begin(); err != nil { return err } if err = pr.Issue.changeStatus(sess, doer, true); err != nil { return fmt.Errorf("Issue.changeStatus: %v", err) } if err = pr.getHeadRepo(sess); err != nil { return fmt.Errorf("getHeadRepo: %v", err) } headRepoPath := RepoPath(pr.HeadUserName, pr.HeadRepo.Name) headGitRepo, err := git.OpenRepository(headRepoPath) if err != nil { return fmt.Errorf("OpenRepository: %v", err) } pr.MergedCommitID, err = headGitRepo.GetCommitIdOfBranch(pr.HeadBranch) if err != nil { return fmt.Errorf("GetCommitIdOfBranch: %v", err) } if err = mergePullRequestAction(sess, doer, pr.Issue.Repo, pr.Issue); err != nil { return fmt.Errorf("mergePullRequestAction: %v", err) } pr.HasMerged = true pr.Merged = time.Now() pr.MergerID = doer.Id if _, err = sess.Id(pr.ID).AllCols().Update(pr); err != nil { return fmt.Errorf("update pull request: %v", err) } // Clone base repo. tmpBasePath := path.Join(setting.AppDataPath, "tmp/repos", com.ToStr(time.Now().Nanosecond())+".git") os.MkdirAll(path.Dir(tmpBasePath), os.ModePerm) defer os.RemoveAll(path.Dir(tmpBasePath)) var stderr string if _, stderr, err = process.ExecTimeout(5*time.Minute, fmt.Sprintf("PullRequest.Merge (git clone): %s", tmpBasePath), "git", "clone", baseGitRepo.Path, tmpBasePath); err != nil { return fmt.Errorf("git clone: %s", stderr) } // Check out base branch. if _, stderr, err = process.ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath), "git", "checkout", pr.BaseBranch); err != nil { return fmt.Errorf("git checkout: %s", stderr) } // Add head repo remote. if _, stderr, err = process.ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git remote add): %s", tmpBasePath), "git", "remote", "add", "head_repo", headRepoPath); err != nil { return fmt.Errorf("git remote add [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) } // Merge commits. if _, stderr, err = process.ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git fetch): %s", tmpBasePath), "git", "fetch", "head_repo"); err != nil { return fmt.Errorf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) } if _, stderr, err = process.ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git merge --no-ff --no-commit): %s", tmpBasePath), "git", "merge", "--no-ff", "--no-commit", "head_repo/"+pr.HeadBranch); err != nil { return fmt.Errorf("git merge --no-ff --no-commit [%s]: %v - %s", tmpBasePath, err, stderr) } sig := doer.NewGitSig() if _, stderr, err = process.ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath), "git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", fmt.Sprintf("Merge branch '%s' of %s/%s into %s", pr.HeadBranch, pr.HeadUserName, pr.HeadRepo.Name, pr.BaseBranch)); err != nil { return fmt.Errorf("git commit [%s]: %v - %s", tmpBasePath, err, stderr) } // Push back to upstream. if _, stderr, err = process.ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git push): %s", tmpBasePath), "git", "push", baseGitRepo.Path, pr.BaseBranch); err != nil { return fmt.Errorf("git push: %s", stderr) } return sess.Commit() }
// CreateRepository creates a repository for given user or organization. func CreateRepository(u *User, name, desc, lang, license string, isPrivate, isMirror, initReadme bool) (_ *Repository, err error) { if err = IsUsableName(name); err != nil { return nil, err } has, err := IsRepositoryExist(u, name) if err != nil { return nil, fmt.Errorf("IsRepositoryExist: %v", err) } else if has { return nil, ErrRepoAlreadyExist } repo := &Repository{ OwnerId: u.Id, Owner: u, Name: name, LowerName: strings.ToLower(name), Description: desc, IsPrivate: isPrivate, } sess := x.NewSession() defer sessionRelease(sess) if err = sess.Begin(); err != nil { return nil, err } if _, err = sess.Insert(repo); err != nil { return nil, err } else if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", u.Id); err != nil { return nil, err } // TODO fix code for mirrors? // Give access to all members in owner team. if u.IsOrganization() { t, err := u.getOwnerTeam(sess) if err != nil { return nil, fmt.Errorf("getOwnerTeam: %v", err) } else if err = t.addRepository(sess, repo); err != nil { return nil, fmt.Errorf("addRepository: %v", err) } } else { // Organization called this in addRepository method. if err = repo.recalculateAccesses(sess); err != nil { return nil, fmt.Errorf("recalculateAccesses: %v", err) } } if err = watchRepo(sess, u.Id, repo.Id, true); err != nil { return nil, fmt.Errorf("watchRepo: %v", err) } else if err = newRepoAction(sess, u, repo); err != nil { return nil, fmt.Errorf("newRepoAction: %v", err) } // No need for init mirror. if !isMirror { repoPath := RepoPath(u.Name, repo.Name) if err = initRepository(sess, repoPath, u, repo, initReadme, lang, license); err != nil { if err2 := os.RemoveAll(repoPath); err2 != nil { log.Error(4, "initRepository: %v", err) return nil, fmt.Errorf( "delete repo directory %s/%s failed(2): %v", u.Name, repo.Name, err2) } return nil, fmt.Errorf("initRepository: %v", err) } _, stderr, err := process.ExecDir(-1, repoPath, fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath), "git", "update-server-info") if err != nil { return nil, errors.New("CreateRepository(git update-server-info): " + stderr) } } return repo, sess.Commit() }
// CreateRepository creates a repository for given user or organization. func CreateRepository(u *User, name, desc, lang, license string, private, mirror, initReadme bool) (*Repository, error) { if !IsLegalName(name) { return nil, ErrRepoNameIllegal } isExist, err := IsRepositoryExist(u, name) if err != nil { return nil, err } else if isExist { return nil, ErrRepoAlreadyExist } sess := x.NewSession() defer sess.Close() if err = sess.Begin(); err != nil { return nil, err } repo := &Repository{ OwnerId: u.Id, Owner: u, Name: name, LowerName: strings.ToLower(name), Description: desc, IsPrivate: private, } if _, err = sess.Insert(repo); err != nil { sess.Rollback() return nil, err } var t *Team // Owner team. mode := WRITABLE if mirror { mode = READABLE } access := &Access{ UserName: u.LowerName, RepoName: path.Join(u.LowerName, repo.LowerName), Mode: mode, } // Give access to all members in owner team. if u.IsOrganization() { t, err = u.GetOwnerTeam() if err != nil { sess.Rollback() return nil, err } if err = t.GetMembers(); err != nil { sess.Rollback() return nil, err } for _, u := range t.Members { access.Id = 0 access.UserName = u.LowerName if _, err = sess.Insert(access); err != nil { sess.Rollback() return nil, err } } } else { if _, err = sess.Insert(access); err != nil { sess.Rollback() return nil, err } } if _, err = sess.Exec( "UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", u.Id); err != nil { sess.Rollback() return nil, err } // Update owner team info and count. if u.IsOrganization() { t.RepoIds += "$" + com.ToStr(repo.Id) + "|" t.NumRepos++ if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil { sess.Rollback() return nil, err } } if err = sess.Commit(); err != nil { return nil, err } if u.IsOrganization() { t, err := u.GetOwnerTeam() if err != nil { log.Error(4, "GetOwnerTeam: %v", err) } else { if err = t.GetMembers(); err != nil { log.Error(4, "GetMembers: %v", err) } else { for _, u := range t.Members { if err = WatchRepo(u.Id, repo.Id, true); err != nil { log.Error(4, "WatchRepo2: %v", err) } } } } } else { if err = WatchRepo(u.Id, repo.Id, true); err != nil { log.Error(4, "WatchRepo3: %v", err) } } if err = NewRepoAction(u, repo); err != nil { log.Error(4, "NewRepoAction: %v", err) } // No need for init mirror. if mirror { return repo, nil } repoPath := RepoPath(u.Name, repo.Name) if err = initRepository(repoPath, u, repo, initReadme, lang, license); err != nil { if err2 := os.RemoveAll(repoPath); err2 != nil { log.Error(4, "initRepository: %v", err) return nil, fmt.Errorf( "delete repo directory %s/%s failed(2): %v", u.Name, repo.Name, err2) } return nil, fmt.Errorf("initRepository: %v", err) } _, stderr, err := process.ExecDir(-1, repoPath, fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath), "git", "update-server-info") if err != nil { return nil, errors.New("CreateRepository(git update-server-info): " + stderr) } return repo, nil }
// Merge merges pull request to base repository. func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error) { if err = pr.GetHeadRepo(); err != nil { return fmt.Errorf("GetHeadRepo: %v", err) } else if err = pr.GetBaseRepo(); err != nil { return fmt.Errorf("GetBaseRepo: %v", err) } sess := x.NewSession() defer sessionRelease(sess) if err = sess.Begin(); err != nil { return err } if err = pr.Issue.changeStatus(sess, doer, pr.Issue.Repo, true); err != nil { return fmt.Errorf("Issue.changeStatus: %v", err) } headRepoPath := RepoPath(pr.HeadUserName, pr.HeadRepo.Name) headGitRepo, err := git.OpenRepository(headRepoPath) if err != nil { return fmt.Errorf("OpenRepository: %v", err) } pr.MergedCommitID, err = headGitRepo.GetBranchCommitID(pr.HeadBranch) if err != nil { return fmt.Errorf("GetBranchCommitID: %v", err) } if err = mergePullRequestAction(sess, doer, pr.Issue.Repo, pr.Issue); err != nil { return fmt.Errorf("mergePullRequestAction: %v", err) } pr.HasMerged = true pr.Merged = time.Now() pr.MergerID = doer.ID if _, err = sess.Id(pr.ID).AllCols().Update(pr); err != nil { return fmt.Errorf("update pull request: %v", err) } // Clone base repo. tmpBasePath := path.Join(setting.AppDataPath, "tmp/repos", com.ToStr(time.Now().Nanosecond())+".git") os.MkdirAll(path.Dir(tmpBasePath), os.ModePerm) defer os.RemoveAll(path.Dir(tmpBasePath)) var stderr string if _, stderr, err = process.ExecTimeout(5*time.Minute, fmt.Sprintf("PullRequest.Merge (git clone): %s", tmpBasePath), "git", "clone", baseGitRepo.Path, tmpBasePath); err != nil { return fmt.Errorf("git clone: %s", stderr) } // Check out base branch. if _, stderr, err = process.ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath), "git", "checkout", pr.BaseBranch); err != nil { return fmt.Errorf("git checkout: %s", stderr) } // Add head repo remote. if _, stderr, err = process.ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git remote add): %s", tmpBasePath), "git", "remote", "add", "head_repo", headRepoPath); err != nil { return fmt.Errorf("git remote add [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) } // Merge commits. if _, stderr, err = process.ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git fetch): %s", tmpBasePath), "git", "fetch", "head_repo"); err != nil { return fmt.Errorf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) } if _, stderr, err = process.ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git merge --no-ff --no-commit): %s", tmpBasePath), "git", "merge", "--no-ff", "--no-commit", "head_repo/"+pr.HeadBranch); err != nil { return fmt.Errorf("git merge --no-ff --no-commit [%s]: %v - %s", tmpBasePath, err, stderr) } sig := doer.NewGitSig() if _, stderr, err = process.ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath), "git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", fmt.Sprintf("Merge branch '%s' of %s/%s into %s", pr.HeadBranch, pr.HeadUserName, pr.HeadRepo.Name, pr.BaseBranch)); err != nil { return fmt.Errorf("git commit [%s]: %v - %s", tmpBasePath, err, stderr) } // Push back to upstream. if _, stderr, err = process.ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git push): %s", tmpBasePath), "git", "push", baseGitRepo.Path, pr.BaseBranch); err != nil { return fmt.Errorf("git push: %s", stderr) } if err = sess.Commit(); err != nil { return fmt.Errorf("Commit: %v", err) } // Compose commit repository action l, err := headGitRepo.CommitsBetweenIDs(pr.MergedCommitID, pr.MergeBase) if err != nil { return fmt.Errorf("CommitsBetween: %v", err) } p := &api.PushPayload{ Ref: "refs/heads/" + pr.BaseBranch, Before: pr.MergeBase, After: pr.MergedCommitID, CompareUrl: setting.AppUrl + pr.BaseRepo.ComposeCompareURL(pr.MergeBase, pr.MergedCommitID), Commits: ListToPushCommits(l).ToApiPayloadCommits(pr.BaseRepo.FullLink()), Repo: pr.BaseRepo.ComposePayload(), Pusher: &api.PayloadAuthor{ Name: pr.HeadRepo.MustOwner().DisplayName(), Email: pr.HeadRepo.MustOwner().Email, UserName: pr.HeadRepo.MustOwner().Name, }, Sender: &api.PayloadUser{ UserName: doer.Name, ID: doer.ID, AvatarUrl: setting.AppUrl + doer.RelAvatarLink(), }, } if err = PrepareWebhooks(pr.BaseRepo, HOOK_EVENT_PUSH, p); err != nil { return fmt.Errorf("PrepareWebhooks: %v", err) } go HookQueue.Add(pr.BaseRepo.ID) return nil }
// InitRepository initializes README and .gitignore if needed. func initRepository(e Engine, repoPath string, u *User, repo *Repository, initReadme bool, repoLang, license string) error { // Somehow the directory could exist. if com.IsExist(repoPath) { return fmt.Errorf("initRepository: path already exists: %s", repoPath) } // Init bare new repository. os.MkdirAll(repoPath, os.ModePerm) _, stderr, err := process.ExecDir(-1, repoPath, fmt.Sprintf("initRepository(git init --bare): %s", repoPath), "git", "init", "--bare") if err != nil { return fmt.Errorf("git init --bare: %s", err) } if err := createUpdateHook(repoPath); err != nil { return err } // Initialize repository according to user's choice. fileName := map[string]string{} if initReadme { fileName["readme"] = "README.md" } if repoLang != "" { fileName["gitign"] = ".gitignore" } if license != "" { fileName["license"] = "LICENSE" } // Clone to temprory path and do the init commit. tmpDir := filepath.Join(os.TempDir(), com.ToStr(time.Now().Nanosecond())) os.MkdirAll(tmpDir, os.ModePerm) defer os.RemoveAll(tmpDir) _, stderr, err = process.Exec( fmt.Sprintf("initRepository(git clone): %s", repoPath), "git", "clone", repoPath, tmpDir) if err != nil { return errors.New("git clone: " + stderr) } // README if initReadme { defaultReadme := repo.Name + "\n" + strings.Repeat("=", utf8.RuneCountInString(repo.Name)) + "\n\n" + repo.Description if err := ioutil.WriteFile(filepath.Join(tmpDir, fileName["readme"]), []byte(defaultReadme), 0644); err != nil { return err } } // FIXME: following two can be merged. // .gitignore // Copy custom file when available. customPath := path.Join(setting.CustomPath, "conf/gitignore", repoLang) targetPath := path.Join(tmpDir, fileName["gitign"]) if com.IsFile(customPath) { if err := com.Copy(customPath, targetPath); err != nil { return fmt.Errorf("copy gitignore: %v", err) } } else if com.IsSliceContainsStr(Gitignores, repoLang) { if err = ioutil.WriteFile(targetPath, bindata.MustAsset(path.Join("conf/gitignore", repoLang)), 0644); err != nil { return fmt.Errorf("generate gitignore: %v", err) } } else { delete(fileName, "gitign") } // LICENSE customPath = path.Join(setting.CustomPath, "conf/license", license) targetPath = path.Join(tmpDir, fileName["license"]) if com.IsFile(customPath) { if err = com.Copy(customPath, targetPath); err != nil { return fmt.Errorf("copy license: %v", err) } } else if com.IsSliceContainsStr(Licenses, license) { if err = ioutil.WriteFile(targetPath, bindata.MustAsset(path.Join("conf/license", license)), 0644); err != nil { return fmt.Errorf("generate license: %v", err) } } else { delete(fileName, "license") } if len(fileName) == 0 { // Re-fetch the repository from database before updating it (else it would // override changes that were done earlier with sql) if repo, err = getRepositoryById(e, repo.Id); err != nil { return err } repo.IsBare = true repo.DefaultBranch = "master" return updateRepository(e, repo, false) } // Apply changes and commit. return initRepoCommit(tmpDir, u.NewGitSig()) }
func ForkRepository(u *User, oldRepo *Repository) (*Repository, error) { isExist, err := IsRepositoryExist(u, oldRepo.Name) if err != nil { return nil, err } else if isExist { return nil, ErrRepoAlreadyExist } // In case the old repository is a fork. if oldRepo.IsFork { oldRepo, err = GetRepositoryById(oldRepo.ForkId) if err != nil { return nil, err } } sess := x.NewSession() defer sess.Close() if err = sess.Begin(); err != nil { return nil, err } repo := &Repository{ OwnerId: u.Id, Owner: u, Name: oldRepo.Name, LowerName: oldRepo.LowerName, Description: oldRepo.Description, IsPrivate: oldRepo.IsPrivate, IsFork: true, ForkId: oldRepo.Id, } if _, err = sess.Insert(repo); err != nil { sess.Rollback() return nil, err } var t *Team // Owner team. mode := WRITABLE access := &Access{ UserName: u.LowerName, RepoName: path.Join(u.LowerName, repo.LowerName), Mode: mode, } // Give access to all members in owner team. if u.IsOrganization() { t, err = u.GetOwnerTeam() if err != nil { sess.Rollback() return nil, err } if err = t.GetMembers(); err != nil { sess.Rollback() return nil, err } for _, u := range t.Members { access.Id = 0 access.UserName = u.LowerName if _, err = sess.Insert(access); err != nil { sess.Rollback() return nil, err } } } else { if _, err = sess.Insert(access); err != nil { sess.Rollback() return nil, err } } if _, err = sess.Exec( "UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", u.Id); err != nil { sess.Rollback() return nil, err } // Update owner team info and count. if u.IsOrganization() { t.RepoIds += "$" + com.ToStr(repo.Id) + "|" t.NumRepos++ if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil { sess.Rollback() return nil, err } } if u.IsOrganization() { t, err := u.GetOwnerTeam() if err != nil { log.Error(4, "GetOwnerTeam: %v", err) } else { if err = t.GetMembers(); err != nil { log.Error(4, "GetMembers: %v", err) } else { for _, u := range t.Members { if err = watchRepoWithEngine(sess, u.Id, repo.Id, true); err != nil { log.Error(4, "WatchRepo2: %v", err) } } } } } else { if err = watchRepoWithEngine(sess, u.Id, repo.Id, true); err != nil { log.Error(4, "WatchRepo3: %v", err) } } if err = NewRepoAction(u, repo); err != nil { log.Error(4, "NewRepoAction: %v", err) } if _, err = sess.Exec( "UPDATE `repository` SET num_forks = num_forks + 1 WHERE id = ?", oldRepo.Id); err != nil { sess.Rollback() return nil, err } oldRepoPath, err := oldRepo.RepoPath() if err != nil { sess.Rollback() return nil, fmt.Errorf("fail to get repo path(%s): %v", oldRepo.Name, err) } if err = sess.Commit(); err != nil { return nil, err } repoPath := RepoPath(u.Name, repo.Name) _, stderr, err := process.ExecTimeout(10*time.Minute, fmt.Sprintf("ForkRepository(git clone): %s/%s", u.Name, repo.Name), "git", "clone", "--bare", oldRepoPath, repoPath) if err != nil { return nil, errors.New("ForkRepository(git clone): " + stderr) } _, stderr, err = process.ExecDir(-1, repoPath, fmt.Sprintf("ForkRepository(git update-server-info): %s", repoPath), "git", "update-server-info") if err != nil { return nil, errors.New("ForkRepository(git update-server-info): " + stderr) } return repo, nil }
func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Repository, err error) { has, err := IsRepositoryExist(u, name) if err != nil { return nil, fmt.Errorf("IsRepositoryExist: %v", err) } else if has { return nil, ErrRepoAlreadyExist } // In case the old repository is a fork. if oldRepo.IsFork { oldRepo, err = GetRepositoryById(oldRepo.ForkId) if err != nil { return nil, err } } repo := &Repository{ OwnerId: u.Id, Owner: u, Name: name, LowerName: strings.ToLower(name), Description: desc, IsPrivate: oldRepo.IsPrivate, IsFork: true, ForkId: oldRepo.Id, } sess := x.NewSession() defer sessionRelease(sess) if err = sess.Begin(); err != nil { return nil, err } if _, err = sess.Insert(repo); err != nil { return nil, err } if err = repo.recalculateAccesses(sess); err != nil { return nil, err } else if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", u.Id); err != nil { return nil, err } if u.IsOrganization() { // Update owner team info and count. t, err := u.getOwnerTeam(sess) if err != nil { return nil, fmt.Errorf("getOwnerTeam: %v", err) } else if err = t.addRepository(sess, repo); err != nil { return nil, fmt.Errorf("addRepository: %v", err) } } else { if err = watchRepo(sess, u.Id, repo.Id, true); err != nil { return nil, fmt.Errorf("watchRepo: %v", err) } } if err = newRepoAction(sess, u, repo); err != nil { return nil, fmt.Errorf("newRepoAction: %v", err) } if _, err = sess.Exec("UPDATE `repository` SET num_forks=num_forks+1 WHERE id=?", oldRepo.Id); err != nil { return nil, err } oldRepoPath, err := oldRepo.RepoPath() if err != nil { return nil, fmt.Errorf("get old repository path: %v", err) } repoPath := RepoPath(u.Name, repo.Name) _, stderr, err := process.ExecTimeout(10*time.Minute, fmt.Sprintf("ForkRepository(git clone): %s/%s", u.Name, repo.Name), "git", "clone", "--bare", oldRepoPath, repoPath) if err != nil { return nil, fmt.Errorf("git clone: %v", stderr) } _, stderr, err = process.ExecDir(-1, repoPath, fmt.Sprintf("ForkRepository(git update-server-info): %s", repoPath), "git", "update-server-info") if err != nil { return nil, fmt.Errorf("git update-server-info: %v", err) } if err = createUpdateHook(repoPath); err != nil { return nil, fmt.Errorf("createUpdateHook: %v", err) } return repo, sess.Commit() }
// Merge merges pull request to base repository. // FIXME: add repoWorkingPull make sure two merges does not happen at same time. func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository) (err error) { if err = pr.GetHeadRepo(); err != nil { return fmt.Errorf("GetHeadRepo: %v", err) } else if err = pr.GetBaseRepo(); err != nil { return fmt.Errorf("GetBaseRepo: %v", err) } defer func() { go HookQueue.Add(pr.BaseRepo.ID) go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false) }() sess := x.NewSession() defer sessionRelease(sess) if err = sess.Begin(); err != nil { return err } if err = pr.Issue.changeStatus(sess, doer, pr.Issue.Repo, true); err != nil { return fmt.Errorf("Issue.changeStatus: %v", err) } headRepoPath := RepoPath(pr.HeadUserName, pr.HeadRepo.Name) headGitRepo, err := git.OpenRepository(headRepoPath) if err != nil { return fmt.Errorf("OpenRepository: %v", err) } // Clone base repo. tmpBasePath := path.Join(setting.AppDataPath, "tmp/repos", com.ToStr(time.Now().Nanosecond())+".git") os.MkdirAll(path.Dir(tmpBasePath), os.ModePerm) defer os.RemoveAll(path.Dir(tmpBasePath)) var stderr string if _, stderr, err = process.ExecTimeout(5*time.Minute, fmt.Sprintf("PullRequest.Merge (git clone): %s", tmpBasePath), "git", "clone", baseGitRepo.Path, tmpBasePath); err != nil { return fmt.Errorf("git clone: %s", stderr) } // Check out base branch. if _, stderr, err = process.ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath), "git", "checkout", pr.BaseBranch); err != nil { return fmt.Errorf("git checkout: %s", stderr) } // Add head repo remote. if _, stderr, err = process.ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git remote add): %s", tmpBasePath), "git", "remote", "add", "head_repo", headRepoPath); err != nil { return fmt.Errorf("git remote add [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) } // Merge commits. if _, stderr, err = process.ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git fetch): %s", tmpBasePath), "git", "fetch", "head_repo"); err != nil { return fmt.Errorf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr) } if _, stderr, err = process.ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git merge --no-ff --no-commit): %s", tmpBasePath), "git", "merge", "--no-ff", "--no-commit", "head_repo/"+pr.HeadBranch); err != nil { return fmt.Errorf("git merge --no-ff --no-commit [%s]: %v - %s", tmpBasePath, err, stderr) } sig := doer.NewGitSig() if _, stderr, err = process.ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath), "git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", fmt.Sprintf("Merge branch '%s' of %s/%s into %s", pr.HeadBranch, pr.HeadUserName, pr.HeadRepo.Name, pr.BaseBranch)); err != nil { return fmt.Errorf("git commit [%s]: %v - %s", tmpBasePath, err, stderr) } // Push back to upstream. if _, stderr, err = process.ExecDir(-1, tmpBasePath, fmt.Sprintf("PullRequest.Merge (git push): %s", tmpBasePath), "git", "push", baseGitRepo.Path, pr.BaseBranch); err != nil { return fmt.Errorf("git push: %s", stderr) } pr.MergedCommitID, err = headGitRepo.GetBranchCommitID(pr.HeadBranch) if err != nil { return fmt.Errorf("GetBranchCommit: %v", err) } pr.HasMerged = true pr.Merged = time.Now() pr.MergerID = doer.ID if _, err = sess.Id(pr.ID).AllCols().Update(pr); err != nil { return fmt.Errorf("update pull request: %v", err) } if err = sess.Commit(); err != nil { return fmt.Errorf("Commit: %v", err) } if err = MergePullRequestAction(doer, pr.Issue.Repo, pr.Issue); err != nil { log.Error(4, "MergePullRequestAction [%d]: %v", pr.ID, err) } // Reload pull request information. if err = pr.LoadAttributes(); err != nil { log.Error(4, "LoadAttributes: %v", err) return nil } if err = PrepareWebhooks(pr.Issue.Repo, HOOK_EVENT_PULL_REQUEST, &api.PullRequestPayload{ Action: api.HOOK_ISSUE_CLOSED, Index: pr.Index, PullRequest: pr.APIFormat(), Repository: pr.Issue.Repo.APIFormat(nil), Sender: doer.APIFormat(), }); err != nil { log.Error(4, "PrepareWebhooks: %v", err) return nil } l, err := headGitRepo.CommitsBetweenIDs(pr.MergedCommitID, pr.MergeBase) if err != nil { log.Error(4, "CommitsBetweenIDs: %v", err) return nil } // TODO: when squash commits, no need to append merge commit. // It is possible that head branch is not fully sync with base branch for merge commits, // so we need to get latest head commit and append merge commit manully // to avoid strange diff commits produced. mergeCommit, err := baseGitRepo.GetBranchCommit(pr.BaseBranch) if err != nil { log.Error(4, "GetBranchCommit: %v", err) return nil } l.PushFront(mergeCommit) p := &api.PushPayload{ Ref: git.BRANCH_PREFIX + pr.BaseBranch, Before: pr.MergeBase, After: pr.MergedCommitID, CompareURL: setting.AppUrl + pr.BaseRepo.ComposeCompareURL(pr.MergeBase, pr.MergedCommitID), Commits: ListToPushCommits(l).ToApiPayloadCommits(pr.BaseRepo.HTMLURL()), Repo: pr.BaseRepo.APIFormat(nil), Pusher: pr.HeadRepo.MustOwner().APIFormat(), Sender: doer.APIFormat(), } if err = PrepareWebhooks(pr.BaseRepo, HOOK_EVENT_PUSH, p); err != nil { return fmt.Errorf("PrepareWebhooks: %v", err) } return nil }