use git2::Oid instead of String; various small refactors

This commit is contained in:
Sebastian Thiel 2024-05-26 15:48:36 +02:00
parent a5f71be44c
commit 590d713f91
No known key found for this signature in database
GPG Key ID: 9CB5EE7895E8268B
13 changed files with 130 additions and 82 deletions

View File

@ -62,7 +62,7 @@ fn list_snapshots(repo_dir: &str) -> Result<()> {
fn restore_snapshot(repo_dir: &str, snapshot_id: &str) -> Result<()> {
let project = project_from_path(repo_dir);
project.restore_snapshot(snapshot_id.to_owned())?;
project.restore_snapshot(snapshot_id.parse()?)?;
Ok(())
}

View File

@ -34,7 +34,7 @@ regex = "1.10"
reqwest = { version = "0.12.4", features = ["json"] }
resolve-path = "0.1.0"
rusqlite.workspace = true
serde.workspace = true
serde = { workspace = true, features = ["std"]}
serde_json = { version = "1.0", features = [ "std", "arbitrary_precision" ] }
sha2 = "0.10.8"
ssh-key = { version = "0.6.6", features = [ "alloc", "ed25519" ] }

View File

@ -63,4 +63,49 @@ pub mod serde {
{
v.seconds().serialize(s)
}
pub mod oid_opt {
use crate::git;
use serde::{Deserialize, Deserializer, Serialize};
pub fn serialize<S>(v: &Option<git::Oid>, s: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
v.as_ref().map(|v| v.to_string()).serialize(s)
}
pub fn deserialize<'de, D>(d: D) -> Result<Option<git::Oid>, D::Error>
where
D: Deserializer<'de>,
{
let hex = <Option<String> as Deserialize>::deserialize(d)?;
hex.map(|v| {
v.parse()
.map_err(|err: git2::Error| serde::de::Error::custom(err.to_string()))
})
.transpose()
}
}
pub mod oid {
use crate::git;
use serde::{Deserialize, Deserializer, Serialize};
pub fn serialize<S>(v: &git::Oid, s: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
v.to_string().serialize(s)
}
pub fn deserialize<'de, D>(d: D) -> Result<git::Oid, D::Error>
where
D: Deserializer<'de>,
{
let hex = String::deserialize(d)?;
hex.parse()
.map_err(|err: git2::Error| serde::de::Error::custom(err.to_string()))
}
}
}

View File

@ -9,6 +9,7 @@ use std::path::PathBuf;
use std::str::FromStr;
use strum::EnumString;
use crate::git;
use serde::Serialize;
/// A snapshot of the repository and virtual branches state that GitButler can restore to.
@ -17,7 +18,8 @@ use serde::Serialize;
#[serde(rename_all = "camelCase")]
pub struct Snapshot {
/// The sha of the commit that represents the snapshot
pub id: String,
#[serde(with = "crate::serde::oid")]
pub id: git::Oid,
/// Snapshot creation time in seconds from Unix epoch seconds, based on a Git commit
#[serde(serialize_with = "crate::serde::as_time_seconds_from_unix_epoch")]
pub created_at: git2::Time,

View File

@ -4,4 +4,5 @@ mod reflog;
pub mod snapshot;
mod state;
/// The name of the file holding our state, useful for watching for changes.
pub const OPLOG_FILE_NAME: &str = "operations-log.toml";

View File

@ -1,5 +1,5 @@
use anyhow::anyhow;
use git2::{FileMode, Oid};
use git2::FileMode;
use itertools::Itertools;
use std::collections::HashMap;
use std::str::FromStr;
@ -11,7 +11,7 @@ use tracing::instrument;
use crate::git::diff::FileDiff;
use crate::virtual_branches::Branch;
use crate::{git::diff::hunks_by_filepath, projects::Project};
use crate::{git, git::diff::hunks_by_filepath, projects::Project};
use super::{
entry::{OperationKind, Snapshot, SnapshotDetails, Trailer},
@ -38,7 +38,7 @@ impl Project {
/// Prepares a snapshot of the current state of the working directory as well as GitButler data.
/// Returns a tree sha of the snapshot. The snapshot is not discoverable until it is comitted with `commit_snapshot`
/// If there are files that are untracked and larger than `SNAPSHOT_FILE_LIMIT_BYTES`, they are excluded from snapshot creation and restoring.
pub(crate) fn prepare_snapshot(&self) -> Result<String> {
pub(crate) fn prepare_snapshot(&self) -> Result<git::Oid> {
let repo_path = self.path.as_path();
let repo = git2::Repository::init(repo_path)?;
@ -68,7 +68,7 @@ impl Project {
let mut tree_builder = repo.treebuilder(None)?;
tree_builder.insert("index", index_tree_oid, FileMode::Tree.into())?;
tree_builder.insert("target_tree", target_tree_oid, FileMode::Tree.into())?;
tree_builder.insert("conflicts", conflicts_tree, FileMode::Tree.into())?;
tree_builder.insert("conflicts", conflicts_tree.into(), FileMode::Tree.into())?;
tree_builder.insert("virtual_branches.toml", vb_blob, FileMode::Blob.into())?;
// go through all virtual branches and create a subtree for each with the tree and any commits encoded
@ -185,7 +185,7 @@ impl Project {
let head_trees_iter = head_trees.iter();
// iterate through all head trees
for head_tree in head_trees_iter {
let current_theirs = repo.find_tree(git2::Oid::from(*head_tree))?;
let current_theirs = repo.find_tree((*head_tree).into())?;
let mut workdir_temp_index =
repo.merge_trees(&base_tree, &current_ours, &current_theirs, None)?;
workdir_tree_oid = workdir_temp_index.write_tree_to(&repo)?;
@ -196,7 +196,7 @@ impl Project {
// ok, write out the final oplog tree
let tree_id = tree_builder.write()?;
Ok(tree_id.to_string())
Ok(tree_id.into())
}
/// Commits the snapshot tree that is created with the `prepare_snapshot` method.
@ -204,17 +204,17 @@ impl Project {
/// Returns the sha of the created snapshot commit or None if snapshots are disabled.
pub(crate) fn commit_snapshot(
&self,
tree_id: String,
tree_id: git::Oid,
details: SnapshotDetails,
) -> Result<Option<String>> {
) -> Result<Option<git::Oid>> {
let repo_path = self.path.as_path();
let repo = git2::Repository::init(repo_path)?;
let tree = repo.find_tree(Oid::from_str(&tree_id)?)?;
let tree = repo.find_tree(tree_id.into())?;
let oplog_state = OplogHandle::new(&self.gb_dir());
let oplog_head_commit = match oplog_state.get_oplog_head()? {
Some(head_sha) => match repo.find_commit(git2::Oid::from_str(&head_sha)?) {
Some(head_sha) => match repo.find_commit(head_sha.into()) {
Ok(commit) => Some(commit),
Err(_) => None, // cant find the old one, start over
},
@ -249,19 +249,15 @@ impl Project {
parents.as_slice(),
)?;
oplog_state.set_oplog_head(new_commit_oid.to_string())?;
oplog_state.set_oplog_head(new_commit_oid.into())?;
let vb_state = self.virtual_branches();
// grab the target tree sha
let default_target_sha = vb_state.get_default_target()?.sha;
set_reference_to_oplog(
self,
&default_target_sha.to_string(),
&new_commit_oid.to_string(),
)?;
set_reference_to_oplog(self, default_target_sha, new_commit_oid.into())?;
Ok(Some(new_commit_oid.to_string()))
Ok(Some(new_commit_oid.into()))
}
/// Creates a snapshot of the current state of the working directory as well as GitButler data.
@ -269,7 +265,7 @@ impl Project {
///
/// Note that errors in snapshot creation is typically ignored, so we want to learn about them.
#[instrument(skip(details), err(Debug))]
pub fn create_snapshot(&self, details: SnapshotDetails) -> Result<Option<String>> {
pub fn create_snapshot(&self, details: SnapshotDetails) -> Result<Option<git::Oid>> {
let tree_id = self.prepare_snapshot()?;
self.commit_snapshot(tree_id, details)
}
@ -278,7 +274,7 @@ impl Project {
/// An alternative way of retrieving the snapshots would be to manually the oplog head `git log <oplog_head>` available in `.git/gitbutler/operations-log.toml`.
///
/// If there are no snapshots, an empty list is returned.
pub fn list_snapshots(&self, limit: usize, sha: Option<String>) -> Result<Vec<Snapshot>> {
pub fn list_snapshots(&self, limit: usize, sha: Option<git::Oid>) -> Result<Vec<Snapshot>> {
let repo_path = self.path.as_path();
let repo = git2::Repository::init(repo_path)?;
@ -295,7 +291,7 @@ impl Project {
}
};
let oplog_head_commit = repo.find_commit(git2::Oid::from_str(&head_sha)?)?;
let oplog_head_commit = repo.find_commit(head_sha.into())?;
let mut revwalk = repo.revwalk()?;
revwalk.push(oplog_head_commit.id())?;
@ -345,7 +341,7 @@ impl Project {
let lines_removed = stats.deletions();
snapshots.push(Snapshot {
id: commit_id.to_string(),
id: commit_id.into(),
details,
lines_added,
lines_removed,
@ -359,7 +355,7 @@ impl Project {
} else {
// this is the very first snapshot
snapshots.push(Snapshot {
id: commit_id.to_string(),
id: commit_id.into(),
details,
lines_added: 0,
lines_removed: 0,
@ -384,14 +380,14 @@ impl Project {
///
/// If there are files that are untracked and larger than `SNAPSHOT_FILE_LIMIT_BYTES`, they are excluded from snapshot creation and restoring.
/// Returns the sha of the created revert snapshot commit or None if snapshots are disabled.
pub fn restore_snapshot(&self, sha: String) -> Result<Option<String>> {
pub fn restore_snapshot(&self, sha: git::Oid) -> Result<Option<git::Oid>> {
let repo_path = self.path.as_path();
let repo = git2::Repository::init(repo_path)?;
// prepare snapshot
let snapshot_tree = self.prepare_snapshot();
let commit = repo.find_commit(git2::Oid::from_str(&sha)?)?;
let commit = repo.find_commit(sha.into())?;
// Top tree
let top_tree = commit.tree()?;
let vb_tree_entry = top_tree
@ -442,8 +438,8 @@ impl Project {
// for each commit, recreate the commit from the commit data if it doesn't exist
if let Some(commit_id) = commit_entry.name() {
// check for the oid in the repo
let commit_oid = git2::Oid::from_str(commit_id)?;
if repo.find_commit(commit_oid).is_err() {
let commit_oid = git::Oid::from_str(commit_id)?;
if repo.find_commit(commit_oid.into()).is_err() {
// commit is not in the repo, let's build it from our data
// we get the data from the blob entry and create a commit object from it, which should match the oid of the entry
let commit_tree = commit_entry
@ -460,7 +456,7 @@ impl Project {
let new_commit_oid = repo
.odb()?
.write(git2::ObjectType::Commit, commit_blob.content())?;
if new_commit_oid != commit_oid {
if new_commit_oid != commit_oid.into() {
return Err(anyhow!("commit oid mismatch"));
}
}
@ -468,13 +464,13 @@ impl Project {
// if branch_name is 'integration', we need to create or update the gitbutler/integration branch
if let Some(branch_name) = branch_name {
if branch_name == "integration" {
let integration_commit = repo.find_commit(commit_oid)?;
let integration_commit = repo.find_commit(commit_oid.into())?;
// reset the branch if it's there
let branch =
repo.find_branch("gitbutler/integration", git2::BranchType::Local);
if let Ok(mut branch) = branch {
// need to detatch the head for just a minuto
repo.set_head_detached(commit_oid)?;
repo.set_head_detached(commit_oid.into())?;
branch.delete()?;
}
// ok, now we set the branch to what it was and update HEAD
@ -538,7 +534,7 @@ impl Project {
trailers: vec![
Trailer {
key: "restored_from".to_string(),
value: sha,
value: sha.to_string(),
},
Trailer {
key: "restored_operation".to_string(),
@ -580,11 +576,11 @@ impl Project {
/// Returns the diff of the snapshot and it's parent. It only includes the workdir changes.
///
/// This is useful to show what has changed in this particular snapshot
pub fn snapshot_diff(&self, sha: String) -> Result<HashMap<PathBuf, FileDiff>> {
pub fn snapshot_diff(&self, sha: git::Oid) -> Result<HashMap<PathBuf, FileDiff>> {
let repo_path = self.path.as_path();
let repo = git2::Repository::init(repo_path)?;
let commit = repo.find_commit(git2::Oid::from_str(&sha)?)?;
let commit = repo.find_commit(sha.into())?;
// Top tree
let tree = commit.tree()?;
let old_tree = commit.parent(0)?.tree()?;
@ -621,7 +617,7 @@ impl Project {
}
/// Gets the sha of the last snapshot commit if present.
pub fn oplog_head(&self) -> Result<Option<String>> {
pub fn oplog_head(&self) -> Result<Option<git::Oid>> {
let oplog_state = OplogHandle::new(&self.gb_dir());
oplog_state.get_oplog_head()
}
@ -663,7 +659,7 @@ fn restore_conflicts_tree(
Ok(())
}
fn write_conflicts_tree(repo_path: &std::path::Path, repo: &git2::Repository) -> Result<git2::Oid> {
fn write_conflicts_tree(repo_path: &std::path::Path, repo: &git2::Repository) -> Result<git::Oid> {
let merge_parent_path = repo_path.join(".git").join("base_merge_parent");
let merge_parent_blob = if merge_parent_path.exists() {
let merge_parent_content = fs::read(merge_parent_path)?;
@ -690,7 +686,7 @@ fn write_conflicts_tree(repo_path: &std::path::Path, repo: &git2::Repository) ->
tree_builder.insert("conflicts", conflicts_blob.unwrap(), FileMode::Blob.into())?;
}
let conflicts_tree = tree_builder.write()?;
Ok(conflicts_tree)
Ok(conflicts_tree.into())
}
fn get_exclude_list(repo: &git2::Repository) -> Result<String> {
@ -740,11 +736,9 @@ fn lines_since_snapshot(project: &Project) -> Result<usize> {
repo.add_ignore_rule(&files_to_exclude)?;
let oplog_state = OplogHandle::new(&project.gb_dir());
let head_sha = oplog_state.get_oplog_head()?;
if head_sha.is_none() {
let Some(head_sha) = oplog_state.get_oplog_head()? else {
return Ok(0);
}
let head_sha = head_sha.unwrap();
};
let vb_state = project.virtual_branches();
let binding = vb_state.list_branches()?;
@ -757,7 +751,7 @@ fn lines_since_snapshot(project: &Project) -> Result<usize> {
let mut lines_changed = 0;
for branch in dirty_branches {
lines_changed += branch_lines_since_snapshot(branch, &repo, head_sha.clone())?;
lines_changed += branch_lines_since_snapshot(branch, &repo, head_sha)?;
}
Ok(lines_changed)
}
@ -765,11 +759,11 @@ fn lines_since_snapshot(project: &Project) -> Result<usize> {
fn branch_lines_since_snapshot(
branch: &Branch,
repo: &git2::Repository,
head_sha: String,
head_sha: git::Oid,
) -> Result<usize> {
let active_branch_tree = repo.find_tree(branch.tree.into())?;
let commit = repo.find_commit(git2::Oid::from_str(&head_sha)?)?;
let commit = repo.find_commit(head_sha.into())?;
let head_tree = commit.tree()?;
let virtual_branches = head_tree
.get_name("virtual_branches")

View File

@ -1,4 +1,5 @@
use crate::fs::write;
use crate::git;
use anyhow::Result;
use itertools::Itertools;
use std::path::PathBuf;
@ -22,8 +23,8 @@ use crate::projects::Project;
/// The reflog entry is continuously updated to refer to the current target and oplog head commits.
pub(crate) fn set_reference_to_oplog(
project: &Project,
target_head_sha: &str,
oplog_head_sha: &str,
target_head_sha: git::Oid,
oplog_head_sha: git::Oid,
) -> Result<()> {
let repo_path = project.path.as_path();
let reflog_file_path = repo_path
@ -36,7 +37,7 @@ pub(crate) fn set_reference_to_oplog(
if !reflog_file_path.exists() {
let repo = git2::Repository::init(repo_path)?;
let commit = repo.find_commit(git2::Oid::from_str(target_head_sha)?)?;
let commit = repo.find_commit(target_head_sha.into())?;
repo.branch("gitbutler/target", &commit, false)?;
}
@ -46,8 +47,8 @@ pub(crate) fn set_reference_to_oplog(
));
}
set_target_ref(&reflog_file_path, target_head_sha)?;
set_oplog_ref(&reflog_file_path, oplog_head_sha)?;
set_target_ref(&reflog_file_path, &target_head_sha.to_string())?;
set_oplog_ref(&reflog_file_path, &oplog_head_sha.to_string())?;
Ok(())
}
@ -88,6 +89,7 @@ fn set_oplog_ref(file_path: &PathBuf, sha: &str) -> Result<()> {
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
use tempfile::tempdir;
#[test]
@ -109,7 +111,8 @@ mod tests {
assert!(!log_file_path.exists());
// Set ref for the first time
assert!(set_reference_to_oplog(&project, &commit_id.to_string(), "oplog_sha").is_ok());
let oplog_sha = git::Oid::from_str("0123456789abcdef0123456789abcdef0123456").unwrap();
assert!(set_reference_to_oplog(&project, commit_id.into(), oplog_sha).is_ok());
assert!(log_file_path.exists());
let log_file = std::fs::read_to_string(&log_file_path).unwrap();
let log_lines = log_file.lines().collect::<Vec<_>>();
@ -119,13 +122,12 @@ mod tests {
commit_id
)));
assert!(log_lines[0].ends_with(&format!("branch: Created from {}", commit_id)));
assert!(log_lines[1].starts_with(&format!("{} {}", commit_id, "oplog_sha")));
assert!(log_lines[1].ends_with("reset: moving to oplog_sha"));
assert!(log_lines[1].starts_with(&format!("{} {}", commit_id, oplog_sha)));
assert!(log_lines[1].ends_with(&format!("reset: moving to {oplog_sha}")));
// Update the oplog head only
assert!(
set_reference_to_oplog(&project, &commit_id.to_string(), "another_oplog_sha").is_ok()
);
let another_oplog_sha = git2::Oid::zero().into();
assert!(set_reference_to_oplog(&project, commit_id.into(), another_oplog_sha).is_ok());
let log_file = std::fs::read_to_string(&log_file_path).unwrap();
let log_lines = log_file.lines().collect::<Vec<_>>();
assert_eq!(log_lines.len(), 2);
@ -134,23 +136,21 @@ mod tests {
commit_id
)));
assert!(log_lines[0].ends_with(&format!("branch: Created from {}", commit_id)));
println!("{:?}", log_lines[1]);
assert!(log_lines[1].starts_with(&format!("{} {}", commit_id, "another_oplog_sha")));
assert!(log_lines[1].ends_with("reset: moving to another_oplog_sha"));
assert!(log_lines[1].starts_with(&format!("{} {}", commit_id, another_oplog_sha)));
assert!(log_lines[1].ends_with(&format!("reset: moving to {another_oplog_sha}")));
// Update the target head only
assert!(set_reference_to_oplog(&project, "new_target", "another_oplog_sha").is_ok());
let new_target = git::Oid::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap();
assert!(set_reference_to_oplog(&project, new_target, another_oplog_sha).is_ok());
let log_file = std::fs::read_to_string(&log_file_path).unwrap();
let log_lines = log_file.lines().collect::<Vec<_>>();
assert_eq!(log_lines.len(), 2);
assert!(log_lines[0].starts_with(&format!(
"0000000000000000000000000000000000000000 {}",
"new_target"
"0000000000000000000000000000000000000000 {new_target}"
)));
assert!(log_lines[0].ends_with(&format!("branch: Created from {}", "new_target")));
println!("{:?}", log_lines[1]);
assert!(log_lines[1].starts_with(&format!("{} {}", "new_target", "another_oplog_sha")));
assert!(log_lines[1].ends_with("reset: moving to another_oplog_sha"));
assert!(log_lines[0].ends_with(&format!("branch: Created from {new_target}")));
assert!(log_lines[1].starts_with(&format!("{new_target} {another_oplog_sha}")));
assert!(log_lines[1].ends_with(&format!("reset: moving to {another_oplog_sha}")));
}
fn setup_repo() -> (tempfile::TempDir, git2::Oid) {

View File

@ -2,6 +2,7 @@ use std::vec;
use crate::projects::Project;
use crate::{
git,
ops::entry::{OperationKind, SnapshotDetails},
virtual_branches::{branch::BranchUpdateRequest, Branch},
};
@ -28,20 +29,20 @@ impl Project {
self.create_snapshot(details)?;
Ok(())
}
pub(crate) fn snapshot_commit_undo(&self, commit_sha: String) -> anyhow::Result<()> {
pub(crate) fn snapshot_commit_undo(&self, commit_sha: git::Oid) -> anyhow::Result<()> {
let details =
SnapshotDetails::new(OperationKind::UndoCommit).with_trailers(vec![Trailer {
key: "sha".to_string(),
value: commit_sha,
value: commit_sha.to_string(),
}]);
self.create_snapshot(details)?;
Ok(())
}
pub(crate) fn snapshot_commit_creation(
&self,
snapshot_tree: String,
snapshot_tree: git::Oid,
commit_message: String,
sha: Option<String>,
sha: Option<git::Oid>,
) -> anyhow::Result<()> {
let details = SnapshotDetails::new(OperationKind::CreateCommit).with_trailers(vec![
Trailer {
@ -50,7 +51,7 @@ impl Project {
},
Trailer {
key: "sha".to_string(),
value: sha.unwrap_or_default(),
value: sha.map(|sha| sha.to_string()).unwrap_or_default(),
},
]);
self.commit_snapshot(snapshot_tree, details)?;

View File

@ -5,6 +5,7 @@ use std::{
};
use crate::fs::read_toml_file_or_default;
use crate::git;
use serde::{Deserialize, Deserializer, Serialize};
use super::OPLOG_FILE_NAME;
@ -26,7 +27,8 @@ fn unix_epoch() -> SystemTime {
#[derive(Serialize, Deserialize, Debug)]
pub struct Oplog {
/// This is the sha of the last oplog commit
pub head_sha: Option<String>,
#[serde(with = "crate::serde::oid_opt")]
pub head_sha: Option<git::Oid>,
/// The time when the last snapshot was created. Seconds since Epoch
#[serde(
deserialize_with = "unfailing_system_time_deserialize",
@ -59,7 +61,7 @@ impl OplogHandle {
/// Persists the oplog head for the given repository.
///
/// Errors if the file cannot be read or written.
pub fn set_oplog_head(&self, sha: String) -> Result<()> {
pub fn set_oplog_head(&self, sha: git::Oid) -> Result<()> {
let mut oplog = self.read_file()?;
oplog.head_sha = Some(sha);
self.write_file(oplog)?;
@ -69,7 +71,7 @@ impl OplogHandle {
/// Gets the oplog head sha for the given repository.
///
/// Errors if the file cannot be read or written.
pub fn get_oplog_head(&self) -> Result<Option<String>> {
pub fn get_oplog_head(&self) -> Result<Option<git::Oid>> {
let oplog = self.read_file()?;
Ok(oplog.head_sha)
}

View File

@ -460,7 +460,7 @@ impl ControllerInner {
project_repository.project().snapshot_commit_creation(
snapshot_tree,
message.to_owned(),
Some("".to_string()),
None,
)
});
result
@ -717,7 +717,7 @@ impl ControllerInner {
self.with_verify_branch(project_id, |project_repository, _| {
let _ = project_repository
.project()
.snapshot_commit_undo(commit_oid.to_string());
.snapshot_commit_undo(commit_oid);
super::undo_commit(project_repository, branch_id, commit_oid).map_err(Into::into)
})
}

View File

@ -88,14 +88,14 @@ mod snapshot_details {
#[test]
fn new() {
let commit_sha = "1234567890".to_string();
let commit_sha = git2::Oid::zero().into();
let commit_message =
"Create a new snapshot\n\nBody text 1\nBody text2\n\nBody text 3\n\nVersion: 1\nOperation: CreateCommit\nFoo: Bar\n".to_string();
let timezone_offset_does_not_matter = 1234;
let created_at = git2::Time::new(1234567890, timezone_offset_does_not_matter);
let details = SnapshotDetails::from_str(&commit_message.clone()).unwrap();
let snapshot = Snapshot {
id: commit_sha.clone(),
id: commit_sha,
created_at,
lines_added: 1,
lines_removed: 1,

View File

@ -157,7 +157,6 @@ mod go_back_to_integration {
controller
.set_base_branch(project_id, &"refs/remotes/origin/master".parse().unwrap())
.await
.map_err(|error| dbg!(error))
.unwrap_err()
.downcast_ref(),
Some(errors::SetBaseBranchError::DirtyWorkingDirectory)

View File

@ -22,7 +22,11 @@ pub async fn list_snapshots(
.state::<projects::Controller>()
.get(&project_id)
.context("failed to get project")?;
let snapshots = project.list_snapshots(limit, sha)?;
let snapshots = project.list_snapshots(
limit,
sha.map(|hex| hex.parse().map_err(anyhow::Error::from))
.transpose()?,
)?;
Ok(snapshots)
}
@ -37,7 +41,7 @@ pub async fn restore_snapshot(
.state::<projects::Controller>()
.get(&project_id)
.context("failed to get project")?;
project.restore_snapshot(sha)?;
project.restore_snapshot(sha.parse().map_err(anyhow::Error::from)?)?;
Ok(())
}
@ -52,6 +56,6 @@ pub async fn snapshot_diff(
.state::<projects::Controller>()
.get(&project_id)
.context("failed to get project")?;
let diff = project.snapshot_diff(sha)?;
let diff = project.snapshot_diff(sha.parse().map_err(anyhow::Error::from)?)?;
Ok(diff)
}