##// END OF EJS Templates
head-revs: add a native implementation of the `stop_rev` parameter...
marmoute -
r52872:609700e5 default
parent child Browse files
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 (len == 0) {
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(len, 1);
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 = len - 1; i >= 0; 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)len - 1) < 0)
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 < len; 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_filtered(&HashSet::new(), false)
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_filtered(
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 not_heads
590 (
584 .into_iter()
591 not_heads
585 .enumerate()
592 .into_iter()
586 .filter_map(|(idx, is_not_head)| {
593 .enumerate()
587 if is_not_head {
594 .filter_map(|(idx, is_not_head)| {
588 None
595 if is_not_head {
589 } else {
596 None
590 Some(Revision(idx as BaseRevision))
597 } else {
591 }
598 Some(Revision(idx as BaseRevision))
592 })
599 }
593 .collect()
600 })
601 .collect(),
602 cachable,
603 )
594 };
604 };
595 *self
605 if cachable {
596 .head_revs
606 *self
597 .write()
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_filtered(&HashSet::new(), true)
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_filtered(&filtered_revs, true)
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 .as_ref()
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