aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Fischer <martin@push-f.com>2021-06-24 11:47:48 +0200
committerMartin Fischer <martin@push-f.com>2021-06-24 11:49:25 +0200
commitd24f51baf6e4e97e6f3e9ff3ce83274f353e82f9 (patch)
treec605de9066d6297655b1bceb47db2a64f96fbf94
parentde3437e4a2b357c9774dbef37ee7f76f50a3fabf (diff)
log: allow diffing specific revisions
-rw-r--r--src/get_routes.rs98
-rw-r--r--src/static/style.css5
-rw-r--r--src/static/style.css.sha2562
3 files changed, 72 insertions, 33 deletions
diff --git a/src/get_routes.rs b/src/get_routes.rs
index 73c9301..17b7941 100644
--- a/src/get_routes.rs
+++ b/src/get_routes.rs
@@ -1,7 +1,9 @@
use chrono::NaiveDateTime;
+use git2::Commit;
use git2::FileMode;
use git2::ObjectType;
use git2::Oid;
+use git2::Repository;
use git2::Tree;
use git2::TreeEntry;
use hyper::header;
@@ -166,7 +168,10 @@ fn log_blob<C: Controller>(
// the very first commit of the branch
commits.push(prev_commit);
}
- page.body.push_str("<div>");
+ page.body.push_str(
+ "<form><input type=hidden name=action value=diff>\
+ <button>Compare selected revisions</button>",
+ );
let mut prev_date = None;
for c in commits {
let date = NaiveDateTime::from_timestamp(c.time().seconds(), 0).date();
@@ -175,50 +180,71 @@ fn log_blob<C: Controller>(
page.body.push_str("</ul>");
}
page.body.push_str(&format!(
- "<h2 class=regular-text>{}</h2><ul>",
+ "<h2 class=regular-text>{}</h2><ul class=rev-list>",
date.format("%b %d, %Y")
));
}
page.body.push_str(&format!(
- "<li><a href='?action=diff&id={}'>{}: {}</a></li>",
+ "<li>\
+ <input type=radio name=oldid required value={0}>\
+ <input type=radio name=id required value={0}> ",
+ c.id().to_string()
+ ));
+ page.body.push_str(&format!(
+ "<a href='?action=diff&id={}'>{}: {}</a></li>",
html_escape(c.id().to_string()),
html_escape(c.author().name().unwrap_or_default()),
html_escape(c.summary().unwrap_or_default()),
));
prev_date = Some(date);
}
- page.body.push_str("</ul></div>");
+ page.body.push_str("</ul></form>");
Ok(page.into())
}
#[derive(Deserialize)]
-struct DiffParam {
+struct DiffParams {
id: String,
+ oldid: Option<String>,
+}
+
+fn find_commit<'a>(repo: &'a Repository, id: &str, branch_head: &Oid) -> Option<Commit<'a>> {
+ let oid = Oid::from_str(id).ok()?;
+ repo.find_commit(oid)
+ .ok()
+ // disallow viewing commits from other branches you shouldn't have access to
+ .filter(|c| oid == *branch_head || repo.graph_descendant_of(*branch_head, c.id()).unwrap())
}
fn diff_blob<C: Controller>(
_entr: TreeEntry,
- params: ActionParam,
+ action_param: ActionParam,
controller: &C,
ctx: Context,
) -> Result<Response, Error> {
- let param: DiffParam = ctx.parts.query()?;
+ let params: DiffParams = ctx.parts.query()?;
let branch_commit = ctx.branch_head()?;
- let commit = ctx
- .repo
- .find_commit(Oid::from_str(&param.id)?)
- .map_err(|_| Error::NotFound("commit not found".into()))?;
+ let mut commit = find_commit(&ctx.repo, &params.id, &branch_commit.id())
+ .ok_or_else(|| Error::NotFound("commit not found".into()))?;
- if branch_commit.id() != commit.id()
- && !ctx
- .repo
- .graph_descendant_of(branch_commit.id(), commit.id())?
- {
- // disallow viewing commits from other branches you shouldn't have access to
- return Err(Error::NotFound("commit not found".into()));
- }
+ let old_commit = if let Some(oldid) = &params.oldid {
+ let other_commit = find_commit(&ctx.repo, &oldid, &branch_commit.id())
+ .ok_or_else(|| Error::NotFound("commit not found".into()))?;
+
+ if other_commit.time() > commit.time() {
+ // TODO: redirect instead
+ let c = commit;
+ commit = other_commit;
+ Some(c)
+ } else {
+ Some(other_commit)
+ }
+ } else {
+ // TODO: what if there are multiple parents?
+ commit.parents().next()
+ };
let blob_id = if let Ok(entry) = commit.tree()?.get_path(&ctx.path) {
entry.id()
@@ -226,36 +252,44 @@ fn diff_blob<C: Controller>(
return Ok(Page {
title: format!("Commit for {}", ctx.file_name().unwrap()),
body: "file removed".into(),
- header: Some(action_links(&params.action, controller, &ctx)),
+ header: Some(action_links(&action_param.action, controller, &ctx)),
controller,
parts: &ctx.parts,
}
.into());
};
- // TODO: what if there are multiple parents?
- let old_blob_id = commit
- .parents()
- .next()
+ let old_blob_id = old_commit
.and_then(|p| p.tree().unwrap().get_path(&ctx.path).ok())
.map(|e| e.id());
if Some(blob_id) == old_blob_id {
- return Err(Error::NotFound("commit not found".into()));
+ return Err(Error::NotFound("no difference".into()));
}
let mut page = Page {
- title: format!("Commit for {}", ctx.file_name().unwrap()),
- header: Some(action_links(&params.action, controller, &ctx)),
- body: format!(
- "<h1>{}</h1>{} committed on {}",
- html_escape(commit.summary().unwrap_or_default()),
- html_escape(commit.author().name().unwrap_or_default()),
- NaiveDateTime::from_timestamp(commit.time().seconds(), 0).format("%b %d, %Y, %H:%M")
+ title: format!(
+ "{} for {}",
+ if params.oldid.is_some() {
+ "Diff"
+ } else {
+ "Commit"
+ },
+ ctx.file_name().unwrap()
),
+ header: Some(action_links(&action_param.action, controller, &ctx)),
+ body: String::new(),
controller,
parts: &ctx.parts,
};
+ if params.oldid.is_none() {
+ page.body.push_str(&format!(
+ "<h1>{}</h1>{} committed on {}",
+ html_escape(commit.summary().unwrap_or_default()),
+ html_escape(commit.author().name().unwrap_or_default()),
+ NaiveDateTime::from_timestamp(commit.time().seconds(), 0).format("%b %d, %Y, %H:%M")
+ ));
+ }
// TODO: if UTF-8 decoding fails, link ?action=raw&rev=
if let Some(old_blob_id) = old_blob_id {
diff --git a/src/static/style.css b/src/static/style.css
index a91b9df..0733d56 100644
--- a/src/static/style.css
+++ b/src/static/style.css
@@ -81,6 +81,11 @@ label {
font-weight: inherit;
}
+.rev-list {
+ list-style: none;
+ padding-left: 0;
+}
+
.addition { background: #e6ffed; }
.deletion { background: #ffeef0; }
.addition ins {background: #acf2bd; text-decoration: none; }
diff --git a/src/static/style.css.sha256 b/src/static/style.css.sha256
index 434cce7..17a3812 100644
--- a/src/static/style.css.sha256
+++ b/src/static/style.css.sha256
@@ -1 +1 @@
-nNLYarzU1i7D9yDyQw5R+15yNbfgxY838qGt/MH3I0E= \ No newline at end of file
+pVajQAgXar+xHqEuGZsATyQGTVFvnr82Sf28zZ3g4e0= \ No newline at end of file