~/selfhut

69345aa06b1308f69b7815a9dd9caf46b8f15da6 — Arthur Melton 468a751a 2 years ago
commits
M src/git/diffs.rs => src/git/diffs.rs +1 -1
@@ 3,7 3,7 @@ pub fn diffs<'a>(commit: git2::Commit<'a>, repo: &'a git2::Repository) -> Option

        Ok(tree) => match commit.parent(0) {
            Ok(parent) => match parent.tree() {
                Ok(parent_tree) => {
                    match repo.diff_tree_to_tree(Some(&tree), Some(&parent_tree), None) {
                    match repo.diff_tree_to_tree(Some(&parent_tree), Some(&tree), None) {
                        Ok(diff) => Some(diff),
                        Err(_) => None,
                    }

M src/main.rs => src/main.rs +3 -0
@@ 16,6 16,7 @@ use crate::repository::log;

use crate::repository::raw;
use crate::repository::summary;
use crate::repository::tree;
use crate::repository::commit;
use crate::utils::own_pathbuf::PathBufWithDotfiles;
use rocket_dyn_templates::Template;


@@ 36,6 37,8 @@ fn rocket() -> _ {

                log::log_main,
                log::log,
                blame::blames,
                commit::commit,
                commit::patch
            ],
        )
        .attach(Template::fairing())

A src/repository/commit.rs +162 -0
@@ 0,0 1,162 @@

use crate::config::CONFIG;
use crate::git::blame::blame;
use crate::git::commits::get_commits;
use crate::git::file::file;
use crate::git::diffs::diffs;
use serde_derive::Serialize;
use git2::DiffLineType::*;

use crate::utils::repo_config::repo_config;
use crate::PathBufWithDotfiles;
use rocket_dyn_templates::{context, Template};
use std::ffi::OsStr;
use std::path::Path;
use syntect::highlighting::ThemeSet;
use syntect::html::highlighted_html_for_string;
use syntect::parsing::SyntaxSet;
use git2::Delta::*;

#[get("/<repo>/commit/<oid>", rank = 2)]
pub fn commit(repo: String, oid: String) -> Option<Template> {
    let mut repo_path = CONFIG.git_location.clone();
    repo_path.push(format!("{}.git", repo));
    let repo_clone = repo.clone();
    let repo = git2::Repository::open(repo_path).ok()?;
    let commit = repo.find_commit(git2::Oid::from_str(&oid).ok()?).ok()?;
    let diff = diffs(commit.clone(), &repo)?;
    let stats = diff.stats().ok()?;
    Some(Template::render(
        "repository/commit",
        context! {
            title: format!("{} :: {}", oid, repo_clone.clone()),
            repo: repo_clone.clone(),
            config: repo_config(repo_clone.clone()),
            domain: CONFIG.domain.to_string(),
            active: "log",
            payment: CONFIG.payment_link.clone(),
            mailing_list: CONFIG.mailing_list.clone(),
            commit: match get_commits(repo_clone.clone(), 1, Some(oid.clone()), None) {
                Some(x) => match x.clone().get(0) {
                    Some(x) => Some(x.clone()),
                    None => None
                }
                None => None
            },
            parent: match commit.parent_id(0) {
                Ok(parent) => {
                    match get_commits(repo_clone.clone(), 1, Some(parent.to_string()), None) {
                        Some(x) => Some(x.first()?.clone()),
                        None => None
                    }
                },
                Err(_) => None
            },
            files_changed: stats.files_changed(),
            insertions: stats.insertions(),
            deletions: stats.deletions(),
            files: {
                let mut items = Vec::new();
                let mut x = 0;
                for i in diff.deltas() {
                    let patch = git2::Patch::from_diff(&diff, x).ok()??;
                    let hunk_n = patch.num_hunks();
                    let mut hunks = Vec::new();
                    for y in 0..hunk_n {
                        let hunk = patch.hunk(y).ok()?;
                        let line_n = patch.num_lines_in_hunk(y).ok()?;
                        let mut lines = Vec::new();
                        for z in 0..line_n {
                            let line = patch.line_in_hunk(y, z).ok()?;
                            lines.push(Lines {
                                line_n: z,
                                class: match line.origin_value() {
                                        Addition => "text-success",
                                        Deletion => "text-danger",
                                        AddEOFNL => "text-success",
                                        DeleteEOFNL => "text-danger",
                                        _ => ""
                                }.to_string(),
                                types: line.origin(),
                                line: std::str::from_utf8(line.content()).ok()?.to_string()
                            });
                        }
                        let header = std::str::from_utf8(hunk.0.header()).ok()?.to_string();
                        let mut first_line = header.split("@@").collect::<Vec<&str>>();
                        for _ in 0..2 {
                            first_line.remove(0);
                        }
                        hunks.push(Hunk {
                            hunk_n: y,
                            first: hunk.0.old_start(),
                            second: hunk.0.old_lines(),
                            third: hunk.0.new_start(),
                            fourth: hunk.0.new_lines(),
                            first_line: first_line.join("@@"),
                            lines
                        });
                    }
                    items.push(Files {
                        beging_path: (*i.old_file().path()?).display().to_string(),
                        end_path: (*i.new_file().path()?).display().to_string(),
                        status: match i.status() {
                            Added => 'A',
                            Deleted => 'D',
                            Modified => 'M',
                            Renamed => 'R',
                            Copied => 'C',
                            Ignored => 'I',
                            Typechange => 'T',
                            Conflicted => 'C',
                            _ => ' ',
                        },
                        insertions: patch.line_stats().ok()?.1, 
                        deletions: patch.line_stats().ok()?.2,
                        hunks
                    });
                    x+=1;
                }
                items
            },
        }
    ))
}

#[get("/<repo>/patch/<oid>", rank = 2)]
pub fn patch(repo: String, oid: String) -> Option<Vec<u8>> {
    let mut repo_path = CONFIG.git_location.clone();
    repo_path.push(format!("{}.git", repo));
    let repo_clone = repo.clone();
    let repo = git2::Repository::open(repo_path).ok()?;
    let commit = repo.find_commit(git2::Oid::from_str(&oid).ok()?).ok()?;
    let diff = diffs(commit, &repo)?;
    None
}

#[derive(Serialize, Clone)]
pub struct Files {
    beging_path: String,
    end_path: String,
    status: char,
    insertions: usize,
    deletions: usize,
    hunks: Vec<Hunk>
}

#[derive(Serialize, Clone)]
pub struct Hunk {
    hunk_n: usize,
    first: u32,
    second: u32,
    third: u32,
    fourth: u32,
    first_line: String,
    lines: Vec<Lines>
}

#[derive(Serialize, Clone)]
pub struct Lines {
    line_n: usize,
    class: String,
    types: char,
    line: String
}

M src/repository/mod.rs => src/repository/mod.rs +1 -0
@@ 3,3 3,4 @@ pub mod log;

pub mod raw;
pub mod summary;
pub mod tree;
pub mod commit;

A templates/repository/commit.html.hbs +56 -0
@@ 0,0 1,56 @@

{{#*inline "page"}}
{{> navbar}}
<div class="container">
	<div class="row" style="margin-bottom: 1rem">
		<div class="col-md-10">
			<div class="event-list">
				<div class="event">
                    <div>
                    {{commit.commit_hash}} — {{commit.commitie}}  
                    {{#if parent}}
                        <span aria-hidden="true" class="icon icon-code-branch sm"><svg viewBox="0 0 384 512" xmlns="http://www.w3.org/2000/svg"><path d="M384 144c0-44.2-35.8-80-80-80s-80 35.8-80 80c0 36.4 24.3 67.1 57.5 76.8-.6 16.1-4.2 28.5-11 36.9-15.4 19.2-49.3 22.4-85.2 25.7-28.2 2.6-57.4 5.4-81.3 16.9v-144c32.5-10.2 56-40.5 56-76.3 0-44.2-35.8-80-80-80S0 35.8 0 80c0 35.8 23.5 66.1 56 76.3v199.3C23.5 365.9 0 396.2 0 432c0 44.2 35.8 80 80 80s80-35.8 80-80c0-34-21.2-63.1-51.2-74.6 3.1-5.2 7.8-9.8 14.9-13.4 16.2-8.2 40.4-10.4 66.1-12.8 42.2-3.9 90-8.4 118.2-43.4 14-17.4 21.1-39.8 21.6-67.9 31.6-10.8 54.4-40.7 54.4-75.9zM80 64c8.8 0 16 7.2 16 16s-7.2 16-16 16-16-7.2-16-16 7.2-16 16-16zm0 384c-8.8 0-16-7.2-16-16s7.2-16 16-16 16 7.2 16 16-7.2 16-16 16zm224-320c8.8 0 16 7.2 16 16s-7.2 16-16 16-16-7.2-16-16 7.2-16 16-16z"></path></svg></span>    
                        <a href="/{{repo}}/commit/{{parent.commit_hash}}">{{parent.commit_hash_short}}</a>
                    {{/if}}
                    <small class="pull-right">
                    <a href="/{{repo}}/log?from={{commit.commit_hash}}#log-{{this.commit_hash}}" id="log-{{commit.commit_hash}}"><span title="{{commit.time_utc}}">{{commit.time_relitive}}</span></a>
                    </small>
                    </div>
                    <pre class="commit">{{commit.commit}}</pre>
                </div>
			</div>
        </div>
        <div class="col-md-2">
            <div style="margin-bottom: 1rem">
                <a class="btn btn-primary btn-block" href="/{{repo}}/patch/{{commit.commit_hash}}">patch <span aria-hidden="true" class="icon icon-caret-right"><svg viewBox="0 0 192 512" xmlns="http://www.w3.org/2000/svg"><path d="M0 384.662V127.338c0-17.818 21.543-26.741 34.142-14.142l128.662 128.662c7.81 7.81 7.81 20.474 0 28.284L34.142 398.804C21.543 411.404 0 402.48 0 384.662z"></path></svg>
                </span></a>
                <a class="btn btn-default btn-block" href="/{{repo}}/tree/{{commit.commit_hash}}">browse <span aria-hidden="true" class="icon icon-caret-right"><svg viewBox="0 0 192 512" xmlns="http://www.w3.org/2000/svg"><path d="M0 384.662V127.338c0-17.818 21.543-26.741 34.142-14.142l128.662 128.662c7.81 7.81 7.81 20.474 0 28.284L34.142 398.804C21.543 411.404 0 402.48 0 384.662z"></path></svg>
                </span></a>
            </div>
        </div>
	</div>
    <div class="row">
        <div class="col-md-12">
            <div class="event-list">
                <div class="event diff">
                    <pre>{{files_changed}} files changed, <strong class="text-success">{{insertions}}</strong> insertions(+), <strong class="text-danger">{{deletions}}</strong> deletions(-)
{{#each files}}

{{#if (or (eq this.status "R") (eq this.status "C") )}}{{this.status}} <a href="#{{this.end_path}}">{{this.beging_path}}</a> => {{this.status}} <a href="#{{this.end_path}}">{{this.end_path}}</a>{{else}}{{this.status}} <a href="#{{this.beging_path}}">{{this.beging_path}}</a>{{/if}}{{/each}}</pre>
                </div>
                <div style="margin-bottom: 2rem"></div>
                {{#each files}}
                    <pre style="margin-bottom: 0; background: transparent; padding: 0;">{{this.status}} <a href="/{{../repo}}/tree/{{../parent.commit_hash}}/{{this.beging_path}}" id="{{this.beging_path}}">{{this.beging_path}}</a> =&gt; <a href="/{{../repo}}/tree/{{../commit.commit_hash}}/{{this.end_path}}" id="{{this.end_path}}">{{this.end_path}}</a> <span class="pull-right"><ins class="text-success">+{{this.insertions}}</ins> <del class="text-danger">-{{this.deletions}}</del></span></pre>
                    <div class="event diff">
                    <pre>
{{#each hunks}}
<strong class="text-info">@@ <a href="/{{../../repo}}/tree/{{../../parent.commit_hash}}/{{this.end_path}}#L{{this.first}}" style="text-decoration: underline">{{this.first}}</a>,{{this.second}} <a href="/{{../../repo}}/tree/{{../../commit.commit_hash}}/{{this.end_path}}#L{{this.third}}" style="text-decoration: underline">{{this.third}}</a>,{{this.fourth}} @@{{this.first_line}}</strong>
{{#each lines}}</span><span class="{{this.class}}"><a aria-hidden="true" class="lineno" href="#{{../../this.end_path}}-{{../this.hunk_n}}-{{this.line_n}}" id="{{../../this.end_path}}-{{../this.hunk_n}}-{{this.line_n}}" style="color: inherit">{{this.types}}</a>{{this.line}}</span>{{/each}}
{{/each}}
</pre>
                    </div>
                {{/each}}
        </div>
    </div>
</div>
{{/inline}}
{{> layout}}