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