Skip to content

Commit f6c91a0

Browse files
committed
Add the ability to specify an upstream
1 parent 48e1ec9 commit f6c91a0

File tree

1 file changed

+65
-26
lines changed

1 file changed

+65
-26
lines changed

src/main.rs

Lines changed: 65 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ use std::process::Command;
1818

1919
use console::style;
2020
use dialoguer::{Confirmation, Select};
21-
use git2::{Branch, Commit, Diff, Repository};
21+
use git2::{Branch, Commit, Diff, Object, ObjectType, Oid, Repository};
2222
use structopt::StructOpt;
2323

24+
const UPSTREAM_VAR: &str = "GIT_INSTAFIX_UPSTREAM";
25+
2426
#[derive(StructOpt, Debug)]
2527
#[structopt(
2628
about = "Fix a commit in your history with your currently-staged changes",
@@ -69,34 +71,71 @@ fn main() {
6971

7072
fn run(squash: bool, max_commits: usize) -> Result<(), Box<dyn Error>> {
7173
let repo = Repository::open(".")?;
72-
match repo.head() {
73-
Ok(head) => {
74-
let head_tree = head.peel_to_tree()?;
75-
let head_branch = Branch::wrap(head);
76-
let diff = repo.diff_tree_to_index(Some(&head_tree), None, None)?;
77-
let commit_to_amend =
78-
create_fixup_commit(&repo, &head_branch, &diff, squash, max_commits)?;
79-
println!(
80-
"selected: {} {}",
81-
&commit_to_amend.id().to_string()[0..10],
82-
commit_to_amend.summary().unwrap_or("")
83-
);
84-
// do the rebase
85-
let target_id = format!("{}~", commit_to_amend.id());
86-
Command::new("git")
87-
.args(&["rebase", "--interactive", "--autosquash", &target_id])
88-
.env("GIT_SEQUENCE_EDITOR", "true")
89-
.spawn()?
90-
.wait()?;
91-
}
92-
Err(e) => return Err(format!("head is not pointing at a valid branch: {}", e).into()),
93-
};
74+
let head = repo
75+
.head()
76+
.map_err(|e| format!("HEAD is not pointing at a valid branch: {}", e))?;
77+
let head_tree = head.peel_to_tree()?;
78+
let head_branch = Branch::wrap(head);
79+
let diff = repo.diff_tree_to_index(Some(&head_tree), None, None)?;
80+
let upstream = get_upstream(&repo, &head_branch)?;
81+
let commit_to_amend =
82+
create_fixup_commit(&repo, &head_branch, &upstream, &diff, squash, max_commits)?;
83+
println!(
84+
"selected: {} {}",
85+
&commit_to_amend.id().to_string()[0..10],
86+
commit_to_amend.summary().unwrap_or("")
87+
);
88+
// do the rebase
89+
let target_id = format!("{}~", commit_to_amend.id());
90+
Command::new("git")
91+
.args(&["rebase", "--interactive", "--autosquash", &target_id])
92+
.env("GIT_SEQUENCE_EDITOR", "true")
93+
.spawn()?
94+
.wait()?;
9495
Ok(())
9596
}
9697

98+
fn get_upstream<'a>(
99+
repo: &'a Repository,
100+
head_branch: &'a Branch,
101+
) -> Result<Object<'a>, Box<dyn Error>> {
102+
let upstream = if let Ok(upstream_name) = env::var(UPSTREAM_VAR) {
103+
let branch = repo
104+
.branches(None)?
105+
.filter_map(|branch| branch.ok().map(|(b, _type)| b))
106+
.find(|b| {
107+
b.name()
108+
.map(|n| n.expect("valid utf8 branchname") == &upstream_name)
109+
.unwrap_or(false)
110+
})
111+
.ok_or_else(|| format!("cannot find branch with name {:?}", upstream_name))?;
112+
let result = Command::new("git")
113+
.args(&[
114+
"merge-base",
115+
head_branch.name().unwrap().unwrap(),
116+
branch.name().unwrap().unwrap(),
117+
])
118+
.output()?
119+
.stdout;
120+
let oid = Oid::from_str(std::str::from_utf8(&result)?.trim())?;
121+
let commit = repo.find_object(oid, None).unwrap();
122+
123+
commit
124+
} else {
125+
head_branch
126+
.upstream()
127+
.unwrap()
128+
.into_reference()
129+
.peel(ObjectType::Commit)?
130+
};
131+
132+
Ok(upstream)
133+
}
134+
97135
fn create_fixup_commit<'a>(
98136
repo: &'a Repository,
99137
head_branch: &'a Branch,
138+
upstream: &'a Object,
100139
diff: &'a Diff,
101140
squash: bool,
102141
max_commits: usize,
@@ -120,7 +159,7 @@ fn create_fixup_commit<'a>(
120159
println!("Staged changes:");
121160
print_diff(Changes::Staged)?;
122161
}
123-
let commit_to_amend = select_commit_to_amend(&repo, head_branch.upstream().ok(), max_commits)?;
162+
let commit_to_amend = select_commit_to_amend(&repo, Some(upstream), max_commits)?;
124163
do_fixup_commit(&repo, &head_branch, &commit_to_amend, squash)?;
125164
Ok(commit_to_amend)
126165
}
@@ -147,13 +186,13 @@ fn do_fixup_commit<'a>(
147186

148187
fn select_commit_to_amend<'a>(
149188
repo: &'a Repository,
150-
upstream: Option<Branch<'a>>,
189+
upstream: Option<&Object<'a>>,
151190
max_commits: usize,
152191
) -> Result<Commit<'a>, Box<dyn Error>> {
153192
let mut walker = repo.revwalk()?;
154193
walker.push_head()?;
155194
let commits = if let Some(upstream) = upstream {
156-
let upstream_oid = upstream.get().target().expect("No upstream target");
195+
let upstream_oid = upstream.id();
157196
walker
158197
.flat_map(|r| r)
159198
.take_while(|rev| *rev != upstream_oid)

0 commit comments

Comments
 (0)