Transfer git implementation
This commit is contained in:
parent
035f8b2872
commit
b1dd07aed1
11 changed files with 542 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
Cargo.lock
|
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "git-social-server"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Vladan Popovic <vladanovic@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
chrono = { version = "*" }
|
||||||
|
|
||||||
|
[dependencies.git2]
|
||||||
|
version = "*"
|
||||||
|
default-features = false
|
||||||
|
features = []
|
22
src/errors.rs
Normal file
22
src/errors.rs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
use git2::Error as GitError;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SocialError {
|
||||||
|
pub message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<GitError> for SocialError {
|
||||||
|
fn from(err: GitError) -> Self {
|
||||||
|
Self {
|
||||||
|
message: err.message().to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for SocialError {
|
||||||
|
fn from(err: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
message: err.to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
src/git/blame.rs
Normal file
50
src/git/blame.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
use crate::errors::SocialError;
|
||||||
|
use crate::git::repo::SocialRepo;
|
||||||
|
use crate::git::{SocialBlame, SocialBlameHunk};
|
||||||
|
use git2::{Blame, BlameHunk, BlameOptions};
|
||||||
|
use std::io::{BufRead, BufReader};
|
||||||
|
|
||||||
|
fn into_social_blame_hunk(bh: BlameHunk, line: String) -> SocialBlameHunk {
|
||||||
|
SocialBlameHunk {
|
||||||
|
signature: bh.final_signature().into(),
|
||||||
|
commit_id: format!("{}", bh.orig_commit_id()),
|
||||||
|
line,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_social_blame(file: String, blame: Blame<'_>, buffer: BufReader<&[u8]>) -> SocialBlame {
|
||||||
|
let mut hunks: Vec<SocialBlameHunk> = Vec::new();
|
||||||
|
for (i, line) in buffer.lines().enumerate() {
|
||||||
|
if let (Ok(line), Some(hunk)) = (line, blame.get_line(i + 1)) {
|
||||||
|
let shunk = into_social_blame_hunk(hunk, line);
|
||||||
|
hunks.push(shunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SocialBlame { file, hunks }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait GitBlame {
|
||||||
|
fn blame(&self, from_oid: &str, path: &str) -> Result<SocialBlame, SocialError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GitBlame for SocialRepo {
|
||||||
|
fn blame(&self, from_oid: &str, path: &str) -> Result<SocialBlame, SocialError> {
|
||||||
|
let revspec = self.repo.revparse_single(from_oid)?;
|
||||||
|
let commit_id = revspec.id();
|
||||||
|
|
||||||
|
let spec = format!("{}:{}", commit_id, path);
|
||||||
|
let object = self.repo.revparse_single(&spec[..])?;
|
||||||
|
let blob = self.repo.find_blob(object.id())?;
|
||||||
|
let reader = BufReader::new(blob.content());
|
||||||
|
|
||||||
|
let mut opts = BlameOptions::new();
|
||||||
|
opts.track_copies_any_commit_copies(true)
|
||||||
|
.track_copies_same_commit_copies(true)
|
||||||
|
.newest_commit(revspec.id());
|
||||||
|
|
||||||
|
self.repo
|
||||||
|
.blame_file(std::path::Path::new(path), Some(&mut opts))
|
||||||
|
.map(|blame| into_social_blame(path.to_owned(), blame, reader))
|
||||||
|
.map_err(|d| d.into())
|
||||||
|
}
|
||||||
|
}
|
47
src/git/diff.rs
Normal file
47
src/git/diff.rs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
use crate::errors::SocialError;
|
||||||
|
use crate::git::repo::SocialRepo;
|
||||||
|
use crate::git::SocialDiff;
|
||||||
|
use git2::{Diff, DiffOptions};
|
||||||
|
use git2::{DiffFormat, DiffLine};
|
||||||
|
|
||||||
|
fn into_social_diff(d: Diff<'_>) -> SocialDiff {
|
||||||
|
let mut lines: Vec<String> = Vec::new();
|
||||||
|
d.print(DiffFormat::Patch, |_, _, line: DiffLine| {
|
||||||
|
if let Ok(l) = String::from_utf8(line.content().to_vec()) {
|
||||||
|
lines.push(format!("{} {}", line.origin(), l));
|
||||||
|
} else {
|
||||||
|
lines.push("? cannot convert utf8 string on this line!".to_string());
|
||||||
|
}
|
||||||
|
true
|
||||||
|
})
|
||||||
|
.unwrap_or(());
|
||||||
|
SocialDiff { lines }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Show {
|
||||||
|
fn show(&self, from_oid: &str, to_oid: &str) -> Result<SocialDiff, SocialError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Show for SocialRepo {
|
||||||
|
fn show(&self, from_oid: &str, to_oid: &str) -> Result<SocialDiff, SocialError> {
|
||||||
|
let mut opts = DiffOptions::new();
|
||||||
|
opts.reverse(false)
|
||||||
|
.force_text(false)
|
||||||
|
.ignore_whitespace_eol(false)
|
||||||
|
.ignore_whitespace_change(false)
|
||||||
|
.ignore_whitespace(false)
|
||||||
|
.include_ignored(false)
|
||||||
|
.include_untracked(false)
|
||||||
|
.patience(true)
|
||||||
|
.context_lines(5)
|
||||||
|
.minimal(false);
|
||||||
|
|
||||||
|
let t1 = self.get_tree(from_oid).ok();
|
||||||
|
let t2 = self.get_tree(to_oid).ok();
|
||||||
|
|
||||||
|
self.repo
|
||||||
|
.diff_tree_to_tree(t1.as_ref(), t2.as_ref(), Some(&mut opts))
|
||||||
|
.map(into_social_diff)
|
||||||
|
.map_err(|d| d.into())
|
||||||
|
}
|
||||||
|
}
|
75
src/git/log.rs
Normal file
75
src/git/log.rs
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
use crate::errors::SocialError;
|
||||||
|
use crate::git::repo::SocialRepo;
|
||||||
|
use crate::git::SocialCommit;
|
||||||
|
|
||||||
|
pub trait Log {
|
||||||
|
fn log<'a>(
|
||||||
|
&'a self,
|
||||||
|
start: Option<String>,
|
||||||
|
end: Option<String>,
|
||||||
|
count: usize,
|
||||||
|
short: bool,
|
||||||
|
) -> Result<Box<dyn Iterator<Item = SocialCommit> + 'a>, SocialError>;
|
||||||
|
|
||||||
|
fn commit(&self, rev: &str) -> Result<SocialCommit, SocialError>;
|
||||||
|
fn first_commit(&self) -> Result<SocialCommit, SocialError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Log for SocialRepo {
|
||||||
|
fn log<'a>(
|
||||||
|
&'a self,
|
||||||
|
start: Option<String>,
|
||||||
|
end: Option<String>,
|
||||||
|
count: usize,
|
||||||
|
short: bool,
|
||||||
|
) -> Result<Box<dyn Iterator<Item = SocialCommit> + 'a>, SocialError> {
|
||||||
|
let mut git_revwalk = self.repo.revwalk()?;
|
||||||
|
let start = start.or_else(|| Some("HEAD".to_owned()));
|
||||||
|
|
||||||
|
if let (Some(s), Some(e)) = (&start, &end) {
|
||||||
|
git_revwalk.push_range(&format!("{}..{}", s, e))?;
|
||||||
|
} else if let Some(s) = start {
|
||||||
|
git_revwalk
|
||||||
|
.push_ref(&s)
|
||||||
|
.or(git_revwalk.push(self.repo.revparse(&s)?.from().unwrap().id()))?;
|
||||||
|
} else {
|
||||||
|
git_revwalk.push_head()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Box::new(
|
||||||
|
git_revwalk
|
||||||
|
.filter_map(move |idres| idres.map(|id| self.repo.find_commit(id.clone())).ok())
|
||||||
|
.flatten()
|
||||||
|
.map(|c| c.into())
|
||||||
|
.map(move |c: SocialCommit| if short { c.oneline() } else { c })
|
||||||
|
.take(count),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn commit(&self, rev: &str) -> Result<SocialCommit, SocialError> {
|
||||||
|
self.repo
|
||||||
|
.revparse_single(rev.as_ref())
|
||||||
|
.and_then(|o| self.repo.find_commit(o.id()))
|
||||||
|
.map(|c| c.into())
|
||||||
|
.map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn first_commit(&self) -> Result<SocialCommit, SocialError> {
|
||||||
|
let mut revwalk = self.repo.revwalk()?;
|
||||||
|
revwalk.set_sorting(git2::Sort::REVERSE | git2::Sort::TOPOLOGICAL)?;
|
||||||
|
revwalk.push_head()?;
|
||||||
|
revwalk
|
||||||
|
.filter_map(move |idres| idres.map(|id| self.repo.find_commit(id.clone())).ok())
|
||||||
|
.take(1)
|
||||||
|
.next()
|
||||||
|
.map(|r| {
|
||||||
|
r.map_err(|_| SocialError {
|
||||||
|
message: "Invalid commit reference!".to_string(),
|
||||||
|
})
|
||||||
|
.map(|c| c.into())
|
||||||
|
})
|
||||||
|
.ok_or(SocialError {
|
||||||
|
message: "Repo doesn't contain any commits!".to_owned(),
|
||||||
|
})?
|
||||||
|
}
|
||||||
|
}
|
146
src/git/mod.rs
Normal file
146
src/git/mod.rs
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
pub(crate) mod blame;
|
||||||
|
pub(crate) mod diff;
|
||||||
|
pub(crate) mod log;
|
||||||
|
pub(crate) mod references;
|
||||||
|
pub(crate) mod repo;
|
||||||
|
pub(crate) mod tree;
|
||||||
|
|
||||||
|
use chrono::offset::{Local, TimeZone};
|
||||||
|
use chrono::DateTime;
|
||||||
|
use git2::{Commit, Signature};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SocialSignature {
|
||||||
|
pub when: DateTime<Local>,
|
||||||
|
pub name: String,
|
||||||
|
pub email: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SocialSignature {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
when: Local.timestamp(0, 0),
|
||||||
|
name: "".to_string(),
|
||||||
|
email: "".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SocialCommit {
|
||||||
|
pub id: String,
|
||||||
|
pub time: DateTime<Local>,
|
||||||
|
pub message: String,
|
||||||
|
pub author: SocialSignature,
|
||||||
|
pub committer: SocialSignature,
|
||||||
|
pub diff: Option<SocialDiff>,
|
||||||
|
pub parent_ids: Option<Vec<String>>, // TODO: Use iterator instead of vec.
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SocialCommit {
|
||||||
|
pub fn oneline(self) -> Self {
|
||||||
|
Self {
|
||||||
|
message: self.message.lines().next().unwrap_or("").to_owned(),
|
||||||
|
parent_ids: None,
|
||||||
|
diff: None,
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SocialCommit {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
id: "".to_string(),
|
||||||
|
time: Local.timestamp(0, 0),
|
||||||
|
message: "".to_string(),
|
||||||
|
author: SocialSignature::default(),
|
||||||
|
committer: SocialSignature::default(),
|
||||||
|
diff: None,
|
||||||
|
parent_ids: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Signature<'_>> for SocialSignature {
|
||||||
|
fn from(s: Signature<'_>) -> Self {
|
||||||
|
Self {
|
||||||
|
name: s.name().unwrap_or("").to_owned(),
|
||||||
|
email: s.email().unwrap_or("").to_owned(),
|
||||||
|
when: Local.timestamp(s.when().seconds(), 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Commit<'_>> for SocialCommit {
|
||||||
|
fn from(c: Commit<'_>) -> Self {
|
||||||
|
Self {
|
||||||
|
id: format!("{}", c.id()),
|
||||||
|
time: Local.timestamp(c.time().seconds(), 0),
|
||||||
|
message: c.message().unwrap_or("").to_owned(),
|
||||||
|
author: c.author().into(),
|
||||||
|
committer: c.committer().into(),
|
||||||
|
diff: None,
|
||||||
|
parent_ids: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct SocialDiff {
|
||||||
|
pub lines: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct SocialTreeEntry {
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
pub kind: String,
|
||||||
|
pub filemode: i32,
|
||||||
|
pub size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct SocialFileContent {
|
||||||
|
pub path: String,
|
||||||
|
pub content: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct SocialBranch {
|
||||||
|
pub name: String,
|
||||||
|
pub head: SocialCommit,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct SocialTag {
|
||||||
|
pub name: String,
|
||||||
|
pub head: SocialCommit,
|
||||||
|
pub annotation: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct SocialRefs {
|
||||||
|
pub branches: Vec<SocialBranch>,
|
||||||
|
pub tags: Vec<SocialTag>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct SocialBlameHunk {
|
||||||
|
pub commit_id: String,
|
||||||
|
pub line: String,
|
||||||
|
pub signature: SocialSignature,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct SocialBlame {
|
||||||
|
pub file: String,
|
||||||
|
pub hunks: Vec<SocialBlameHunk>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct SocialRepoInfo {
|
||||||
|
pub name: String,
|
||||||
|
pub author: SocialSignature,
|
||||||
|
pub description: String,
|
||||||
|
}
|
66
src/git/references.rs
Normal file
66
src/git/references.rs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
use crate::errors::SocialError;
|
||||||
|
use crate::git::repo::SocialRepo;
|
||||||
|
use crate::git::{SocialBranch, SocialCommit, SocialTag};
|
||||||
|
use git2::{Branch, BranchType, Commit};
|
||||||
|
|
||||||
|
fn into_social_branch(b: Branch<'_>) -> SocialBranch {
|
||||||
|
SocialBranch {
|
||||||
|
name: b.name().unwrap_or(Some("")).unwrap_or("").to_owned(),
|
||||||
|
head: SocialCommit::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_social_tag(name: Option<&str>) -> SocialTag {
|
||||||
|
SocialTag {
|
||||||
|
name: name.unwrap_or("").to_owned(),
|
||||||
|
head: SocialCommit::default(),
|
||||||
|
annotation: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Refs {
|
||||||
|
fn branches(&self) -> Result<Vec<SocialBranch>, SocialError>;
|
||||||
|
fn tags(&self) -> Result<Vec<SocialTag>, SocialError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Refs for SocialRepo {
|
||||||
|
fn branches(&self) -> Result<Vec<SocialBranch>, SocialError> {
|
||||||
|
let all_branches = self.repo.branches(Some(BranchType::Local))?;
|
||||||
|
let mut resulting_brances: Vec<SocialBranch> = vec![];
|
||||||
|
|
||||||
|
for r in all_branches {
|
||||||
|
r.map(|(branch, _)| {
|
||||||
|
let mut sb = into_social_branch(branch);
|
||||||
|
self.repo
|
||||||
|
.revparse_single(&sb.name)
|
||||||
|
.map(|obj| {
|
||||||
|
obj.peel_to_commit()
|
||||||
|
.map(|c: Commit| {
|
||||||
|
sb.head = c.into();
|
||||||
|
resulting_brances.push(sb);
|
||||||
|
})
|
||||||
|
.unwrap_or(());
|
||||||
|
})
|
||||||
|
.unwrap_or(());
|
||||||
|
})
|
||||||
|
.unwrap_or(());
|
||||||
|
}
|
||||||
|
Ok(resulting_brances)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tags(&self) -> Result<Vec<SocialTag>, SocialError> {
|
||||||
|
let tagnames = self.repo.tag_names(None)?;
|
||||||
|
let mut result: Vec<SocialTag> = vec![];
|
||||||
|
|
||||||
|
for t in tagnames.into_iter() {
|
||||||
|
let mut st = into_social_tag(t);
|
||||||
|
let _ = self.repo.revparse_single(&st.name).map(|obj| {
|
||||||
|
obj.peel_to_commit().map(|c: Commit| {
|
||||||
|
st.head = c.into();
|
||||||
|
result.push(st);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
}
|
31
src/git/repo.rs
Normal file
31
src/git/repo.rs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
use crate::errors::SocialError;
|
||||||
|
use git2::{Error as GitError, Repository, Tree};
|
||||||
|
use std::{fs::read_to_string, path::Path};
|
||||||
|
|
||||||
|
pub struct SocialRepo {
|
||||||
|
pub repo: Repository,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'repo> SocialRepo {
|
||||||
|
pub fn new(path: &str) -> Result<Self, SocialError> {
|
||||||
|
Repository::open(path)
|
||||||
|
.map(|repo| Self { repo })
|
||||||
|
.map_err(|_| {
|
||||||
|
let err = "fatal: not a git repository";
|
||||||
|
SocialError::from(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_tree(&self, rev: &str) -> Result<Tree, GitError> {
|
||||||
|
self.repo
|
||||||
|
.revparse_single(rev)
|
||||||
|
.and_then(|obj| obj.peel_to_tree())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_description(&self) -> String {
|
||||||
|
// The description is in a file named "description",
|
||||||
|
// in the .git directory, or the root directory if bare.
|
||||||
|
let git_path = self.repo.path().join(Path::new("description"));
|
||||||
|
read_to_string(git_path).unwrap_or("".to_string())
|
||||||
|
}
|
||||||
|
}
|
82
src/git/tree.rs
Normal file
82
src/git/tree.rs
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
use crate::errors::SocialError;
|
||||||
|
use crate::git::repo::SocialRepo;
|
||||||
|
use crate::git::{SocialFileContent, SocialTreeEntry};
|
||||||
|
use git2::{Blob, Error as GitError, Object, ObjectType, TreeEntry};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
fn into_social_tree_entry(te: TreeEntry<'_>, size: usize) -> SocialTreeEntry {
|
||||||
|
SocialTreeEntry {
|
||||||
|
id: format!("{}", te.id()),
|
||||||
|
name: te.name().unwrap_or("").to_owned(),
|
||||||
|
kind: te.kind().map(|ot| ot.str()).unwrap_or("any").to_owned(),
|
||||||
|
filemode: te.filemode(),
|
||||||
|
size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait LsTree {
|
||||||
|
fn ls_tree(&self, rev: &str, path: &Path) -> Result<Vec<SocialTreeEntry>, SocialError>;
|
||||||
|
fn show_file(&self, rev: &str, path: &Path) -> Result<SocialFileContent, SocialError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LsTree for SocialRepo {
|
||||||
|
fn ls_tree(&self, rev: &str, path: &Path) -> Result<Vec<SocialTreeEntry>, SocialError> {
|
||||||
|
let matched_tree = if path.to_str().unwrap_or("") == "" {
|
||||||
|
self.get_tree(rev)?
|
||||||
|
} else {
|
||||||
|
self.get_tree(rev)
|
||||||
|
.and_then(|t| t.get_path(&path))
|
||||||
|
.and_then(|entry: TreeEntry| self.repo.find_tree(entry.id()))?
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut result: Vec<SocialTreeEntry> = vec![];
|
||||||
|
let mut files: Vec<SocialTreeEntry> = vec![];
|
||||||
|
for entry in matched_tree.iter() {
|
||||||
|
match entry.kind() {
|
||||||
|
Some(git2::ObjectType::Blob) => {
|
||||||
|
self.repo
|
||||||
|
.revparse_single(&format!("{}", entry.id()))
|
||||||
|
.map(|obj: Object| {
|
||||||
|
obj.as_blob().map(|b: &Blob| {
|
||||||
|
files.push(into_social_tree_entry(entry, b.content().len()))
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.unwrap_or(());
|
||||||
|
}
|
||||||
|
Some(git2::ObjectType::Tree) => result.push(into_social_tree_entry(entry, 0)),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.extend(files);
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_file(&self, rev: &str, path: &Path) -> Result<SocialFileContent, SocialError> {
|
||||||
|
self.get_tree(rev)
|
||||||
|
.and_then(|t| t.get_path(&path))
|
||||||
|
.and_then(|entry: TreeEntry| match entry.kind() {
|
||||||
|
Some(ObjectType::Blob) => self
|
||||||
|
.repo
|
||||||
|
.revparse_single(&format!("{}", entry.id()))
|
||||||
|
.and_then(|obj: Object| {
|
||||||
|
obj.as_blob()
|
||||||
|
.ok_or_else(|| {
|
||||||
|
GitError::from_str(&format!("Cannot get blob for {}", obj.id()))
|
||||||
|
})
|
||||||
|
.and_then(|blob: &Blob| {
|
||||||
|
String::from_utf8(blob.content().to_vec()).map_err(|_| {
|
||||||
|
GitError::from_str(
|
||||||
|
format!("Cannot read file {}", path.display()).as_ref(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.map(|content| SocialFileContent {
|
||||||
|
content,
|
||||||
|
path: format!("{}", path.display()),
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
_ => Result::Err(GitError::from_str("Object is not a Blob!")),
|
||||||
|
})
|
||||||
|
.map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
}
|
8
src/main.rs
Normal file
8
src/main.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
mod errors;
|
||||||
|
mod git;
|
||||||
|
|
||||||
|
use git::repo::SocialRepo;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
SocialRepo::new("/home/vladan/dev/git.social/git-social-server").ok();
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue