Example #1
0
    def merge_into(self, repo: GitRepo, dry_run: bool = False) -> None:
        # Raises exception if matching rule is not found
        find_matching_merge_rule(self, repo)
        if repo.current_branch() != self.default_branch():
            repo.checkout(self.default_branch())
        if not self.is_ghstack_pr():
            msg = self.get_title() + "\n\n" + self.get_body()
            msg += f"\nPull Request resolved: {self.get_pr_url()}\n"
            pr_branch_name = f"__pull-request-{self.pr_num}__init__"
            repo.fetch(f"pull/{self.pr_num}/head", pr_branch_name)
            repo._run_git("merge", "--squash", pr_branch_name)
            repo._run_git("commit", f"--author=\"{self.get_author()}\"", "-m",
                          msg)
        else:
            self.merge_ghstack_into(repo)

        repo.push(self.default_branch(), dry_run)
Example #2
0
def try_revert(repo: GitRepo, pr: GitHubPR, dry_run: bool = False) -> None:
    def post_comment(msg: str) -> None:
        gh_post_comment(pr.org, pr.project, pr.pr_num, msg, dry_run=dry_run)

    if not pr.is_closed():
        return post_comment(f"Can't revert open PR #{pr.pr_num}")
    if not RE_REVERT_CMD.match(pr.get_comment_body()):
        raise RuntimeError(
            f"Comment {pr.get_comment_body()} does not seem to be a valid revert command"
        )
    if pr.get_comment_editor_login() is not None:
        return post_comment("Don't want to revert based on edited command")
    author_association = pr.get_comment_author_association()
    author_login = pr.get_comment_author_login()
    # For some reason, one can not be a member of private repo, only CONTRIBUTOR
    expected_association = "CONTRIBUTOR" if pr.is_base_repo_private(
    ) else "MEMBER"
    if author_association != expected_association and author_association != "OWNER":
        return post_comment(
            f"Will not revert as @{author_login} is not a {expected_association}, but {author_association}"
        )

    # Raises exception if matching rule is not found
    find_matching_merge_rule(pr, repo)
    commit_sha = pr.get_merge_commit()
    if commit_sha is None:
        commits = repo.commits_resolving_gh_pr(pr.pr_num)
        if len(commits) == 0:
            raise RuntimeError("Can't find any commits resolving PR")
        commit_sha = commits[0]
    msg = repo.commit_message(commit_sha)
    rc = RE_DIFF_REV.search(msg)
    if rc is not None:
        raise RuntimeError(
            f"Can't revert PR that was landed via phabricator as {rc.group(1)}"
        )
    repo.checkout(pr.default_branch())
    repo.revert(commit_sha)
    msg = repo.commit_message("HEAD")
    msg = re.sub(RE_PULL_REQUEST_RESOLVED, "", msg)
    msg += f"\nReverted {pr.get_pr_url()} on behalf of @{author_login}\n"
    repo.amend_commit_message(msg)
    repo.push(pr.default_branch(), dry_run)
    if not dry_run:
        gh_add_labels(pr.org, pr.project, pr.pr_num, ["reverted"])
Example #3
0
 def merge_changes(self,
                   repo: GitRepo,
                   force: bool = False,
                   comment_id: Optional[int] = None,
                   branch: Optional[str] = None) -> None:
     branch_to_merge_into = self.default_branch(
     ) if branch is None else branch
     if repo.current_branch() != branch_to_merge_into:
         repo.checkout(branch_to_merge_into)
     if not self.is_ghstack_pr():
         msg = self.gen_commit_message()
         pr_branch_name = f"__pull-request-{self.pr_num}__init__"
         repo.fetch(f"pull/{self.pr_num}/head", pr_branch_name)
         repo._run_git("merge", "--squash", pr_branch_name)
         repo._run_git("commit", f"--author=\"{self.get_author()}\"", "-m",
                       msg)
     else:
         self.merge_ghstack_into(repo, force, comment_id=comment_id)
