Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement autorebase for PRs with multiple commits #473

Merged
merged 1 commit into from
Aug 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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!'));
mmarchini marked this conversation as resolved.
Show resolved Hide resolved
}

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