ralph_workflow/git_helpers/
rebase_continuation.rs1#[cfg(any(test, feature = "test-utils"))]
27pub fn verify_rebase_completed(upstream_branch: &str) -> io::Result<bool> {
28 let repo = git2::Repository::discover(".").map_err(|e| git2_to_io_error(&e))?;
29
30 let state = repo.state();
32 if state == git2::RepositoryState::Rebase
33 || state == git2::RepositoryState::RebaseMerge
34 || state == git2::RepositoryState::RebaseInteractive
35 {
36 return Ok(false);
37 }
38
39 let index = repo.index().map_err(|e| git2_to_io_error(&e))?;
41 if index.has_conflicts() {
42 return Ok(false);
43 }
44
45 let head = repo.head().map_err(|e| {
47 io::Error::new(
48 io::ErrorKind::InvalidData,
49 format!("Repository HEAD is invalid: {e}"),
50 )
51 })?;
52
53 if let Ok(head_commit) = head.peel_to_commit() {
55 if let Ok(upstream_object) = repo.revparse_single(upstream_branch) {
56 if let Ok(upstream_commit) = upstream_object.peel_to_commit() {
57 match repo.graph_descendant_of(head_commit.id(), upstream_commit.id()) {
58 Ok(is_descendant) => {
59 if is_descendant {
60 return Ok(true);
61 }
62 return Ok(false);
63 }
64 Err(e) => {
65 let _ = e;
66 }
67 }
68 }
69 }
70 }
71
72 Ok(!index.has_conflicts())
73}
74
75pub fn continue_rebase(executor: &dyn crate::executor::ProcessExecutor) -> io::Result<()> {
83 let repo = git2::Repository::discover(".").map_err(|e| git2_to_io_error(&e))?;
84 continue_rebase_impl(&repo, executor)
85}
86
87fn continue_rebase_impl(
89 repo: &git2::Repository,
90 executor: &dyn crate::executor::ProcessExecutor,
91) -> io::Result<()> {
92 if !rebase_in_progress_impl(repo) {
93 return Err(no_rebase_in_progress_error());
94 }
95
96 let conflicted = get_conflicted_files()?;
97 if !conflicted.is_empty() {
98 return Err(conflict_remains_error(conflicted.len()));
99 }
100
101 let output = executor.execute("git", &["rebase", "--continue"], &[], None)?;
102
103 if output.succeeded() {
104 Ok(())
105 } else {
106 Err(io::Error::other(format!(
107 "Failed to continue rebase: {}",
108 output.stderr
109 )))
110 }
111}
112
113fn no_rebase_in_progress_error() -> io::Error {
114 io::Error::new(io::ErrorKind::InvalidInput, "No rebase in progress")
115}
116
117fn conflict_remains_error(count: usize) -> io::Error {
118 io::Error::new(
119 io::ErrorKind::InvalidInput,
120 format!(
121 "Cannot continue rebase: {} file(s) still have conflicts",
122 count
123 ),
124 )
125}
126
127pub fn rebase_in_progress() -> io::Result<bool> {
133 let repo = git2::Repository::discover(".").map_err(|e| git2_to_io_error(&e))?;
134 Ok(rebase_in_progress_impl(&repo))
135}
136
137fn rebase_in_progress_impl(repo: &git2::Repository) -> bool {
139 let state = repo.state();
140 state == git2::RepositoryState::Rebase
141 || state == git2::RepositoryState::RebaseMerge
142 || state == git2::RepositoryState::RebaseInteractive
143}