Example #4
0
def try_revert(repo: GitRepo, pr: GitHubPR, *,
               dry_run: bool = False,
               comment_id: Optional[int] = None,
               reason: Optional[str] = None) -> None:
    def post_comment(msg: str) -> None:
        gh_post_comment(pr.org, pr.project, pr.pr_num, msg, dry_run=dry_run)
    if not pr.is_closed():
        return post_comment(f"Can't revert open PR #{pr.pr_num}")
    comment = pr.get_last_comment() if comment_id is None else pr.get_comment_by_id(comment_id)
    if not RE_REVERT_CMD.match(comment.body_text) and not RE_REVERT_CMD_CLI.match(comment.body_text):
        raise RuntimeError(f"Comment {comment.body_text} does not seem to be a valid revert command")
    if comment.editor_login is not None:
        return post_comment("Don't want to revert based on edited command")
    author_association = comment.author_association
    author_login = comment.author_login
    # For some reason, one can not be a member of private repo, only CONTRIBUTOR
    expected_association = "CONTRIBUTOR" if pr.is_base_repo_private() else "MEMBER"
    if author_association != expected_association and author_association != "OWNER":
        return post_comment(f"Will not revert as @{author_login} is not a {expected_association}, but {author_association}")
    skip_internal_checks = can_skip_internal_checks(pr, comment_id)

    # Raises exception if matching rule is not found, but ignores all status checks
    find_matching_merge_rule(pr, repo, force=True, skip_internal_checks=skip_internal_checks)
    commit_sha = pr.get_merge_commit()
    if commit_sha is None:
        commits = repo.commits_resolving_gh_pr(pr.pr_num)
        if len(commits) == 0:
            raise RuntimeError("Can't find any commits resolving PR")
        commit_sha = commits[0]
    msg = repo.commit_message(commit_sha)
    rc = RE_DIFF_REV.search(msg)
    if rc is not None and not can_skip_internal_checks:
        raise RuntimeError(f"Can't revert PR that was landed via phabricator as {rc.group(1)}")
    repo.checkout(pr.default_branch())
    repo.revert(commit_sha)
    msg = repo.commit_message("HEAD")
    msg = re.sub(RE_PULL_REQUEST_RESOLVED, "", msg)
    msg += f"\nReverted {pr.get_pr_url()} on behalf of {prefix_with_github_url(author_login)}"
    msg += f" due to {reason}\n" if reason is not None else "\n"
    repo.amend_commit_message(msg)
    repo.push(pr.default_branch(), dry_run)
    if not dry_run:
        gh_add_labels(pr.org, pr.project, pr.pr_num, ["reverted"])
Example #5
0
    def merge_into(self, repo: GitRepo, *, force: bool = False, dry_run: bool = False, comment_id: Optional[int] = None) -> None:
        # Raises exception if matching rule is not found
        find_matching_merge_rule(self, repo, force=force, skip_internal_checks=can_skip_internal_checks(self, comment_id))
        if repo.current_branch() != self.default_branch():
            repo.checkout(self.default_branch())
        if not self.is_ghstack_pr():
            # Adding the url here makes it clickable within the Github UI
            approved_by_urls = ', '.join(prefix_with_github_url(login) for login in self.get_approved_by())
            msg = self.get_title() + f" (#{self.pr_num})\n\n" + self.get_body()
            msg += f"\nPull Request resolved: {self.get_pr_url()}\n"
            msg += f"Approved by: {approved_by_urls}\n"
            pr_branch_name = f"__pull-request-{self.pr_num}__init__"
            repo.fetch(f"pull/{self.pr_num}/head", pr_branch_name)
            repo._run_git("merge", "--squash", pr_branch_name)
            repo._run_git("commit", f"--author=\"{self.get_author()}\"", "-m", msg)
        else:
            self.merge_ghstack_into(repo, force, comment_id=comment_id)

        repo.push(self.default_branch(), dry_run)
        if not dry_run:
            gh_add_labels(self.org, self.project, self.pr_num, ["merged"])
Example #6
0
    def merge_into(self, repo: GitRepo, *, force: bool = False, dry_run: bool = False) -> None:
        # Raises exception if matching rule is not found
        find_matching_merge_rule(self, repo, force=force)
        if self.has_internal_changes():
            raise RuntimeError("This PR must be landed via phabricator")
        if repo.current_branch() != self.default_branch():
            repo.checkout(self.default_branch())
        if not self.is_ghstack_pr():
            # Adding the url here makes it clickable within the Github UI
            approved_by_urls = ', '.join(prefix_with_github_url(login) for login in self.get_approved_by())
            msg = self.get_title() + "\n\n" + self.get_body()
            msg += f"\nPull Request resolved: {self.get_pr_url()}\n"
            msg += f"Approved by: {approved_by_urls}\n"
            pr_branch_name = f"__pull-request-{self.pr_num}__init__"
            repo.fetch(f"pull/{self.pr_num}/head", pr_branch_name)
            repo._run_git("merge", "--squash", pr_branch_name)
            repo._run_git("commit", f"--author=\"{self.get_author()}\"", "-m", msg)
        else:
            self.merge_ghstack_into(repo, force)

        repo.push(self.default_branch(), dry_run)