diff --git a/lib/landing_session.js b/lib/landing_session.js index 15b67ed7..a8a179ae 100644 --- a/lib/landing_session.js +++ b/lib/landing_session.js @@ -82,12 +82,38 @@ export default class LandingSession extends Session { } async downloadAndPatch() { - const { cli, upstream, prid, expectedCommitShas } = this; + const { cli, upstream, prid, expectedCommitShas, crossRepoPR } = this; + + if (crossRepoPR) { + const { owner, repo } = this; + cli.warn(`The configured remote "${upstream}" does not point to the ` + + 'Pull Request repository.'); + const isHeadAMergeCommit = () => { + try { + return runSync('git', ['rev-parse', 'FETCH_HEAD^2']).trim(); + } catch {} + }; + do { + cli.warn('Fetching commits from a different upstream might require user intervention.'); + + cli.info('Run the following command to fetch the commit(s):'); + cli.info(`git fetch git@github.com:${owner}/${repo}.git refs/pull/${prid}/merge`); + const response = await cli.prompt('Should NCU run the command for you?', + { defaultAnswer: false }); + if (response) { + cli.startSpinner(`Downloading patch for ${prid}`); + await forceRunAsync('git', + ['fetch', `git@github.com:${owner}/${repo}.git`, `refs/pull/${prid}/merge`], + { ignoreFailure: false }); + } + } while (!isHeadAMergeCommit()); + } else { + cli.startSpinner(`Downloading patch for ${prid}`); + await runAsync('git', [ + 'fetch', upstream, + `refs/pull/${prid}/merge`]); + } - cli.startSpinner(`Downloading patch for ${prid}`); - await runAsync('git', [ - 'fetch', upstream, - `refs/pull/${prid}/merge`]); // We fetched the commit that would result if we used `git merge`. // ^1 and ^2 refer to the PR base and the PR head, respectively. const [base, head] = await runAsync('git', @@ -472,7 +498,7 @@ export default class LandingSession extends Session { } cli.separator(); - cli.log('The following commits are ready to be pushed to ' + + cli.log('The following commits are landable on ' + `${upstream}/${branch}`); cli.log(`- ${strayVerbose.join('\n- ')}`); cli.separator(); @@ -483,8 +509,15 @@ export default class LandingSession extends Session { willBeLanded = `${head}...${willBeLanded}`; } + cli.startSpinner('Removing temporary files'); this.cleanFiles(); - cli.log('Temporary files removed.'); + cli.stopSpinner('Temporary files removed.'); + if (this.crossRepoPR) { + cli.warn(`The configured remote "${upstream}" does not point to the ` + + 'Pull Request repository.'); + cli.info(`${willBeLanded} should likely not be pushed to "${upstream}"`); + return; + } cli.log('To finish landing:'); cli.log('1. Run: '); cli.log(` git push ${upstream} ${branch}`); diff --git a/lib/session.js b/lib/session.js index 4c2dacf3..088d168b 100644 --- a/lib/session.js +++ b/lib/session.js @@ -33,10 +33,11 @@ export default class Session { const upstreamHref = runSync('git', [ 'config', '--get', `remote.${upstream}.url`]).trim(); - if (!new RegExp(`${owner}/${repo}(?:.git)?$`).test(upstreamHref)) { + if (!upstreamHref.endsWith(`${owner}/${repo}.git`) && + !upstreamHref.endsWith(`${owner}/${repo}`)) { cli.warn('Remote repository URL does not point to the expected ' + `repository ${owner}/${repo}`); - cli.setExitCode(1); + this.crossRepoPR = true; } } }