Skip to content

Commit

Permalink
WIP: Add support for in rebase gps listing
Browse files Browse the repository at this point in the history
The intention with this is to facilitate users running gps list while in
the middle of a rebase and being presented with useful information about
the current state that they are in. For example it should show which
patches have been played and which ones are planned to be played, and
their associated statuses.

To accomplish this there is a decent amount of work that was needed. We
effectively had to implement our own functionality to read the file
system state that Git manages during rebases and interpret it so that we
can have the necessary information to be able to present to the user
about the rebase underway.

This included adding the `git::in_rebase` function to determine if the
user is currently in the middle of a rebase. Adding the `RebaseTodo`
struct to represent rebase todo items. Rebase todo items are line items
that Git keeps track of during the rebase to keep track of which patches
have been played already and which ones are still to be played. Hence,
the name todo.

<!-- ps-id: 347c1924-99d4-4d67-b4a3-bcd4f040e5f6 -->
  • Loading branch information
drewdeponte committed Nov 21, 2023
1 parent 6f44241 commit 474ff42
Show file tree
Hide file tree
Showing 5 changed files with 319 additions and 165 deletions.
6 changes: 6 additions & 0 deletions src/ps/private/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,11 @@ mod get_summary;
mod git_error;
#[cfg(feature = "backup_cmd")]
mod hash_object_write;
mod in_rebase;
mod line_to_rebase_todo;
#[cfg(feature = "backup_cmd")]
mod read_hashed_object;
mod rebase_todo;
mod signers;
#[cfg(test)]
mod test_utils;
Expand Down Expand Up @@ -78,7 +81,10 @@ pub use get_summary::*;
pub use git_error::*;
#[cfg(feature = "backup_cmd")]
pub use hash_object_write::*;
pub use in_rebase::*;
pub use line_to_rebase_todo::*;
#[cfg(feature = "backup_cmd")]
pub use read_hashed_object::*;
pub use rebase_todo::*;
pub use signers::*;
pub use uncommited_changes_exist::*;
9 changes: 9 additions & 0 deletions src/ps/private/git/in_rebase.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use std::path::Path;

/// Check if we are in the middle of a rebase
///
/// Checks to see if we are in the middle of a rebase given a path to the .git directory within the
/// repository and return `true` if we are or `false` if we aren't.
pub fn in_rebase<P: AsRef<Path>>(repo_gitdir: P) -> bool {
repo_gitdir.as_ref().join("rebase-merge").is_dir()
}
122 changes: 122 additions & 0 deletions src/ps/private/git/line_to_rebase_todo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
use super::rebase_todo::RebaseTodo;
use std::result::Result;

#[derive(Debug, PartialEq, Eq)]
pub enum LineToRebaseTodoError {
MissingSha { line: String },
}

impl std::fmt::Display for LineToRebaseTodoError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::MissingSha { line } => write!(f, "missing sha in line, {}", line),
}
}
}

impl std::error::Error for LineToRebaseTodoError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::MissingSha { line: _ } => None,
}
}
}

fn str_to_next_whitespace(s: &str) -> Option<(&str, &str)> {
s.find(char::is_whitespace)
.map(|idx| (&s[..idx], &s[(idx + 1)..]))
}

/// Parse line from a rebase todo file into a RebaseTodo struct
///
/// Attempt to parse the given line into a RebaseTodo struct.
pub fn line_to_rebase_todo(line: &str) -> Result<RebaseTodo, LineToRebaseTodoError> {
let (cmd, rest_after_cmd) =
str_to_next_whitespace(line.trim()).ok_or(LineToRebaseTodoError::MissingSha {
line: line.to_string(),
})?;

match str_to_next_whitespace(rest_after_cmd) {
Some((sha, rest_after_sha)) => Ok(RebaseTodo {
command: cmd.to_string(),
sha: sha.to_string(),
summary: rest_after_sha.to_string(),
}),
None => Ok(RebaseTodo {
command: cmd.to_string(),
sha: rest_after_cmd.to_string(),
summary: "".to_string(),
}),
}
}

#[cfg(test)]
mod tests {
use super::RebaseTodo;
use super::{line_to_rebase_todo, LineToRebaseTodoError};

#[test]
fn parses_valid_rebase_todo_line() {
let rebase_todo =
line_to_rebase_todo("pick a403343e8a173dc8cc2fa27d9465b13fe2c0d627 Free new world")
.unwrap();
let expected_rebase_todo = RebaseTodo {
command: "pick".to_string(),
sha: "a403343e8a173dc8cc2fa27d9465b13fe2c0d627".to_string(),
summary: "Free new world".to_string(),
};

assert_eq!(rebase_todo, expected_rebase_todo);
}

#[test]
fn parses_valid_rebase_todo_line_with_ending_newline() {
let rebase_todo =
line_to_rebase_todo("pick a403343e8a173dc8cc2fa27d9465b13fe2c0d627 Free new world\n")
.unwrap();
let expected_rebase_todo = RebaseTodo {
command: "pick".to_string(),
sha: "a403343e8a173dc8cc2fa27d9465b13fe2c0d627".to_string(),
summary: "Free new world".to_string(),
};

assert_eq!(rebase_todo, expected_rebase_todo);
}

#[test]
fn parses_valid_rebase_todo_line_missing_a_summary() {
let rebase_todo =
line_to_rebase_todo("pick a403343e8a173dc8cc2fa27d9465b13fe2c0d627").unwrap();
let expected_rebase_todo = RebaseTodo {
command: "pick".to_string(),
sha: "a403343e8a173dc8cc2fa27d9465b13fe2c0d627".to_string(),
summary: "".to_string(),
};

assert_eq!(rebase_todo, expected_rebase_todo);
}

#[test]
fn parses_valid_rebase_todo_line_missing_a_summary_with_ending_newline() {
let rebase_todo =
line_to_rebase_todo("pick a403343e8a173dc8cc2fa27d9465b13fe2c0d627\n").unwrap();
let expected_rebase_todo = RebaseTodo {
command: "pick".to_string(),
sha: "a403343e8a173dc8cc2fa27d9465b13fe2c0d627".to_string(),
summary: "".to_string(),
};

assert_eq!(rebase_todo, expected_rebase_todo);
}

#[test]
fn returns_error_missing_sha_when_invalid_rebase_todo_line_missing_sha() {
let rebase_todo = line_to_rebase_todo("pick");
let expected_error: Result<RebaseTodo, LineToRebaseTodoError> =
Err(LineToRebaseTodoError::MissingSha {
line: "pick".to_string(),
});

assert_eq!(rebase_todo, expected_error);
}
}
6 changes: 6 additions & 0 deletions src/ps/private/git/rebase_todo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#[derive(Debug, PartialEq, Eq)]
pub struct RebaseTodo {
pub command: String, /* pick, edit, etc. */
pub sha: String,
pub summary: String,
}

0 comments on commit 474ff42

Please sign in to comment.