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() }
// 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, CreateRepoOptions{ Name: name, Description: desc, IsPrivate: private, IsMirror: mirror, }) 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, false) } else { os.RemoveAll(repoPath) } // FIXME: this command could for both migrate and mirror _, stderr, err := process.ExecTimeout(10*time.Minute, fmt.Sprintf("MigrateRepository: %s", repoPath), "git", "clone", "--mirror", "--bare", "--quiet", url, repoPath) if err != nil { return repo, fmt.Errorf("git clone --mirror --bare --quiet: %v", stderr) } else if err = createUpdateHook(repoPath); err != nil { return repo, fmt.Errorf("create update hook: %v", err) } // Check if repository has master branch, if so set it to default branch. gitRepo, err := git.OpenRepository(repoPath) if err != nil { return repo, fmt.Errorf("open git repository: %v", err) } if gitRepo.IsBranchExist("master") { repo.DefaultBranch = "master" } return repo, UpdateRepository(repo, false) }
// 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) }
// MirrorRepository creates a mirror repository from source. func MirrorRepository(repoId int64, userName, repoName, repoPath, url string) error { _, stderr, err := process.ExecTimeout(10*time.Minute, fmt.Sprintf("MirrorRepository: %s/%s", userName, repoName), "git", "clone", "--mirror", url, repoPath) if err != nil { return errors.New("git clone --mirror: " + stderr) } if _, err = x.InsertOne(&Mirror{ RepoID: repoId, Interval: 24, NextUpdate: time.Now().Add(24 * time.Hour), }); err != nil { return err } 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) 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, false) } else { os.RemoveAll(repoPath) } // FIXME: this command could for both migrate and mirror _, stderr, err := process.ExecTimeout(10*time.Minute, fmt.Sprintf("MigrateRepository: %s", repoPath), "git", "clone", "--mirror", "--bare", url, repoPath) if err != nil { return repo, fmt.Errorf("git clone --mirror --bare: %v", stderr) } else if err = createUpdateHook(repoPath); err != nil { return repo, fmt.Errorf("create update hook: %v", err) } return repo, UpdateRepository(repo, false) }
// 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 }
// 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() }
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. 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() }
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 }
// 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 }