Show More
@@ -1237,22 +1237,36 release: | |||||
1237 |
|
1237 | |||
1238 | static PyObject *index_headrevs(indexObject *self, PyObject *args) |
|
1238 | static PyObject *index_headrevs(indexObject *self, PyObject *args) | |
1239 | { |
|
1239 | { | |
1240 | Py_ssize_t i, j, len; |
|
1240 | Py_ssize_t i, j, len, stop_rev; | |
1241 | char *nothead = NULL; |
|
1241 | char *nothead = NULL; | |
1242 | PyObject *heads = NULL; |
|
1242 | PyObject *heads = NULL; | |
1243 | PyObject *filter = NULL; |
|
1243 | PyObject *filter = NULL; | |
1244 | PyObject *filteredrevs = Py_None; |
|
1244 | PyObject *filteredrevs = Py_None; | |
1245 |
|
1245 | PyObject *stop_rev_obj = Py_None; | ||
1246 | if (!PyArg_ParseTuple(args, "|O", &filteredrevs)) { |
|
1246 | ||
|
1247 | if (!PyArg_ParseTuple(args, "|OO", &filteredrevs, &stop_rev_obj)) { | |||
1247 | return NULL; |
|
1248 | return NULL; | |
1248 | } |
|
1249 | } | |
1249 |
|
1250 | |||
1250 | if (self->headrevs && filteredrevs == self->filteredrevs) |
|
1251 | len = index_length(self); | |
|
1252 | if (stop_rev_obj == Py_None) { | |||
|
1253 | stop_rev = len; | |||
|
1254 | } else { | |||
|
1255 | stop_rev = PyLong_AsSsize_t(stop_rev_obj); | |||
|
1256 | if (stop_rev == -1 && PyErr_Occurred() != NULL) { | |||
|
1257 | return NULL; | |||
|
1258 | } | |||
|
1259 | } | |||
|
1260 | ||||
|
1261 | if (self->headrevs && filteredrevs == self->filteredrevs && | |||
|
1262 | stop_rev == len) | |||
1251 | return list_copy(self->headrevs); |
|
1263 | return list_copy(self->headrevs); | |
1252 |
|
1264 | |||
1253 | Py_DECREF(self->filteredrevs); |
|
1265 | if (stop_rev == len) { | |
1254 | self->filteredrevs = filteredrevs; |
|
1266 | Py_DECREF(self->filteredrevs); | |
1255 | Py_INCREF(filteredrevs); |
|
1267 | self->filteredrevs = filteredrevs; | |
|
1268 | Py_INCREF(filteredrevs); | |||
|
1269 | } | |||
1256 |
|
1270 | |||
1257 | if (filteredrevs != Py_None) { |
|
1271 | if (filteredrevs != Py_None) { | |
1258 | filter = PyObject_GetAttrString(filteredrevs, "__contains__"); |
|
1272 | filter = PyObject_GetAttrString(filteredrevs, "__contains__"); | |
@@ -1264,11 +1278,10 static PyObject *index_headrevs(indexObj | |||||
1264 | } |
|
1278 | } | |
1265 | } |
|
1279 | } | |
1266 |
|
1280 | |||
1267 | len = index_length(self); |
|
|||
1268 | heads = PyList_New(0); |
|
1281 | heads = PyList_New(0); | |
1269 | if (heads == NULL) |
|
1282 | if (heads == NULL) | |
1270 | goto bail; |
|
1283 | goto bail; | |
1271 |
if ( |
|
1284 | if (stop_rev == 0) { | |
1272 | if (pylist_append_owned(heads, PyLong_FromLong(-1)) == -1) { |
|
1285 | if (pylist_append_owned(heads, PyLong_FromLong(-1)) == -1) { | |
1273 | Py_XDECREF(nullid); |
|
1286 | Py_XDECREF(nullid); | |
1274 | goto bail; |
|
1287 | goto bail; | |
@@ -1276,13 +1289,13 static PyObject *index_headrevs(indexObj | |||||
1276 | goto done; |
|
1289 | goto done; | |
1277 | } |
|
1290 | } | |
1278 |
|
1291 | |||
1279 |
nothead = calloc( |
|
1292 | nothead = calloc(stop_rev, 1); | |
1280 | if (nothead == NULL) { |
|
1293 | if (nothead == NULL) { | |
1281 | PyErr_NoMemory(); |
|
1294 | PyErr_NoMemory(); | |
1282 | goto bail; |
|
1295 | goto bail; | |
1283 | } |
|
1296 | } | |
1284 |
|
1297 | |||
1285 |
for (i = |
|
1298 | for (i = stop_rev - 1; i >= 0; i--) { | |
1286 | int isfiltered; |
|
1299 | int isfiltered; | |
1287 | int parents[2]; |
|
1300 | int parents[2]; | |
1288 |
|
1301 | |||
@@ -1304,7 +1317,7 static PyObject *index_headrevs(indexObj | |||||
1304 | } |
|
1317 | } | |
1305 | } |
|
1318 | } | |
1306 |
|
1319 | |||
1307 |
if (index_get_parents(self, i, parents, (int) |
|
1320 | if (index_get_parents(self, i, parents, (int)stop_rev - 1) < 0) | |
1308 | goto bail; |
|
1321 | goto bail; | |
1309 | for (j = 0; j < 2; j++) { |
|
1322 | for (j = 0; j < 2; j++) { | |
1310 | if (parents[j] >= 0) |
|
1323 | if (parents[j] >= 0) | |
@@ -1312,7 +1325,7 static PyObject *index_headrevs(indexObj | |||||
1312 | } |
|
1325 | } | |
1313 | } |
|
1326 | } | |
1314 |
|
1327 | |||
1315 |
for (i = 0; i < |
|
1328 | for (i = 0; i < stop_rev; i++) { | |
1316 | if (nothead[i]) |
|
1329 | if (nothead[i]) | |
1317 | continue; |
|
1330 | continue; | |
1318 | if (pylist_append_owned(heads, PyLong_FromSsize_t(i)) == -1) { |
|
1331 | if (pylist_append_owned(heads, PyLong_FromSsize_t(i)) == -1) { | |
@@ -1321,10 +1334,14 static PyObject *index_headrevs(indexObj | |||||
1321 | } |
|
1334 | } | |
1322 |
|
1335 | |||
1323 | done: |
|
1336 | done: | |
1324 | self->headrevs = heads; |
|
|||
1325 | Py_XDECREF(filter); |
|
1337 | Py_XDECREF(filter); | |
1326 | free(nothead); |
|
1338 | free(nothead); | |
1327 | return list_copy(self->headrevs); |
|
1339 | if (stop_rev == len) { | |
|
1340 | self->headrevs = heads; | |||
|
1341 | return list_copy(self->headrevs); | |||
|
1342 | } else { | |||
|
1343 | return heads; | |||
|
1344 | } | |||
1328 | bail: |
|
1345 | bail: | |
1329 | Py_XDECREF(filter); |
|
1346 | Py_XDECREF(filter); | |
1330 | Py_XDECREF(heads); |
|
1347 | Py_XDECREF(heads); |
@@ -696,8 +696,10 class BaseIndexObject: | |||||
696 | p = p[revlog_constants.INDEX_HEADER.size :] |
|
696 | p = p[revlog_constants.INDEX_HEADER.size :] | |
697 | return p |
|
697 | return p | |
698 |
|
698 | |||
699 | def headrevs(self, excluded_revs=None): |
|
699 | def headrevs(self, excluded_revs=None, stop_rev=None): | |
700 | count = len(self) |
|
700 | count = len(self) | |
|
701 | if stop_rev is not None: | |||
|
702 | count = min(count, stop_rev) | |||
701 | if not count: |
|
703 | if not count: | |
702 | return [nullrev] |
|
704 | return [nullrev] | |
703 | # we won't iter over filtered rev so nobody is a head at start |
|
705 | # we won't iter over filtered rev so nobody is a head at start |
@@ -312,11 +312,8 class filteredchangelogmixin: | |||||
312 |
|
312 | |||
313 | def headrevs(self, revs=None, stop_rev=None): |
|
313 | def headrevs(self, revs=None, stop_rev=None): | |
314 | if revs is None: |
|
314 | if revs is None: | |
315 | filtered = self.filteredrevs |
|
315 | return self.index.headrevs(self.filteredrevs, stop_rev) | |
316 | if stop_rev is not None and stop_rev < len(self.index): |
|
316 | # it is ignored from here, so better double check we passed the right argument | |
317 | filtered = set(self.filteredrevs) |
|
|||
318 | filtered.update(range(stop_rev, len(self.index))) |
|
|||
319 | return self.index.headrevs(filtered) |
|
|||
320 | assert stop_rev is None |
|
317 | assert stop_rev is None | |
321 |
|
318 | |||
322 | revs = self._checknofilteredinrevs(revs) |
|
319 | revs = self._checknofilteredinrevs(revs) |
@@ -2382,12 +2382,7 class revlog: | |||||
2382 |
|
2382 | |||
2383 | def headrevs(self, revs=None, stop_rev=None): |
|
2383 | def headrevs(self, revs=None, stop_rev=None): | |
2384 | if revs is None: |
|
2384 | if revs is None: | |
2385 | excluded = None |
|
2385 | return self.index.headrevs(None, stop_rev) | |
2386 | if stop_rev is not None and stop_rev < len(self.index): |
|
|||
2387 | # We should let the native code handle it, but that a |
|
|||
2388 | # simple enough first step. |
|
|||
2389 | excluded = range(stop_rev, len(self.index)) |
|
|||
2390 | return self.index.headrevs(excluded) |
|
|||
2391 | assert stop_rev is None |
|
2386 | assert stop_rev is None | |
2392 | if rustdagop is not None and self.index.rust_ext_compat: |
|
2387 | if rustdagop is not None and self.index.rust_ext_compat: | |
2393 | return rustdagop.headrevs(self.index, revs) |
|
2388 | return rustdagop.headrevs(self.index, revs) |
@@ -111,8 +111,10 class revlogoldindex(list): | |||||
111 | ) |
|
111 | ) | |
112 | return INDEX_ENTRY_V0.pack(*e2) |
|
112 | return INDEX_ENTRY_V0.pack(*e2) | |
113 |
|
113 | |||
114 | def headrevs(self, excluded_revs=None): |
|
114 | def headrevs(self, excluded_revs=None, stop_rev=None): | |
115 | count = len(self) |
|
115 | count = len(self) | |
|
116 | if stop_rev is not None: | |||
|
117 | count = min(count, stop_rev) | |||
116 | if not count: |
|
118 | if not count: | |
117 | return [node.nullrev] |
|
119 | return [node.nullrev] | |
118 | # we won't iter over filtered rev so nobody is a head at start |
|
120 | # we won't iter over filtered rev so nobody is a head at start |
@@ -541,14 +541,15 impl Index { | |||||
541 |
|
541 | |||
542 | /// Return the head revisions of this index |
|
542 | /// Return the head revisions of this index | |
543 | pub fn head_revs(&self) -> Result<Vec<Revision>, GraphError> { |
|
543 | pub fn head_revs(&self) -> Result<Vec<Revision>, GraphError> { | |
544 |
self.head_revs_ |
|
544 | self.head_revs_advanced(&HashSet::new(), None, false) | |
545 | .map(|h| h.unwrap()) |
|
545 | .map(|h| h.unwrap()) | |
546 | } |
|
546 | } | |
547 |
|
547 | |||
548 | /// Return the head revisions of this index |
|
548 | /// Return the head revisions of this index | |
549 |
pub fn head_revs_ |
|
549 | pub fn head_revs_advanced( | |
550 | &self, |
|
550 | &self, | |
551 | filtered_revs: &HashSet<Revision>, |
|
551 | filtered_revs: &HashSet<Revision>, | |
|
552 | stop_rev: Option<Revision>, | |||
552 | py_shortcut: bool, |
|
553 | py_shortcut: bool, | |
553 | ) -> Result<Option<Vec<Revision>>, GraphError> { |
|
554 | ) -> Result<Option<Vec<Revision>>, GraphError> { | |
554 | { |
|
555 | { | |
@@ -560,6 +561,7 impl Index { | |||||
560 | let self_filtered_revs = &guard.1; |
|
561 | let self_filtered_revs = &guard.1; | |
561 | if !self_head_revs.is_empty() |
|
562 | if !self_head_revs.is_empty() | |
562 | && filtered_revs == self_filtered_revs |
|
563 | && filtered_revs == self_filtered_revs | |
|
564 | && stop_rev.is_none() | |||
563 | { |
|
565 | { | |
564 | if py_shortcut { |
|
566 | if py_shortcut { | |
565 | // Don't copy the revs since we've already cached them |
|
567 | // Don't copy the revs since we've already cached them | |
@@ -571,32 +573,42 impl Index { | |||||
571 | } |
|
573 | } | |
572 | } |
|
574 | } | |
573 |
|
575 | |||
574 | let as_vec = if self.is_empty() { |
|
576 | let (as_vec, cachable) = if self.is_empty() { | |
575 | vec![NULL_REVISION] |
|
577 | (vec![NULL_REVISION], true) | |
576 | } else { |
|
578 | } else { | |
577 | let mut not_heads = bitvec![0; self.len()]; |
|
579 | let length: usize = match stop_rev { | |
|
580 | Some(r) => r.0 as usize, | |||
|
581 | None => self.len(), | |||
|
582 | }; | |||
|
583 | let cachable = self.len() == length; | |||
|
584 | let mut not_heads = bitvec![0; length]; | |||
578 | dagops::retain_heads_fast( |
|
585 | dagops::retain_heads_fast( | |
579 | self, |
|
586 | self, | |
580 | not_heads.as_mut_bitslice(), |
|
587 | not_heads.as_mut_bitslice(), | |
581 | filtered_revs, |
|
588 | filtered_revs, | |
582 | )?; |
|
589 | )?; | |
583 |
|
|
590 | ( | |
584 |
|
|
591 | not_heads | |
585 |
. |
|
592 | .into_iter() | |
586 | .filter_map(|(idx, is_not_head)| { |
|
593 | .enumerate() | |
587 |
|
|
594 | .filter_map(|(idx, is_not_head)| { | |
588 |
|
|
595 | if is_not_head { | |
589 |
|
|
596 | None | |
590 | Some(Revision(idx as BaseRevision)) |
|
597 | } else { | |
591 | } |
|
598 | Some(Revision(idx as BaseRevision)) | |
592 |
} |
|
599 | } | |
593 |
|
|
600 | }) | |
|
601 | .collect(), | |||
|
602 | cachable, | |||
|
603 | ) | |||
594 | }; |
|
604 | }; | |
595 | *self |
|
605 | if cachable { | |
596 |
|
|
606 | *self | |
597 |
. |
|
607 | .head_revs | |
598 | .expect("RwLock on Index.head_revs should not be poisoned") = |
|
608 | .write() | |
599 | (as_vec.to_owned(), filtered_revs.to_owned()); |
|
609 | .expect("RwLock on Index.head_revs should not be poisoned") = | |
|
610 | (as_vec.to_owned(), filtered_revs.to_owned()); | |||
|
611 | } | |||
600 | Ok(Some(as_vec)) |
|
612 | Ok(Some(as_vec)) | |
601 | } |
|
613 | } | |
602 |
|
614 | |||
@@ -604,7 +616,7 impl Index { | |||||
604 | pub fn head_revs_shortcut( |
|
616 | pub fn head_revs_shortcut( | |
605 | &self, |
|
617 | &self, | |
606 | ) -> Result<Option<Vec<Revision>>, GraphError> { |
|
618 | ) -> Result<Option<Vec<Revision>>, GraphError> { | |
607 |
self.head_revs_ |
|
619 | self.head_revs_advanced(&HashSet::new(), None, true) | |
608 | } |
|
620 | } | |
609 |
|
621 | |||
610 | /// Return the heads removed and added by advancing from `begin` to `end`. |
|
622 | /// Return the heads removed and added by advancing from `begin` to `end`. |
@@ -27,7 +27,10 use hg::{ | |||||
27 | revlog::{nodemap::NodeMap, Graph, NodePrefix, RevlogError, RevlogIndex}, |
|
27 | revlog::{nodemap::NodeMap, Graph, NodePrefix, RevlogError, RevlogIndex}, | |
28 | BaseRevision, Node, Revision, UncheckedRevision, NULL_REVISION, |
|
28 | BaseRevision, Node, Revision, UncheckedRevision, NULL_REVISION, | |
29 | }; |
|
29 | }; | |
30 | use std::{cell::RefCell, collections::HashMap}; |
|
30 | use std::{ | |
|
31 | cell::RefCell, | |||
|
32 | collections::{HashMap, HashSet}, | |||
|
33 | }; | |||
31 | use vcsgraph::graph::Graph as VCSGraph; |
|
34 | use vcsgraph::graph::Graph as VCSGraph; | |
32 |
|
35 | |||
33 | pub struct PySharedIndex { |
|
36 | pub struct PySharedIndex { | |
@@ -305,12 +308,13 py_class!(pub class Index |py| { | |||||
305 |
|
308 | |||
306 | /// get head revisions |
|
309 | /// get head revisions | |
307 | def headrevs(&self, *args, **_kw) -> PyResult<PyObject> { |
|
310 | def headrevs(&self, *args, **_kw) -> PyResult<PyObject> { | |
308 | let filtered_revs = match &args.len(py) { |
|
311 | let (filtered_revs, stop_rev) = match &args.len(py) { | |
309 | 0 => Ok(py.None()), |
|
312 | 0 => Ok((py.None(), py.None())), | |
310 | 1 => Ok(args.get_item(py, 0)), |
|
313 | 1 => Ok((args.get_item(py, 0), py.None())), | |
|
314 | 2 => Ok((args.get_item(py, 0), args.get_item(py, 1))), | |||
311 | _ => Err(PyErr::new::<cpython::exc::TypeError, _>(py, "too many arguments")), |
|
315 | _ => Err(PyErr::new::<cpython::exc::TypeError, _>(py, "too many arguments")), | |
312 | }?; |
|
316 | }?; | |
313 | self.inner_headrevs(py, &filtered_revs) |
|
317 | self.inner_headrevs(py, &filtered_revs, &stop_rev) | |
314 | } |
|
318 | } | |
315 |
|
319 | |||
316 | /// get head nodeids |
|
320 | /// get head nodeids | |
@@ -821,29 +825,62 impl Index { | |||||
821 | &self, |
|
825 | &self, | |
822 | py: Python, |
|
826 | py: Python, | |
823 | filtered_revs: &PyObject, |
|
827 | filtered_revs: &PyObject, | |
|
828 | stop_rev: &PyObject, | |||
824 | ) -> PyResult<PyObject> { |
|
829 | ) -> PyResult<PyObject> { | |
825 | let index = &*self.index(py).borrow(); |
|
830 | let index = &*self.index(py).borrow(); | |
826 |
|
831 | let stop_rev = match stop_rev.is_none(py) { | ||
827 | let from_core = match filtered_revs.is_none(py) { |
|
|||
828 | true => index.head_revs_shortcut(), |
|
|||
829 | false => { |
|
832 | false => { | |
|
833 | let rev = stop_rev.extract::<i32>(py)?; | |||
|
834 | if 0 <= rev && rev < index.len() as BaseRevision { | |||
|
835 | Some(Revision(rev)) | |||
|
836 | } else { | |||
|
837 | None | |||
|
838 | } | |||
|
839 | } | |||
|
840 | true => None, | |||
|
841 | }; | |||
|
842 | let from_core = match (filtered_revs.is_none(py), stop_rev.is_none()) { | |||
|
843 | (true, true) => index.head_revs_shortcut(), | |||
|
844 | (true, false) => { | |||
|
845 | index.head_revs_advanced(&HashSet::new(), stop_rev, false) | |||
|
846 | } | |||
|
847 | _ => { | |||
830 | let filtered_revs = |
|
848 | let filtered_revs = | |
831 | rev_pyiter_collect(py, filtered_revs, index)?; |
|
849 | rev_pyiter_collect(py, filtered_revs, index)?; | |
832 |
index.head_revs_ |
|
850 | index.head_revs_advanced( | |
|
851 | &filtered_revs, | |||
|
852 | stop_rev, | |||
|
853 | stop_rev.is_none(), | |||
|
854 | ) | |||
833 | } |
|
855 | } | |
834 | }; |
|
856 | }; | |
835 |
|
857 | |||
836 | if let Some(new_heads) = from_core.map_err(|e| graph_error(py, e))? { |
|
858 | if stop_rev.is_some() { | |
837 | self.cache_new_heads_py_list(&new_heads, py); |
|
859 | // we don't cache result for now | |
838 | } |
|
860 | let new_heads = from_core | |
|
861 | .map_err(|e| graph_error(py, e))? | |||
|
862 | .expect("this case should not be cached yet"); | |||
839 |
|
863 | |||
840 | Ok(self |
|
864 | let as_vec: Vec<PyObject> = new_heads | |
841 | .head_revs_py_list(py) |
|
865 | .iter() | |
842 | .borrow() |
|
866 | .map(|r| PyRevision::from(*r).into_py_object(py).into_object()) | |
843 |
. |
|
867 | .collect(); | |
844 | .expect("head revs should be cached") |
|
868 | Ok(PyList::new(py, &as_vec).into_object()) | |
845 | .clone_ref(py) |
|
869 | } else { | |
846 | .into_object()) |
|
870 | if let Some(new_heads) = | |
|
871 | from_core.map_err(|e| graph_error(py, e))? | |||
|
872 | { | |||
|
873 | self.cache_new_heads_py_list(&new_heads, py); | |||
|
874 | } | |||
|
875 | ||||
|
876 | Ok(self | |||
|
877 | .head_revs_py_list(py) | |||
|
878 | .borrow() | |||
|
879 | .as_ref() | |||
|
880 | .expect("head revs should be cached") | |||
|
881 | .clone_ref(py) | |||
|
882 | .into_object()) | |||
|
883 | } | |||
847 | } |
|
884 | } | |
848 |
|
885 | |||
849 | fn check_revision( |
|
886 | fn check_revision( |
General Comments 0
You need to be logged in to leave comments.
Login now