Skip to content

Commit 2254e7e

Browse files
author
Stephan Dilly
committed
rebase state
1 parent 07f5d69 commit 2254e7e

File tree

5 files changed

+207
-21
lines changed

5 files changed

+207
-21
lines changed

asyncgit/src/sync/merge.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
use crate::{
22
error::{Error, Result},
33
sync::{
4-
branch::merge_commit::commit_merge_with_head, reset_stage,
5-
reset_workdir, utils, CommitId,
4+
branch::merge_commit::commit_merge_with_head,
5+
rebase::get_rebase_progress, reset_stage, reset_workdir,
6+
utils, CommitId,
67
},
78
};
89
use git2::{BranchType, Commit, MergeOptions, Repository};
910
use scopetime::scope_time;
1011

11-
use super::rebase::conflict_free_rebase;
12+
use super::rebase::{rebase, RebaseProgress, RebaseState};
1213

1314
///
1415
pub fn mergehead_ids(repo_path: &str) -> Result<Vec<CommitId>> {
@@ -57,14 +58,23 @@ pub fn merge_branch(repo_path: &str, branch: &str) -> Result<()> {
5758
pub fn rebase_branch(
5859
repo_path: &str,
5960
branch: &str,
60-
) -> Result<CommitId> {
61+
) -> Result<RebaseState> {
6162
scope_time!("rebase_branch");
6263

6364
let repo = utils::repo(repo_path)?;
6465

6566
rebase_branch_repo(&repo, branch)
6667
}
6768

69+
///
70+
pub fn rebase_progress(repo_path: &str) -> Result<RebaseProgress> {
71+
scope_time!("rebase_progress");
72+
73+
let repo = utils::repo(repo_path)?;
74+
75+
get_rebase_progress(&repo)
76+
}
77+
6878
///
6979
pub fn merge_branch_repo(
7080
repo: &Repository,
@@ -93,13 +103,13 @@ pub fn merge_branch_repo(
93103
pub fn rebase_branch_repo(
94104
repo: &Repository,
95105
branch_name: &str,
96-
) -> Result<CommitId> {
106+
) -> Result<RebaseState> {
97107
let branch = repo.find_branch(branch_name, BranchType::Local)?;
98108

99109
let annotated =
100110
repo.reference_to_annotated_commit(&branch.into_reference())?;
101111

102-
conflict_free_rebase(repo, &annotated)
112+
rebase(repo, &annotated)
103113
}
104114

105115
///

asyncgit/src/sync/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ pub use ignore::add_to_ignore;
5959
pub use logwalker::{LogWalker, LogWalkerFilter};
6060
pub use merge::{
6161
abort_merge, merge_branch, merge_commit, merge_msg,
62-
mergehead_ids, rebase_branch,
62+
mergehead_ids, rebase_branch, rebase_progress,
6363
};
6464
pub use remotes::{
6565
get_default_remote, get_remotes, push::AsyncProgress,

asyncgit/src/sync/rebase.rs

Lines changed: 153 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,89 @@ pub fn conflict_free_rebase(
3737
})
3838
}
3939

40+
///
41+
#[derive(PartialEq, Debug)]
42+
pub enum RebaseState {
43+
///
44+
Finished,
45+
///
46+
Conflicted,
47+
}
48+
49+
/// rebase
50+
#[allow(dead_code)]
51+
pub fn rebase(
52+
repo: &git2::Repository,
53+
commit: &git2::AnnotatedCommit,
54+
) -> Result<RebaseState> {
55+
let mut rebase = repo.rebase(None, Some(commit), None, None)?;
56+
let signature =
57+
crate::sync::commit::signature_allow_undefined_name(repo)?;
58+
59+
while let Some(op) = rebase.next() {
60+
let _op = op?;
61+
// dbg!(op.id());
62+
63+
if repo.index()?.has_conflicts() {
64+
return Ok(RebaseState::Conflicted);
65+
}
66+
67+
rebase.commit(None, &signature, None)?;
68+
}
69+
70+
if repo.index()?.has_conflicts() {
71+
return Ok(RebaseState::Conflicted);
72+
}
73+
74+
rebase.finish(Some(&signature))?;
75+
76+
Ok(RebaseState::Finished)
77+
}
78+
79+
///
80+
#[derive(PartialEq, Debug)]
81+
pub struct RebaseProgress {
82+
///
83+
pub steps: usize,
84+
///
85+
pub current: usize,
86+
}
87+
88+
///
89+
#[allow(dead_code)]
90+
pub fn get_rebase_progress(
91+
repo: &git2::Repository,
92+
) -> Result<RebaseProgress> {
93+
let mut rebase = repo.open_rebase(None)?;
94+
95+
let progress = RebaseProgress {
96+
steps: rebase.len(),
97+
current: rebase.operation_current().unwrap_or_default(),
98+
};
99+
100+
Ok(progress)
101+
}
102+
103+
///
104+
#[allow(dead_code)]
105+
pub fn abort_rebase(repo: &git2::Repository) -> Result<()> {
106+
let mut rebase = repo.open_rebase(None)?;
107+
108+
rebase.abort()?;
109+
110+
Ok(())
111+
}
112+
40113
#[cfg(test)]
41-
mod tests {
114+
mod test_conflict_free_rebase {
42115
use crate::sync::{
43-
checkout_branch, create_branch, rebase_branch, repo_state,
116+
checkout_branch, create_branch,
117+
rebase::{conflict_free_rebase, RebaseState},
118+
rebase_branch, repo_state,
44119
tests::{repo_init, write_commit_file},
45-
CommitId, RepoState,
120+
utils, CommitId, RepoState,
46121
};
47-
use git2::Repository;
122+
use git2::{BranchType, Repository};
48123

49124
fn parent_ids(repo: &Repository, c: CommitId) -> Vec<CommitId> {
50125
let foo = repo
@@ -57,6 +132,23 @@ mod tests {
57132
foo
58133
}
59134

135+
///
136+
fn test_rebase_branch_repo(
137+
repo_path: &str,
138+
branch_name: &str,
139+
) -> CommitId {
140+
let repo = utils::repo(repo_path).unwrap();
141+
142+
let branch =
143+
repo.find_branch(branch_name, BranchType::Local).unwrap();
144+
145+
let annotated = repo
146+
.reference_to_annotated_commit(&branch.into_reference())
147+
.unwrap();
148+
149+
conflict_free_rebase(&repo, &annotated).unwrap()
150+
}
151+
60152
#[test]
61153
fn test_smoke() {
62154
let (_td, repo) = repo_init().unwrap();
@@ -80,7 +172,7 @@ mod tests {
80172

81173
checkout_branch(repo_path, "refs/heads/foo").unwrap();
82174

83-
let r = rebase_branch(repo_path, "master").unwrap();
175+
let r = test_rebase_branch_repo(repo_path, "master");
84176

85177
assert_eq!(parent_ids(&repo, r), vec![c3]);
86178
}
@@ -105,7 +197,62 @@ mod tests {
105197

106198
let res = rebase_branch(repo_path, "master");
107199

108-
assert!(res.is_err());
200+
assert!(matches!(res.unwrap(), RebaseState::Conflicted));
201+
202+
assert_eq!(repo_state(repo_path).unwrap(), RepoState::Rebase);
203+
}
204+
}
205+
206+
#[cfg(test)]
207+
mod test_rebase {
208+
use crate::sync::{
209+
checkout_branch, create_branch,
210+
rebase::{
211+
abort_rebase, get_rebase_progress, RebaseProgress,
212+
RebaseState,
213+
},
214+
rebase_branch, repo_state,
215+
tests::{repo_init, write_commit_file},
216+
RepoState,
217+
};
218+
219+
#[test]
220+
fn test_conflicted_abort() {
221+
let (_td, repo) = repo_init().unwrap();
222+
let root = repo.path().parent().unwrap();
223+
let repo_path = root.as_os_str().to_str().unwrap();
224+
225+
write_commit_file(&repo, "test.txt", "test1", "commit1");
226+
227+
create_branch(repo_path, "foo").unwrap();
228+
229+
write_commit_file(&repo, "test.txt", "test2", "commit2");
230+
231+
checkout_branch(repo_path, "refs/heads/master").unwrap();
232+
233+
write_commit_file(&repo, "test.txt", "test3", "commit3");
234+
235+
checkout_branch(repo_path, "refs/heads/foo").unwrap();
236+
237+
assert!(get_rebase_progress(&repo).is_err());
238+
239+
// rebase
240+
241+
let r = rebase_branch(repo_path, "master").unwrap();
242+
243+
assert_eq!(r, RebaseState::Conflicted);
244+
assert_eq!(repo_state(repo_path).unwrap(), RepoState::Rebase);
245+
assert_eq!(
246+
get_rebase_progress(&repo).unwrap(),
247+
RebaseProgress {
248+
current: 0,
249+
steps: 1
250+
}
251+
);
252+
253+
// abort
254+
255+
abort_rebase(&repo).unwrap();
109256

110257
assert_eq!(repo_state(repo_path).unwrap(), RepoState::Clean);
111258
}

asyncgit/src/sync/state.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ pub enum RepoState {
1010
///
1111
Merge,
1212
///
13+
Rebase,
14+
///
1315
Other,
1416
}
1517

@@ -18,6 +20,7 @@ impl From<RepositoryState> for RepoState {
1820
match state {
1921
RepositoryState::Clean => Self::Clean,
2022
RepositoryState::Merge => Self::Merge,
23+
RepositoryState::RebaseMerge => Self::Rebase,
2124
_ => Self::Other,
2225
}
2326
}
@@ -29,5 +32,9 @@ pub fn repo_state(repo_path: &str) -> Result<RepoState> {
2932

3033
let repo = utils::repo(repo_path)?;
3134

32-
Ok(repo.state().into())
35+
let state = repo.state();
36+
37+
// dbg!(&state);
38+
39+
Ok(state.into())
3340
}

src/tabs/status.rs

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -215,14 +215,12 @@ impl Status {
215215
}
216216
}
217217

218-
fn draw_repo_state<B: tui::backend::Backend>(
219-
f: &mut tui::Frame<B>,
220-
r: tui::layout::Rect,
221-
) -> Result<()> {
222-
if let Ok(state) = sync::repo_state(CWD) {
223-
if state != RepoState::Clean {
218+
fn repo_state_text(state: RepoState) -> String {
219+
match state {
220+
RepoState::Merge => {
224221
let ids =
225222
sync::mergehead_ids(CWD).unwrap_or_default();
223+
226224
let ids = format!(
227225
"({})",
228226
ids.iter()
@@ -231,7 +229,31 @@ impl Status {
231229
))
232230
.join(",")
233231
);
234-
let txt = format!("{:?} {}", state, ids);
232+
233+
format!("{:?} {}", state, ids)
234+
}
235+
RepoState::Rebase => {
236+
let progress =
237+
if let Ok(p) = sync::rebase_progress(CWD) {
238+
format!("{}/{}", p.current + 1, p.steps)
239+
} else {
240+
String::new()
241+
};
242+
243+
format!("{:?} ({})", state, progress)
244+
}
245+
_ => format!("{:?}", state),
246+
}
247+
}
248+
249+
fn draw_repo_state<B: tui::backend::Backend>(
250+
f: &mut tui::Frame<B>,
251+
r: tui::layout::Rect,
252+
) -> Result<()> {
253+
if let Ok(state) = sync::repo_state(CWD) {
254+
if state != RepoState::Clean {
255+
let txt = Self::repo_state_text(state);
256+
235257
let txt_len = u16::try_from(txt.len())?;
236258
let w = Paragraph::new(txt)
237259
.style(Style::default().fg(Color::Red))

0 commit comments

Comments
 (0)