diff --git a/Cargo.lock b/Cargo.lock
@@ -1711,6 +1711,7 @@ dependencies = [
"axum",
"bat",
"bincode",
+ "bytes",
"clap",
"futures",
"git2",
diff --git a/Cargo.toml b/Cargo.toml
@@ -9,6 +9,7 @@ edition = "2021"
askama = "0.11"
axum = "0.5"
bat = { version = "0.21", default-features = false, features = ["build-assets"] }
+bytes = "1.1"
bincode = "1.3"
clap = { version = "3.2", features = ["cargo"] }
futures = "0.3"
diff --git a/src/git.rs b/src/git.rs
@@ -1,5 +1,6 @@
use std::{borrow::Cow, fmt::Write, path::PathBuf, sync::Arc, time::Duration};
+use bytes::{Bytes, BytesMut};
use git2::{
BranchType, DiffFormat, DiffLineType, DiffOptions, DiffStatsFormat, ObjectType, Oid, Signature,
};
@@ -270,12 +271,13 @@ impl OpenRepository {
let head = repo.head().unwrap();
let commit = head.peel_to_commit().unwrap();
- let (diff_output, diff_stats) =
+ let (diff_plain, diff_output, diff_stats) =
fetch_diff_and_stats(&repo, &commit, &self.git.syntax_set);
let mut commit = Commit::from(commit);
commit.diff_stats = diff_stats;
commit.diff = diff_output;
+ commit.diff_plain = diff_plain;
commit
})
.await
@@ -294,12 +296,13 @@ impl OpenRepository {
let repo = self.repo.lock();
let commit = repo.find_commit(commit).unwrap();
- let (diff_output, diff_stats) =
+ let (diff_plain, diff_output, diff_stats) =
fetch_diff_and_stats(&repo, &commit, &self.git.syntax_set);
let mut commit = Commit::from(commit);
commit.diff_stats = diff_stats;
commit.diff = diff_output;
+ commit.diff_plain = diff_plain;
Arc::new(commit)
})
@@ -426,6 +429,7 @@ pub struct Commit {
body: String,
pub diff_stats: String,
pub diff: String,
+ pub diff_plain: Bytes,
}
impl From<git2::Commit<'_>> for Commit {
@@ -440,6 +444,7 @@ impl From<git2::Commit<'_>> for Commit {
body: commit.body().map(ToString::to_string).unwrap_or_default(),
diff_stats: String::with_capacity(0),
diff: String::with_capacity(0),
+ diff_plain: Bytes::new(),
}
}
}
@@ -479,17 +484,22 @@ fn fetch_diff_and_stats(
repo: &git2::Repository,
commit: &git2::Commit<'_>,
syntax_set: &SyntaxSet,
-) -> (String, String) {
+) -> (Bytes, String, String) {
let current_tree = commit.tree().unwrap();
let parent_tree = commit.parents().next().and_then(|v| v.tree().ok());
let mut diff_opts = DiffOptions::new();
- let diff = repo
+ let mut diff = repo
.diff_tree_to_tree(
parent_tree.as_ref(),
Some(¤t_tree),
Some(&mut diff_opts),
)
.unwrap();
+
+ let mut diff_plain = BytesMut::new();
+ let email = diff.format_email(1, 1, commit, None).unwrap();
+ diff_plain.extend_from_slice(&*email);
+
let diff_stats = diff
.stats()
.unwrap()
@@ -500,7 +510,7 @@ fn fetch_diff_and_stats(
.to_string();
let diff_output = format_diff(&diff, syntax_set);
- (diff_output, diff_stats)
+ (diff_plain.freeze(), diff_output, diff_stats)
}
fn format_file(content: &[u8], extension: &str, syntax_set: &SyntaxSet) -> String {
diff --git a/src/main.rs b/src/main.rs
@@ -12,7 +12,7 @@ use std::sync::Arc;
use std::time::Duration;
use syntect::html::ClassStyle;
use tower_layer::layer_fn;
-use tracing::info;
+use tracing::{info, instrument};
use crate::{git::Git, layers::logger::LoggingMiddleware};
@@ -90,6 +90,7 @@ fn static_css(content: &'static [u8]) -> impl Handler<()> {
}
}
+#[instrument(skip(t))]
pub fn into_response<T: Template>(t: &T) -> Response {
match t.render() {
Ok(body) => {
diff --git a/src/methods/repo.rs b/src/methods/repo.rs
@@ -6,9 +6,11 @@ use std::{
};
use askama::Template;
+use axum::http::HeaderValue;
use axum::{
extract::Query,
handler::Handler,
+ http,
http::Request,
response::{IntoResponse, Response},
Extension,
@@ -66,6 +68,7 @@ pub async fn service<ReqBody: Send + 'static>(mut request: Request<ReqBody>) ->
Some("tree") => BoxCloneService::new(handle_tree.into_service()),
Some("commit") => BoxCloneService::new(handle_commit.into_service()),
Some("diff") => BoxCloneService::new(handle_diff.into_service()),
+ Some("patch") => BoxCloneService::new(handle_patch.into_service()),
Some("tag") => BoxCloneService::new(handle_tag.into_service()),
Some(v) => {
uri_parts.push(v);
@@ -87,8 +90,6 @@ pub async fn service<ReqBody: Send + 'static>(mut request: Request<ReqBody>) ->
child_path = Some(reconstructed_path.into_iter().collect::<PathBuf>().clean());
- eprintln!("repo path: {:?}", child_path);
-
BoxCloneService::new(handle_tree.into_service())
} else {
BoxCloneService::new(handle_summary.into_service())
@@ -354,3 +355,23 @@ pub async fn handle_diff(
into_response(&DiffView { repo, commit })
}
+
+pub async fn handle_patch(
+ Extension(RepositoryPath(repository_path)): Extension<RepositoryPath>,
+ Extension(git): Extension<Arc<Git>>,
+ Query(query): Query<CommitQuery>,
+) -> Response {
+ let open_repo = git.repo(repository_path).await;
+ let commit = if let Some(commit) = query.id {
+ open_repo.commit(&commit).await
+ } else {
+ Arc::new(open_repo.latest_commit().await)
+ };
+
+ let headers = [(
+ http::header::CONTENT_TYPE,
+ HeaderValue::from_static("text/plain"),
+ )];
+
+ (headers, commit.diff_plain.clone()).into_response()
+}
diff --git a/templates/repo/commit.html b/templates/repo/commit.html
@@ -21,7 +21,7 @@
</tr>
<tr>
<th>commit</th>
- <td colspan="2"><pre><a href="/{{ repo.display() }}/commit?id={{ commit.oid() }}" class="no-style">{{ commit.oid() }}</a></pre></td>
+ <td colspan="2"><pre><a href="/{{ repo.display() }}/commit?id={{ commit.oid() }}" class="no-style">{{ commit.oid() }}</a> <a href="/{{ repo.display() }}/patch?id={{ commit.oid() }}">[patch]</a></pre></td>
</tr>
<tr>
<th>tree</th>