Example #1
0
func ensureTargetBranchExists(branch, remote string) error {
	task := fmt.Sprintf("Check whether branch '%v' exists", branch)

	// In case the branch exists locally, we are done.
	localExists, err := git.LocalBranchExists(branch)
	if err != nil {
		return errs.NewError(task, err)
	}
	if localExists {
		return nil
	}

	// Otherwise try to create a tracking branch.
	remoteExists, err := git.RemoteBranchExists(branch, remote)
	if err != nil {
		return errs.NewError(task, err)
	}
	if remoteExists {
		return errs.Wrap(task, git.CreateTrackingBranch(branch, remote))
	}

	// We have failed!
	return &git.ErrRefNotFound{branch}
}
Example #2
0
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()
}
Example #3
0
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()
}