feat: git::Repository cancellation support (#1288)

feat: `git::Repository` support cancellation.

To make sure users can cancel git operation via signal, e.g. when the
git operation fail or users no longer want to install.

Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
Jiahao XU 2023-08-13 00:18:02 +10:00 committed by GitHub
parent ef99dd795f
commit fbed317df5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 113 additions and 16 deletions

View file

@ -1,6 +1,5 @@
use std::{fmt, mem, num::NonZeroU32, path::Path, str::FromStr, sync::atomic::AtomicBool};
use compact_str::CompactString;
use gix::{clone, create, open, remote, Url};
use thiserror::Error as ThisError;
use tracing::debug;
@ -8,6 +7,9 @@ use tracing::debug;
mod progress_tracing;
use progress_tracing::TracingProgress;
mod cancellation_token;
pub use cancellation_token::{GitCancelOnDrop, GitCancellationToken};
pub use gix::url::parse::Error as GitUrlParseError;
#[derive(Debug, ThisError)]
@ -116,14 +118,21 @@ impl Repository {
/// async context then you must wrap the call in [`tokio::task::spawn_blocking`].
///
/// WARNING: This function must be called after tokio runtime is initialized.
pub fn shallow_clone_bare(url: GitUrl, path: &Path) -> Result<Self, GitError> {
pub fn shallow_clone_bare(
url: GitUrl,
path: &Path,
cancellation_token: Option<GitCancellationToken>,
) -> Result<Self, GitError> {
debug!("Shallow cloning {url} to {}", path.display());
Ok(Self(
Self::prepare_fetch(url, path, create::Kind::Bare)?
.fetch_only(
&mut TracingProgress::new(CompactString::new("Cloning")),
&AtomicBool::new(false),
&mut TracingProgress::new("Cloning bare"),
cancellation_token
.as_ref()
.map(GitCancellationToken::get_atomic)
.unwrap_or(&AtomicBool::new(false)),
)?
.0
.into(),
@ -134,16 +143,26 @@ impl Repository {
/// async context then you must wrap the call in [`tokio::task::spawn_blocking`].
///
/// WARNING: This function must be called after tokio runtime is initialized.
pub fn shallow_clone(url: GitUrl, path: &Path) -> Result<Self, GitError> {
pub fn shallow_clone(
url: GitUrl,
path: &Path,
cancellation_token: Option<GitCancellationToken>,
) -> Result<Self, GitError> {
debug!("Shallow cloning {url} to {} with worktree", path.display());
let mut progress = TracingProgress::new(CompactString::new("Cloning"));
let mut progress = TracingProgress::new("Cloning with worktree");
Ok(Self(
Self::prepare_fetch(url, path, create::Kind::WithWorktree)?
.fetch_then_checkout(&mut progress, &AtomicBool::new(false))?
.0
.main_worktree(&mut progress, &AtomicBool::new(false))?
.main_worktree(
&mut progress,
cancellation_token
.as_ref()
.map(GitCancellationToken::get_atomic)
.unwrap_or(&AtomicBool::new(false)),
)?
.0
.into(),
))

View file

@ -0,0 +1,44 @@
use std::sync::{
atomic::{AtomicBool, Ordering::Relaxed},
Arc,
};
use derive_destructure2::destructure;
/// Token that can be used to cancel git operation.
#[derive(Clone, Debug, Default)]
pub struct GitCancellationToken(Arc<AtomicBool>);
impl GitCancellationToken {
/// Create a guard that cancel the git operation on drop.
#[must_use = "You must assign the guard to a variable, \
otherwise it is equivalent to `GitCancellationToken::cancel()`"]
pub fn cancel_on_drop(self) -> GitCancelOnDrop {
GitCancelOnDrop(self)
}
/// Cancel the git operation.
pub fn cancel(&self) {
self.0.store(true, Relaxed)
}
pub(super) fn get_atomic(&self) -> &AtomicBool {
&self.0
}
}
/// Guard used to cancel git operation on drop
#[derive(Debug, destructure)]
pub struct GitCancelOnDrop(GitCancellationToken);
impl Drop for GitCancelOnDrop {
fn drop(&mut self) {
self.0.cancel()
}
}
impl GitCancelOnDrop {
/// Disarm the guard, return the token.
pub fn disarm(self) -> GitCancellationToken {
self.destructure().0
}
}

View file

@ -27,7 +27,7 @@ const SEP: &str = "::";
impl TracingProgress {
/// Create a new instanCompactce from `name`.
pub fn new(name: CompactString) -> Self {
pub fn new(name: &str) -> Self {
let trigger = Arc::new(AtomicBool::new(true));
tokio::spawn({
let mut interval = time::interval(Duration::from_secs_f32(EMIT_LOG_EVERY_S));
@ -43,7 +43,7 @@ impl TracingProgress {
}
});
Self {
name,
name: CompactString::new(name),
id: UNKNOWN,
max: None,
step: 0,