(
prNumber: number,
opts: PullRequestCheckoutOptions = {},
)
| 63 | * to modify a pull request. Skipped if `allowIfMaintainerCannotModify` is set. |
| 64 | */ |
| 65 | export async function checkOutPullRequestLocally( |
| 66 | prNumber: number, |
| 67 | opts: PullRequestCheckoutOptions = {}, |
| 68 | ) { |
| 69 | /** The singleton instance of the authenticated git client. */ |
| 70 | const git = await AuthenticatedGitClient.get(); |
| 71 | |
| 72 | // In order to preserve local changes, checkouts cannot occur if local changes are present in the |
| 73 | // git environment. Checked before retrieving the PR to fail fast. |
| 74 | if (git.hasUncommittedChanges()) { |
| 75 | throw new UnexpectedLocalChangesError('Unable to checkout PR due to uncommitted changes.'); |
| 76 | } |
| 77 | |
| 78 | /** |
| 79 | * The branch or revision originally checked out before this method performed |
| 80 | * any Git operations that may change the working branch. |
| 81 | */ |
| 82 | const previousBranchOrRevision = git.getCurrentBranchOrRevision(); |
| 83 | /** The PR information from Github. */ |
| 84 | const pr = await getPr(PR_SCHEMA, prNumber, git); |
| 85 | |
| 86 | if (pr === null) { |
| 87 | throw new PullRequestNotFoundError(`Pull request #${prNumber} could not be found.`); |
| 88 | } |
| 89 | |
| 90 | /** The branch name of the PR from the repository the PR came from. */ |
| 91 | const headRefName = pr.headRef.name; |
| 92 | /** The full URL path of the repository the PR came from with github token as authentication. */ |
| 93 | const headRefUrl = addTokenToGitHttpsUrl(pr.headRef.repository.url, git.githubToken); |
| 94 | // Note: Since we use a detached head for rebasing the PR and therefore do not have |
| 95 | // remote-tracking branches configured, we need to set our expected ref and SHA. This |
| 96 | // allows us to use `--force-with-lease` for the detached head while ensuring that we |
| 97 | // never accidentally override upstream changes that have been pushed in the meanwhile. |
| 98 | // See: |
| 99 | // https://git-scm.com/docs/git-push#Documentation/git-push.txt---force-with-leaseltrefnamegtltexpectgt |
| 100 | /** Flag for a force push with lease back to upstream. */ |
| 101 | const forceWithLeaseFlag = `--force-with-lease=${headRefName}:${pr.headRefOid}`; |
| 102 | const escapedHeadRefName = `'${headRefName.replace(/'/g, "'\\''")}'`; |
| 103 | const escapedForceWithLeaseFlag = `--force-with-lease=${escapedHeadRefName}:${pr.headRefOid}`; |
| 104 | |
| 105 | // If the PR does not allow maintainers to modify it, exit as the rebased PR cannot |
| 106 | // be pushed up. |
| 107 | if (!pr.maintainerCanModify && !pr.viewerDidAuthor && !opts.allowIfMaintainerCannotModify) { |
| 108 | throw new MaintainerModifyAccessError('PR is not set to allow maintainers to modify the PR'); |
| 109 | } |
| 110 | |
| 111 | try { |
| 112 | // Fetch the branch at the commit of the PR, and check it out in a detached state. |
| 113 | git.run(['fetch', '-q', headRefUrl, '--', headRefName]); |
| 114 | git.run(['checkout', '--detach', 'FETCH_HEAD']); |
| 115 | } catch (e) { |
| 116 | git.checkout(previousBranchOrRevision, true); |
| 117 | throw e; |
| 118 | } |
| 119 | |
| 120 | return { |
| 121 | /** |
| 122 | * Pushes the current local branch to the PR on the upstream repository. |
no test coverage detected