Skip to content

Commit 600ab8f

Browse files
committed
feat: implement autorebase for PRs with multiple commits
1 parent 2c293b0 commit 600ab8f

File tree

2 files changed

+35
-9
lines changed

2 files changed

+35
-9
lines changed

components/git/land.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ const landOptions = {
4242
describe: 'Land a backport PR onto a staging branch',
4343
default: false,
4444
type: 'boolean'
45+
},
46+
autorebase: {
47+
describe: 'Automatically rebase branches with multiple commits',
48+
default: false,
49+
type: 'boolean'
4550
}
4651
};
4752

@@ -159,7 +164,8 @@ async function main(state, argv, cli, req, dir) {
159164
cli.log('run `git node land --abort` before starting a new session');
160165
return;
161166
}
162-
session = new LandingSession(cli, req, dir, argv.prid, argv.backport);
167+
session = new LandingSession(cli, req, dir, argv.prid, argv.backport,
168+
argv.autorebase);
163169
const metadata = await getMetadata(session.argv, cli);
164170
if (argv.backport) {
165171
const split = metadata.metadata.split('\n')[0];

lib/landing_session.js

+28-8
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,17 @@ const { shortSha } = require('./utils');
1515
const isWindows = process.platform === 'win32';
1616

1717
class LandingSession extends Session {
18-
constructor(cli, req, dir, prid, backport) {
18+
constructor(cli, req, dir, prid, backport, autorebase) {
1919
super(cli, dir, prid);
2020
this.req = req;
2121
this.backport = backport;
22+
this.autorebase = autorebase;
2223
}
2324

2425
get argv() {
2526
const args = super.argv;
2627
args.backport = this.backport;
28+
args.autorebase = this.autorebase;
2729
return args;
2830
}
2931

@@ -130,6 +132,10 @@ class LandingSession extends Session {
130132
return command;
131133
}
132134

135+
canAutomaticallyRebase(subjects) {
136+
return subjects.every(line => !line.startsWith('squash!'));
137+
}
138+
133139
async suggestAfterPatch(patch) {
134140
const { cli } = this;
135141
const subjects = patch.match(/Subject: \[PATCH.*?\].*/g);
@@ -155,14 +161,28 @@ class LandingSession extends Session {
155161
return;
156162
}
157163
return this.final();
158-
}
159-
160-
const suggestion = this.getRebaseSuggestion(subjects);
161-
162-
cli.log(`There are ${subjects.length} commits in the PR`);
163-
cli.log('Please run the following commands to complete landing\n\n' +
164-
`$ ${suggestion}\n` +
164+
} else if (this.autorebase && this.canAutomaticallyRebase(subjects)) {
165+
// Run git rebase in interactive mode with autosquash but without editor
166+
// so that it will perform everything automatically.
167+
cli.log(`There are ${subjects.length} commits in the PR. ` +
168+
'Attempring autorebase.');
169+
const { upstream, branch } = this;
170+
const msgAmend = '-x "git-node land --amend"';
171+
await runAsync('git',
172+
['rebase', `${upstream}/${branch}`, '-i', '--autosquash', msgAmend],
173+
{
174+
spawnArgs: {
175+
shell: true,
176+
env: { ...process.env, GIT_SEQUENCE_EDITOR: ':' }
177+
}
178+
});
179+
} else {
180+
const suggestion = this.getRebaseSuggestion(subjects);
181+
cli.log(`There are ${subjects.length} commits in the PR`);
182+
cli.log('Please run the following commands to complete landing\n\n' +
183+
`$ ${suggestion}\n` +
165184
'$ git node land --continue');
185+
}
166186
}
167187

168188
async apply() {

0 commit comments

Comments
 (0)