Skip to content

Commit

Permalink
Copy with user running git rebase --continue before `git gr restack…
Browse files Browse the repository at this point in the history
… continue`
  • Loading branch information
9999years committed Apr 6, 2024
1 parent 7e466e4 commit 3269054
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 19 deletions.
20 changes: 19 additions & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use camino::Utf8PathBuf;
use clap::Args;
use clap::Parser;
use clap::Subcommand;
use reqwest::Method;

use crate::change_number::ChangeNumber;
use crate::commit_hash::CommitHash;
use crate::endpoint::Endpoint;
use crate::patchset::Patchset;

Expand Down Expand Up @@ -136,7 +138,7 @@ pub enum Restack {
/// Restack only the currently checked-out CL on its immediate ancestor.
This,
/// Continue an in-progress restack.
Continue,
Continue(RestackContinue),
/// Abort an in-progress restack.
Abort,
/// Push changes from a completed restack.
Expand All @@ -149,3 +151,19 @@ pub enum Restack {
git_rebase_todo: Utf8PathBuf,
},
}

#[derive(Debug, Clone, Args)]
pub struct RestackContinue {
/// If you ran `git rebase --continue` on your own and then checked something else out,
/// `git-gr` will not be able to determine the new commit hash for the in-progress restack
/// step. Use this flag to supply it manually.
#[arg(long)]
pub in_progress_commit: Option<CommitHash>,

/// If you ran `git rebase --continue` on your own and then checked something else out,
/// `git-gr` will not be able to determine the new commit hash for the in-progress restack
/// step. Use this flag to restart the in-progress restack step, abandoning any changes you
/// may have made.
#[arg(long)]
pub restart_in_progress: bool,
}
13 changes: 9 additions & 4 deletions src/gerrit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use crate::change::Change;
use crate::change::TimestampFormat;
use crate::change_key::ChangeKey;
use crate::change_number::ChangeNumber;
use crate::cli::RestackContinue;
use crate::commit_hash::CommitHash;
use crate::current_exe::current_exe;
use crate::dependency_graph::DependencyGraph;
Expand Down Expand Up @@ -632,12 +633,16 @@ impl GerritGitRemote {
Ok(())
}

