]>
Commit | Line | Data |
---|---|---|
0a29b90c FG |
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. | |
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. | |
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. | |
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 | |
75 | 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 | |
80 | pub fn is_empty(&self) -> bool { | |
81 | self.len() == 0 | |
82 | } | |
83 | ||
84 | /// Get an iterator to all entries inside of this reflog | |
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. | |
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 | ||
105 | unsafe fn from_raw(raw: *mut raw::git_reflog) -> Reflog { | |
106 | Reflog { raw } | |
107 | } | |
108 | fn raw(&self) -> *mut raw::git_reflog { | |
109 | self.raw | |
110 | } | |
111 | } | |
112 | ||
113 | impl Drop for Reflog { | |
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 | |
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 | |
129 | 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 | |
134 | 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. | |
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. | |
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 | ||
152 | unsafe fn from_raw(raw: *const raw::git_reflog_entry) -> ReflogEntry<'reflog> { | |
153 | ReflogEntry { | |
154 | raw, | |
155 | _marker: marker::PhantomData, | |
156 | } | |
157 | } | |
158 | 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>; | |
165 | fn next(&mut self) -> Option<ReflogEntry<'reflog>> { | |
166 | self.range.next().and_then(|i| self.reflog.get(i)) | |
167 | } | |
168 | fn size_hint(&self) -> (usize, Option<usize>) { | |
169 | self.range.size_hint() | |
170 | } | |
171 | } | |
172 | impl<'reflog> DoubleEndedIterator for ReflogIter<'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] | |
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 | } |