1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
use camino::{Utf8Path, Utf8PathBuf};
use git2::{Commit, Index, Repository, Signature, Status};
use crate::WikError;
/// based on https://github.com/rust-lang/git2-rs/blob/main/examples/add.rs#L41
pub struct GitContext {
path: Utf8PathBuf,
repo: Repository,
index: Option<Index>,
}
impl GitContext {
pub fn open<P: AsRef<Utf8Path>>(path: P) -> Self {
let path = path.as_ref();
let repo = Repository::open(&path).unwrap();
Self {
path: path.into(),
repo,
index: None,
}
}
pub fn add(&mut self, path: &Utf8Path) -> Result<(), WikError> {
let root = self.path.clone();
if let Ok(stripped) = path.strip_prefix(&root) {
return self.add_rel(stripped);
}
Ok(())
}
pub fn add_rel(&mut self, path: &Utf8Path) -> Result<(), WikError> {
if self.file_dirty(path.as_std_path()) {
self.index()
.add_path(path.as_std_path())
.map_err(|e| WikError::GitError { git: e })
} else {
Ok(())
}
}
pub fn commit<'s, S: AsRef<str>>(
&mut self,
signature: Signature<'s>,
msg: S,
) -> Result<(), WikError> {
let index = self.index();
let oid = index.write_tree()?;
let last_commit = self.head_commit().ok();
let tree = self.repo.find_tree(oid)?;
let parents: &[&git2::Commit] = match last_commit.as_ref() {
Some(cm) => &[cm],
None => &[],
};
self.repo.commit(
Some("HEAD"),
&signature,
&signature,
msg.as_ref(),
&tree,
parents,
)?;
Ok(())
}
fn head_commit<'c>(&'c self) -> Result<Commit<'c>, WikError> {
self.repo
.head()?
.resolve()?
.peel(git2::ObjectType::Commit)?
.into_commit()
.map_err(|_| WikError::GitError {
git: git2::Error::from_str("Couldn't find commit!"),
})
}
fn index(&mut self) -> &mut Index {
self.index.get_or_insert_with(|| self.repo.index().unwrap())
}
pub fn write_index(&mut self) -> Result<(), WikError> {
if let Some(index) = self.index.as_mut() {
index.write().map_err(<_>::into)
} else {
Ok(())
}
}
fn file_dirty(&self, path: &std::path::Path) -> bool {
let status = match self.repo.status_file(path) {
Err(_) => return false,
Ok(status) => status,
};
status.contains(Status::WT_MODIFIED) || status.contains(Status::WT_NEW)
}
}
|