1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
use regex::Regex;
use crate::graph::FlatDep;
use crate::api::search::PullRequestStatus;
fn process_ref(git_ref: &str) -> String {
let re = Regex::new("heap:").unwrap();
re.replace_all(git_ref, "").into_owned()
}
pub fn generate_rebase_script(deps: FlatDep) -> String {
let deps = deps.iter().filter(|(dep, _)| {
*dep.state() == PullRequestStatus::Open
}).collect::<Vec<_>>();
let mut out = String::new();
out.push_str("#!/usr/bin/env bash\n\n");
out.push_str("set -euo pipefail\n");
out.push_str("set -o xtrace\n\n");
out.push_str("# ------ THIS SCRIPT ASSUMES YOUR PR STACK IS A SINGLE CHAIN WITHOUT BRANCHING ----- #\n\n");
out.push_str("# It starts at the base of the stack, cherry-picking onto the new base and force-pushing as it goes.\n");
out.push_str("# We can't tell where the initial cherry-pick should stop (mainly because of our squash merge workflow),\n");
out.push_str("# so that initial stopping point for the first PR needs to be specified manually.\n\n");
out.push_str("export PREBASE=\"<enter a marker to stop the initial cherry-pick at>\"\n");
for (from, to) in deps {
let to = if let Some(pr) = to {
pr.head().to_string()
} else {
String::from("<enter a ref to rebase the stack on; usually `develop`>")
};
out.push_str("\n# -------------- #\n\n");
out.push_str(&format!("export TO=\"{}\"\n", process_ref(&to)));
out.push_str(&format!("export FROM=\"{}\"\n\n", process_ref(from.head())));
out.push_str("git checkout heap/\"$TO\"\n");
out.push_str("git cherry-pick \"$PREBASE\"..heap/\"$FROM\"\n");
out.push_str("export PREBASE=\"$(git rev-parse --verify heap/$FROM)\"\n");
out.push_str("git push -f heap HEAD:refs/heads/\"$FROM\"\n");
}
out
}