1 use libc::size_t;
2 use std::marker;
3 use std::ops::Range;
4 use std::str;
5 
6 use crate::util::Binding;
7 use crate::{raw, signature, Error, Oid, Signature};
8 
9 /// A reference log of a git repository.
10 pub struct Reflog {
11     raw: *mut raw::git_reflog,
12 }
13 
14 /// An entry inside the reflog of a repository
15 pub struct ReflogEntry<'reflog> {
16     raw: *const raw::git_reflog_entry,
17     _marker: marker::PhantomData<&'reflog Reflog>,
18 }
19 
20 /// An iterator over the entries inside of a reflog.
21 pub struct ReflogIter<'reflog> {
22     range: Range<usize>,
23     reflog: &'reflog Reflog,
24 }
25 
26 impl Reflog {
27     /// Add a new entry to the in-memory reflog.
append( &mut self, new_oid: Oid, committer: &Signature<'_>, msg: Option<&str>, ) -> Result<(), Error>28     pub fn append(
29         &mut self,
30         new_oid: Oid,
31         committer: &Signature<'_>,
32         msg: Option<&str>,
33     ) -> Result<(), Error> {
34         let msg = crate::opt_cstr(msg)?;
35         unsafe {
36             try_call!(raw::git_reflog_append(
37                 self.raw,
38                 new_oid.raw(),
39                 committer.raw(),
40                 msg
41             ));
42         }
43         Ok(())
44     }
45 
46     /// Remove an entry from the reflog by its index
47     ///
48     /// To ensure there's no gap in the log history, set rewrite_previous_entry
49     /// param value to `true`. When deleting entry n, member old_oid of entry
50     /// n-1 (if any) will be updated with the value of member new_oid of entry
51     /// n+1.
remove(&mut self, i: usize, rewrite_previous_entry: bool) -> Result<(), Error>52     pub fn remove(&mut self, i: usize, rewrite_previous_entry: bool) -> Result<(), Error> {
53         unsafe {
54             try_call!(raw::git_reflog_drop(
55                 self.raw,
56                 i as size_t,
57                 rewrite_previous_entry
58             ));
59         }
60         Ok(())
61     }
62 
63     /// Lookup an entry by its index
64     ///
65     /// Requesting the reflog entry with an index of 0 (zero) will return the
66     /// most recently created entry.
get(&self, i: usize) -> Option<ReflogEntry<'_>>67     pub fn get(&self, i: usize) -> Option<ReflogEntry<'_>> {
68         unsafe {
69             let ptr = raw::git_reflog_entry_byindex(self.raw, i as size_t);
70             Binding::from_raw_opt(ptr)
71         }
72     }
73 
74     /// Get the number of log entries in a reflog
len(&self) -> usize75     pub fn len(&self) -> usize {
76         unsafe { raw::git_reflog_entrycount(self.raw) as usize }
77     }
78 
79     /// Return `true ` is there is no log entry in a reflog
is_empty(&self) -> bool80     pub fn is_empty(&self) -> bool {
81         self.len() == 0
82     }
83 
84     /// Get an iterator to all entries inside of this reflog
iter(&self) -> ReflogIter<'_>85     pub fn iter(&self) -> ReflogIter<'_> {
86         ReflogIter {
87             range: 0..self.len(),
88             reflog: self,
89         }
90     }
91 
92     /// Write an existing in-memory reflog object back to disk using an atomic
93     /// file lock.
write(&mut self) -> Result<(), Error>94     pub fn write(&mut self) -> Result<(), Error> {
95         unsafe {
96             try_call!(raw::git_reflog_write(self.raw));
97         }
98         Ok(())
99     }
100 }
101 
102 impl Binding for Reflog {
103     type Raw = *mut raw::git_reflog;
104 
from_raw(raw: *mut raw::git_reflog) -> Reflog105     unsafe fn from_raw(raw: *mut raw::git_reflog) -> Reflog {
106         Reflog { raw: raw }
107     }
raw(&self) -> *mut raw::git_reflog108     fn raw(&self) -> *mut raw::git_reflog {
109         self.raw
110     }
111 }
112 
113 impl Drop for Reflog {
drop(&mut self)114     fn drop(&mut self) {
115         unsafe { raw::git_reflog_free(self.raw) }
116     }
117 }
118 
119 impl<'reflog> ReflogEntry<'reflog> {
120     /// Get the committer of this entry
committer(&self) -> Signature<'_>121     pub fn committer(&self) -> Signature<'_> {
122         unsafe {
123             let ptr = raw::git_reflog_entry_committer(self.raw);
124             signature::from_raw_const(self, ptr)
125         }
126     }
127 
128     /// Get the new oid
id_new(&self) -> Oid129     pub fn id_new(&self) -> Oid {
130         unsafe { Binding::from_raw(raw::git_reflog_entry_id_new(self.raw)) }
131     }
132 
133     /// Get the old oid
id_old(&self) -> Oid134     pub fn id_old(&self) -> Oid {
135         unsafe { Binding::from_raw(raw::git_reflog_entry_id_old(self.raw)) }
136     }
137 
138     /// Get the log message, returning `None` on invalid UTF-8.
message(&self) -> Option<&str>139     pub fn message(&self) -> Option<&str> {
140         self.message_bytes().and_then(|s| str::from_utf8(s).ok())
141     }
142 
143     /// Get the log message as a byte array.
message_bytes(&self) -> Option<&[u8]>144     pub fn message_bytes(&self) -> Option<&[u8]> {
145         unsafe { crate::opt_bytes(self, raw::git_reflog_entry_message(self.raw)) }
146     }
147 }
148 
149 impl<'reflog> Binding for ReflogEntry<'reflog> {
150     type Raw = *const raw::git_reflog_entry;
151 
from_raw(raw: *const raw::git_reflog_entry) -> ReflogEntry<'reflog>152     unsafe fn from_raw(raw: *const raw::git_reflog_entry) -> ReflogEntry<'reflog> {
153         ReflogEntry {
154             raw: raw,
155             _marker: marker::PhantomData,
156         }
157     }
raw(&self) -> *const raw::git_reflog_entry158     fn raw(&self) -> *const raw::git_reflog_entry {
159         self.raw
160     }
161 }
162 
163 impl<'reflog> Iterator for ReflogIter<'reflog> {
164     type Item = ReflogEntry<'reflog>;
next(&mut self) -> Option<ReflogEntry<'reflog>>165     fn next(&mut self) -> Option<ReflogEntry<'reflog>> {
166         self.range.next().and_then(|i| self.reflog.get(i))
167     }
size_hint(&self) -> (usize, Option<usize>)168     fn size_hint(&self) -> (usize, Option<usize>) {
169         self.range.size_hint()
170     }
171 }
172 impl<'reflog> DoubleEndedIterator for ReflogIter<'reflog> {
next_back(&mut self) -> Option<ReflogEntry<'reflog>>173     fn next_back(&mut self) -> Option<ReflogEntry<'reflog>> {
174         self.range.next_back().and_then(|i| self.reflog.get(i))
175     }
176 }
177 impl<'reflog> ExactSizeIterator for ReflogIter<'reflog> {}
178 
179 #[cfg(test)]
180 mod tests {
181     #[test]
smoke()182     fn smoke() {
183         let (_td, repo) = crate::test::repo_init();
184         let mut reflog = repo.reflog("HEAD").unwrap();
185         assert_eq!(reflog.iter().len(), 1);
186         reflog.write().unwrap();
187 
188         let entry = reflog.iter().next().unwrap();
189         assert!(entry.message().is_some());
190 
191         repo.reflog_rename("HEAD", "refs/heads/foo").unwrap();
192         repo.reflog_delete("refs/heads/foo").unwrap();
193     }
194 }
195