Skip to content

Commit

Permalink
feat: implement autorebase for PRs with multiple commits
Browse files Browse the repository at this point in the history
  • Loading branch information
lundibundi committed Aug 18, 2020
1 parent de6d1e2 commit 38dac57
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 9 deletions.
8 changes: 7 additions & 1 deletion components/git/land.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ const landOptions = {
describe: 'Prevent adding Fixes and Refs information to commit metadata',
default: false,
type: 'boolean'
},
autorebase: {
describe: 'Automatically rebase branches with multiple commits',
default: false,
type: 'boolean'
}
};

Expand Down Expand Up @@ -165,7 +170,8 @@ async function main(state, argv, cli, req, dir) {
cli.log('run `git node land --abort` before starting a new session');
return;
}
session = new LandingSession(cli, req, dir, argv.prid, argv.backport);
session = new LandingSession(cli, req, dir, argv.prid, argv.backport,
argv.autorebase);
const metadata = await getMetadata(session.argv, argv.skipRefs, cli);
if (argv.backport) {
const split = metadata.metadata.split('\n')[0];
Expand Down
49 changes: 41 additions & 8 deletions lib/landing_session.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@ const { shortSha } = require('./utils');
const isWindows = process.platform === 'win32';

class LandingSession extends Session {
constructor(cli, req, dir, prid, backport) {
constructor(cli, req, dir, prid, backport, autorebase) {
super(cli, dir, prid);
this.req = req;
this.backport = backport;
this.autorebase = autorebase;
}

get argv() {
const args = super.argv;
args.backport = this.backport;
args.autorebase = this.autorebase;
return args;
}

Expand Down Expand Up @@ -131,6 +133,17 @@ class LandingSession extends Session {
return command;
}

makeRebaseSuggestion(subjects) {
const suggestion = this.getRebaseSuggestion(subjects);
this.cli.log('Please run the following commands to complete landing\n\n' +
`$ ${suggestion}\n` +
'$ git node land --continue');
}

canAutomaticallyRebase(subjects) {
return subjects.every(line => !line.startsWith('squash!'));
}

async validateLint() {
// The linter is currently only run on non-Windows platforms.
if (os.platform() === 'win32') {
Expand Down Expand Up @@ -168,14 +181,34 @@ class LandingSession extends Session {
}

return this.final();
} else if (this.autorebase && this.canAutomaticallyRebase(subjects)) {
// Run git rebase in interactive mode with autosquash but without editor
// so that it will perform everything automatically.
cli.log(`There are ${subjects.length} commits in the PR. ` +
'Attempting autorebase.');
const { upstream, branch } = this;
const assumeYes = this.cli.assumeYes ? '--yes' : '';
const msgAmend = `-x "git node land --amend ${assumeYes}"`;
try {
await forceRunAsync('git',
['rebase', `${upstream}/${branch}`, '-i', '--autosquash', msgAmend],
{
ignoreFailure: false,
spawnArgs: {
shell: true,
env: { ...process.env, GIT_SEQUENCE_EDITOR: ':' }
}
});
return this.final();
} catch (e) {
await runAsync('git', ['rebase', '--abort']);
const count = subjects.length;
cli.log(`Couldn't rebase ${count} commits in the PR automatically`);
this.makeRebaseSuggestion(subjects);
}
} else {
this.makeRebaseSuggestion(subjects);
}

const suggestion = this.getRebaseSuggestion(subjects);

cli.log(`There are ${subjects.length} commits in the PR`);
cli.log('Please run the following commands to complete landing\n\n' +
`$ ${suggestion}\n` +
'$ git node land --continue');
}

async apply() {
Expand Down

0 comments on commit 38dac57

Please sign in to comment.