pub fn restack(&mut self, branch: &str) -> miette::Result<()> {
restack(self, branch)
pub fn restack(
&mut self,
branch: &str,
options: Option<RestackContinue>,
) -> miette::Result<()> {
restack(self, branch, options)
}

pub fn restack_continue(&mut self) -> miette::Result<()> {
self.restack("HEAD")
pub fn restack_continue(&mut self, options: RestackContinue) -> miette::Result<()> {
self.restack("HEAD", Some(options))
}

pub fn restack_push(&self) -> miette::Result<()> {
Expand Down
7 changes: 7 additions & 0 deletions src/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,13 @@ impl Git {
Ok(())
}

/// Determine if a rebase is currently in progress.
pub fn rebase_in_progress(&self) -> miette::Result<bool> {
let git_dir = self.get_git_dir()?;
let rebase_dir = git_dir.join("rebase-merge");
Ok(rebase_dir.exists())
}

pub fn fetch(&self, remote: &str) -> miette::Result<()> {
self.command()
.args(["fetch", remote])
Expand Down
8 changes: 4 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ fn main() -> miette::Result<()> {
let todo = create_todo(&mut gerrit, branch_str)?;
todo.write(&git)?;
gerrit.push(branch.clone(), target)?;
gerrit.restack(branch_str)?;
gerrit.restack(branch_str, None)?;
} else {
gerrit.push(branch, target)?;
}
Expand Down Expand Up @@ -121,11 +121,11 @@ fn main() -> miette::Result<()> {
let mut gerrit = git.gerrit(None)?;
match command {
None => {
gerrit.restack("HEAD")?;
gerrit.restack("HEAD", None)?;
}
Some(command) => match command {
cli::Restack::Continue => {
gerrit.restack_continue()?;
cli::Restack::Continue(restack_continue) => {
gerrit.restack_continue(restack_continue)?;
}
cli::Restack::Abort => {
gerrit.restack_abort()?;
Expand Down
100 changes: 90 additions & 10 deletions src/restack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::collections::VecDeque;
use std::fmt::Display;
use std::io::BufReader;
use std::io::BufWriter;
use std::ops::Deref;

use camino::Utf8PathBuf;
use command_error::CommandExt;
Expand All @@ -15,6 +16,7 @@ use miette::IntoDiagnostic;

use crate::change_number::ChangeNumber;
use crate::change_status::ChangeStatus;
use crate::cli::RestackContinue;
use crate::commit_hash::CommitHash;
use crate::dependency_graph::DependencyGraph;
use crate::gerrit::GerritGitRemote;
Expand All @@ -32,7 +34,7 @@ pub struct RestackTodo {
/// Map from change numbers to updated commit hashes.
pub refs: BTreeMap<ChangeNumber, RefUpdate>,
/// Restack step in progress, if any.
in_progress: Option<Step>,
in_progress: Option<InProgress>,
}

impl RestackTodo {
Expand Down Expand Up @@ -157,15 +159,39 @@ impl Display for RefUpdate {
}
}

pub fn restack(gerrit: &mut GerritGitRemote, branch: &str) -> miette::Result<()> {
pub fn restack(
gerrit: &mut GerritGitRemote,
branch: &str,
options: Option<RestackContinue>,
) -> miette::Result<()> {
let git = gerrit.git();
let mut fetched = false;
let mut todo = get_or_create_todo(gerrit, branch)?;

match &todo.in_progress {
Some(step) => {
if let Some(step) = todo.in_progress.take() {
if options
.as_ref()
.map(|options| options.restart_in_progress)
.unwrap_or(false)
{
tracing::info!("Retrying restacking {step}");
todo.steps.push_front(step.inner);
} else if let Some(commit) =
options.and_then(|mut options| options.in_progress_commit.take())
{
tracing::info!(
"Using `--in-progress-commit`; restacking {step} produced commit {commit}"
);
todo.refs.insert(
step.change,
RefUpdate {
old: step.old_head,
new: commit,
},
);
todo.write(&git)?;
} else if git.rebase_in_progress()? {
tracing::info!("Continuing to restack {step}");
let old_head = git.rev_parse("REBASE_HEAD")?;
match git
.command()
.args(["rebase", "--continue"])
Expand All @@ -178,7 +204,7 @@ pub fn restack(gerrit: &mut GerritGitRemote, branch: &str) -> miette::Result<()>
todo.refs.insert(
step.change,
RefUpdate {
old: old_head,
old: step.old_head,
new: git.get_head()?,
},
);
Expand All @@ -188,8 +214,33 @@ pub fn restack(gerrit: &mut GerritGitRemote, branch: &str) -> miette::Result<()>
return error;
}
}
} else {
let head = git.get_head()?;
let change_id = git.change_id(&head)?;
let expect_change = gerrit.get_change(step.change)?;

tracing::warn!(
"Please use `git gr restack continue` instead of `git rebase --continue`"
);
if change_id == expect_change.id {
// OK, the user just did `git rebase --continue` on their own.
todo.refs.insert(
step.change,
RefUpdate {
old: step.old_head,
new: head,
},
);
todo.write(&git)?;
} else {
// The user did `git rebase --continue` on their own and then did
// something else...
return Err(miette!(
"Cannot find commit for change {}; use `git gr restack continue --in-progress-commit` or `--restart-in-progress` to continue",
expect_change.number.pretty(gerrit)?
));
}
}
None => {}
}

if todo.refs.is_empty() {
Expand All @@ -212,15 +263,22 @@ pub fn restack(gerrit: &mut GerritGitRemote, branch: &str) -> miette::Result<()>
}

while let Some(step) = todo.steps.pop_front() {
let old_head = gerrit.fetch_cl(gerrit.get_change(step.change)?.patchset())?;
let in_progress = InProgress {
inner: step,
old_head,
};

let step_result = todo
.perform_step(&step, gerrit, &mut fetched)
.wrap_err_with(|| format!("Failed to restack {step}"));
.perform_step(&in_progress, gerrit, &mut fetched)
.wrap_err_with(|| format!("Failed to restack {}", in_progress));

match step_result {
Ok(()) => {
todo.write(&git)?;
}
error @ Err(_) => {
todo.in_progress = Some(step);
todo.in_progress = Some(in_progress);
todo.write(&git)?;
return error.wrap_err(CONTINUE_MESSAGE);
}
Expand Down Expand Up @@ -381,3 +439,25 @@ pub fn create_todo(gerrit: &mut GerritGitRemote, branch: &str) -> miette::Result

Ok(todo)
}

#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
struct InProgress {
/// The step in progress.
inner: Step,
/// The HEAD commit of the change before restacking.
old_head: CommitHash,
}

impl Deref for InProgress {
type Target = Step;

fn deref(&self) -> &Self::Target {
&self.inner
}
}

impl Display for InProgress {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.inner)
}
}

0 comments on commit 3269054

Please sign in to comment.