771 free(revstates); |
746 free(revstates); |
772 free(tovisit); |
747 free(tovisit); |
773 return NULL; |
748 return NULL; |
774 } |
749 } |
775 |
750 |
|
751 static int add_roots_get_min(indexObject *self, PyObject *roots, char *phases, |
|
752 char phase) |
|
753 { |
|
754 Py_ssize_t len = index_length(self); |
|
755 PyObject *iter; |
|
756 PyObject *item; |
|
757 PyObject *iterator; |
|
758 int rev, minrev = -1; |
|
759 char *node; |
|
760 |
|
761 if (!PySet_Check(roots)) |
|
762 return -2; |
|
763 iterator = PyObject_GetIter(roots); |
|
764 if (iterator == NULL) |
|
765 return -2; |
|
766 while ((item = PyIter_Next(iterator))) { |
|
767 if (node_check(item, &node) == -1) |
|
768 goto failed; |
|
769 rev = index_find_node(self, node, 20); |
|
770 /* null is implicitly public, so negative is invalid */ |
|
771 if (rev < 0 || rev >= len) |
|
772 goto failed; |
|
773 phases[rev] = phase; |
|
774 if (minrev == -1 || minrev > rev) |
|
775 minrev = rev; |
|
776 Py_DECREF(item); |
|
777 } |
|
778 Py_DECREF(iterator); |
|
779 return minrev; |
|
780 failed: |
|
781 Py_DECREF(iterator); |
|
782 Py_DECREF(item); |
|
783 return -2; |
|
784 } |
|
785 |
776 static PyObject *compute_phases_map_sets(indexObject *self, PyObject *args) |
786 static PyObject *compute_phases_map_sets(indexObject *self, PyObject *args) |
777 { |
787 { |
|
788 /* 0: public (untracked), 1: draft, 2: secret, 32: archive, |
|
789 96: internal */ |
|
790 static const char trackedphases[] = {1, 2, 32, 96}; |
|
791 PyObject *ret = NULL; |
778 PyObject *roots = Py_None; |
792 PyObject *roots = Py_None; |
779 PyObject *ret = NULL; |
793 PyObject *idx = NULL; |
|
794 PyObject *pyphase = NULL; |
|
795 PyObject *pyrev = NULL; |
|
796 PyObject *phaseroots = NULL; |
780 PyObject *phasessize = NULL; |
797 PyObject *phasessize = NULL; |
781 PyObject *phaseroots = NULL; |
798 PyObject *phasesets[4] = {NULL, NULL, NULL, NULL}; |
782 PyObject *phaseset = NULL; |
|
783 PyObject *phasessetlist = NULL; |
|
784 PyObject *rev = NULL; |
|
785 Py_ssize_t len = index_length(self); |
799 Py_ssize_t len = index_length(self); |
786 Py_ssize_t numphase = 0; |
800 const char *currentphase; |
787 Py_ssize_t minrevallphases = 0; |
|
788 Py_ssize_t minrevphase = 0; |
|
789 Py_ssize_t i = 0; |
|
790 char *phases = NULL; |
801 char *phases = NULL; |
791 long phase; |
802 int minphaserev = -1, rev, i; |
|
803 const int numphases = (int)(sizeof(phasesets) / sizeof(phasesets[0])); |
792 |
804 |
793 if (!PyArg_ParseTuple(args, "O", &roots)) |
805 if (!PyArg_ParseTuple(args, "O", &roots)) |
794 goto done; |
806 return NULL; |
795 if (roots == NULL || !PyList_Check(roots)) { |
807 if (roots == NULL || !PyDict_Check(roots)) { |
796 PyErr_SetString(PyExc_TypeError, "roots must be a list"); |
808 PyErr_SetString(PyExc_TypeError, "roots must be a dictionary"); |
797 goto done; |
809 return NULL; |
798 } |
810 } |
799 |
811 |
800 phases = calloc( |
812 phases = calloc(len, 1); |
801 len, 1); /* phase per rev: {0: public, 1: draft, 2: secret} */ |
|
802 if (phases == NULL) { |
813 if (phases == NULL) { |
803 PyErr_NoMemory(); |
814 PyErr_NoMemory(); |
804 goto done; |
815 return NULL; |
805 } |
816 } |
806 /* Put the phase information of all the roots in phases */ |
817 |
807 numphase = PyList_GET_SIZE(roots) + 1; |
818 for (i = 0; i < numphases; ++i) { |
808 minrevallphases = len + 1; |
819 pyphase = PyInt_FromLong(trackedphases[i]); |
809 phasessetlist = PyList_New(numphase); |
820 if (pyphase == NULL) |
810 if (phasessetlist == NULL) |
|
811 goto done; |
|
812 |
|
813 PyList_SET_ITEM(phasessetlist, 0, Py_None); |
|
814 Py_INCREF(Py_None); |
|
815 |
|
816 for (i = 0; i < numphase - 1; i++) { |
|
817 phaseroots = PyList_GET_ITEM(roots, i); |
|
818 phaseset = PySet_New(NULL); |
|
819 if (phaseset == NULL) |
|
820 goto release; |
821 goto release; |
821 PyList_SET_ITEM(phasessetlist, i + 1, phaseset); |
822 phaseroots = PyDict_GetItem(roots, pyphase); |
822 if (!PyList_Check(phaseroots)) { |
823 Py_DECREF(pyphase); |
823 PyErr_SetString(PyExc_TypeError, |
824 if (phaseroots == NULL) |
824 "roots item must be a list"); |
825 continue; |
|
826 rev = add_roots_get_min(self, phaseroots, phases, trackedphases[i]); |
|
827 phaseroots = NULL; |
|
828 if (rev == -2) |
825 goto release; |
829 goto release; |
826 } |
830 if (rev != -1 && (minphaserev == -1 || rev < minphaserev)) |
827 minrevphase = |
831 minphaserev = rev; |
828 add_roots_get_min(self, phaseroots, i + 1, phases); |
832 } |
829 if (minrevphase == -2) /* Error from add_roots_get_min */ |
833 |
|
834 for (i = 0; i < numphases; ++i) { |
|
835 phasesets[i] = PySet_New(NULL); |
|
836 if (phasesets[i] == NULL) |
830 goto release; |
837 goto release; |
831 minrevallphases = MIN(minrevallphases, minrevphase); |
838 } |
832 } |
839 |
833 /* Propagate the phase information from the roots to the revs */ |
840 if (minphaserev == -1) |
834 if (minrevallphases != -1) { |
841 minphaserev = len; |
|
842 for (rev = minphaserev; rev < len; ++rev) { |
835 int parents[2]; |
843 int parents[2]; |
836 for (i = minrevallphases; i < len; i++) { |
844 /* |
837 if (index_get_parents(self, i, parents, (int)len - 1) < |
845 * The parent lookup could be skipped for phaseroots, but |
838 0) |
846 * phase --force would historically not recompute them |
839 goto release; |
847 * correctly, leaving descendents with a lower phase around. |
840 set_phase_from_parents(phases, parents[0], parents[1], |
848 * As such, unconditionally recompute the phase. |
841 i); |
849 */ |
842 } |
850 if (index_get_parents(self, rev, parents, (int)len - 1) < 0) |
843 } |
851 goto release; |
844 /* Transform phase list to a python list */ |
852 set_phase_from_parents(phases, parents[0], parents[1], rev); |
|
853 switch (phases[rev]) { |
|
854 case 0: |
|
855 continue; |
|
856 case 1: |
|
857 pyphase = phasesets[0]; |
|
858 break; |
|
859 case 2: |
|
860 pyphase = phasesets[1]; |
|
861 break; |
|
862 case 32: |
|
863 pyphase = phasesets[2]; |
|
864 break; |
|
865 case 96: |
|
866 pyphase = phasesets[3]; |
|
867 break; |
|
868 default: |
|
869 goto release; |
|
870 } |
|
871 pyrev = PyInt_FromLong(rev); |
|
872 if (pyrev == NULL) |
|
873 goto release; |
|
874 if (PySet_Add(pyphase, pyrev) == -1) { |
|
875 Py_DECREF(pyrev); |
|
876 goto release; |
|
877 } |
|
878 Py_DECREF(pyrev); |
|
879 } |
|
880 phaseroots = _dict_new_presized(numphases); |
|
881 if (phaseroots == NULL) |
|
882 goto release; |
|
883 for (int i = 0; i < numphases; ++i) { |
|
884 pyphase = PyInt_FromLong(trackedphases[i]); |
|
885 if (pyphase == NULL) |
|
886 goto release; |
|
887 if (PyDict_SetItem(phaseroots, pyphase, phasesets[i]) == -1) { |
|
888 Py_DECREF(pyphase); |
|
889 goto release; |
|
890 } |
|
891 Py_DECREF(phasesets[i]); |
|
892 phasesets[i] = NULL; |
|
893 } |
845 phasessize = PyInt_FromSsize_t(len); |
894 phasessize = PyInt_FromSsize_t(len); |
846 if (phasessize == NULL) |
895 if (phasessize == NULL) |
847 goto release; |
896 goto release; |
848 for (i = 0; i < len; i++) { |
897 |
849 phase = phases[i]; |
898 ret = PyTuple_Pack(2, phasessize, phaseroots); |
850 /* We only store the sets of phase for non public phase, the |
899 Py_DECREF(phasessize); |
851 * public phase is computed as a difference */ |
900 Py_DECREF(phaseroots); |
852 if (phase != 0) { |
901 return ret; |
853 phaseset = PyList_GET_ITEM(phasessetlist, phase); |
|
854 rev = PyInt_FromSsize_t(i); |
|
855 if (rev == NULL) |
|
856 goto release; |
|
857 PySet_Add(phaseset, rev); |
|
858 Py_XDECREF(rev); |
|
859 } |
|
860 } |
|
861 ret = PyTuple_Pack(2, phasessize, phasessetlist); |
|
862 |
902 |
863 release: |
903 release: |
864 Py_XDECREF(phasessize); |
904 for (i = 0; i < numphases; ++i) |
865 Py_XDECREF(phasessetlist); |
905 Py_XDECREF(phasesets[i]); |
866 done: |
906 Py_XDECREF(phaseroots); |
|
907 |
867 free(phases); |
908 free(phases); |
868 return ret; |
909 return NULL; |
869 } |
910 } |
870 |
911 |
871 static PyObject *index_headrevs(indexObject *self, PyObject *args) |
912 static PyObject *index_headrevs(indexObject *self, PyObject *args) |
872 { |
913 { |
873 Py_ssize_t i, j, len; |
914 Py_ssize_t i, j, len; |