func postTip() (err error) { // Load Git-related config. gitConfig, err := git.LoadConfig() if err != nil { return err } var ( remoteName = gitConfig.RemoteName ) // Get the current branch. currentBranch, err := gitutil.CurrentBranch() if err != nil { return err } // Get the commit to be posted task := "Get the commit to be posted for code review" commits, err := git.ShowCommits(currentBranch) if err != nil { return errs.NewError(task, err) } // Assert that things are consistent. if numCommits := len(commits); numCommits != 1 { panic(fmt.Sprintf("len(commits): expected 1, got %v", numCommits)) } // Make sure the commit is not a merge commit. if err := ensureNoMergeCommits(commits); err != nil { return err } // Prompt the user to confirm. if err := promptUserToConfirmCommits(commits); err != nil { return err } // Make sure the Story-Id tag is there. commits, changed, err := ensureStoryId(commits) if err != nil { return err } // Push the current branch if necessary. doPush := changed if !doPush { // Check whether the remote branch actually exists. task := fmt.Sprintf( "Make sure branch '%v' exists in remote '%v'", currentBranch, remoteName) exists, err := git.RemoteBranchExists(currentBranch, remoteName) if err != nil { return errs.NewError(task, err) } doPush = !exists } if !doPush { // In case the branch was not modified and it exists remotely, // check whether it is up to date. task := fmt.Sprintf("Check whether branch '%v' is up to date", currentBranch) upToDate, err := git.IsBranchSynchronized(currentBranch, remoteName) if err != nil { return errs.NewError(task, err) } doPush = !upToDate } // Push the branch. if doPush { if err := push(remoteName, currentBranch); err != nil { return err } } // In case the commit was changed, reload. if changed { commits, err = git.ShowCommits(currentBranch) if err != nil { return err } } // Post the commit for review. if err := postCommitsForReview(commits); err != nil { return err } // Print the followup dialog. return printFollowup() }
func postBranch(parentBranch string) (err error) { // Load the git-related config. gitConfig, err := git.LoadConfig() if err != nil { return err } var ( remoteName = gitConfig.RemoteName ) // Get the current branch name. currentBranch, err := gitutil.CurrentBranch() if err != nil { return err } if !flagNoFetch { // Fetch the remote repository. task := "Fetch the remote repository" log.Run(task) if err := git.UpdateRemotes(remoteName); err != nil { return errs.NewError(task, err) } } // Make sure the parent branch is up to date. task := fmt.Sprintf("Make sure reference '%v' is up to date", parentBranch) log.Run(task) if err := git.EnsureBranchSynchronized(parentBranch, remoteName); err != nil { return errs.NewError(task, err) } // Make sure the current branch is up to date. task = fmt.Sprintf("Make sure branch '%v' is up to date", currentBranch) log.Run(task) if err = git.EnsureBranchSynchronized(currentBranch, remoteName); err != nil { return errs.NewError(task, err) } // Get the commits to be posted task = "Get the commits to be posted for code review" commits, err := git.ShowCommitRange(parentBranch + "..") if err != nil { return errs.NewError(task, err) } // Make sure there are no merge commits. if err := ensureNoMergeCommits(commits); err != nil { return err } // Prompt the user to confirm. if err := promptUserToConfirmCommits(commits); err != nil { return err } // Rebase the current branch on top the parent branch. if !flagNoRebase { commits, err = rebase(currentBranch, parentBranch) if err != nil { return err } } // Ensure the Story-Id tag is there. commits, _, err = ensureStoryId(commits) if err != nil { return err } // Get data on the current branch. task = fmt.Sprintf("Get data on branch '%v'", currentBranch) remoteCurrentExists, err := git.RemoteBranchExists(currentBranch, remoteName) if err != nil { return errs.NewError(task, err) } currentUpToDate, err := git.IsBranchSynchronized(currentBranch, remoteName) if err != nil { return errs.NewError(task, err) } // Merge the current branch into the parent branch unless -no_merge. pushTask := "Push the current branch" if flagNoMerge { // In case the user doesn't want to merge, // we need to push the current branch. if !remoteCurrentExists || !currentUpToDate { if err := push(remoteName, currentBranch); err != nil { return errs.NewError(pushTask, err) } } } else { // Still push the current branch if necessary. if remoteCurrentExists && !currentUpToDate { if err := push(remoteName, currentBranch); err != nil { return errs.NewError(pushTask, err) } } // Merge the branch into the parent branch mergeTask := fmt.Sprintf("Merge branch '%v' into branch '%v'", currentBranch, parentBranch) log.Run(mergeTask) act, err := merge(mergeTask, currentBranch, parentBranch) if err != nil { return err } // Push the parent branch. if err := push(remoteName, parentBranch); err != nil { // In case the push fails, we revert the merge as well. if err := act.Rollback(); err != nil { errs.Log(err) } return errs.NewError(mergeTask, err) } // Register a rollback function that just says that // a pushed merge cannot be reverted. defer action.RollbackOnError(&err, action.ActionFunc(func() error { log.Rollback(mergeTask) hint := "\nCannot revert merge that has already been pushed.\n" return errs.NewErrorWithHint( "Revert the merge", errors.New("merge commit already pushed"), hint) })) } // Post the review requests. if err := postCommitsForReview(commits); err != nil { return err } // In case there is no error, tell the user they can do next. return printFollowup() }