##// END OF EJS Templates
histedit: switch state to store node instead of ctx...
histedit: switch state to store node instead of ctx Currently, if the node no longer exists, the state object fails to load and pukes with an exception. Changing the state object to only store the node allows callers to handle these cases. For instance, in bootstrapcontinue we can now detect that the node doesn't exist and exit gracefully. The alternative is to have the state object store something like None when the node doesn't exist, but then outside callers won't be able to access the old node for recovery (unless we store both the node and the ctx, but why bother). More importantly it allows us to detect this case when doing hg histedit --abort. Currently this situation results in both --continue and --abort being broken and the user has to rm .hg/histedit-state to unwedge their repo. (description by Durham Goode)

File last commit:

r23960:bca4b6f1 stable
r24112:5d5ec4fb default
Show More
dirs.c
293 lines | 6.0 KiB | text/x-c | CLexer
/*
dirs.c - dynamic directory diddling for dirstates
Copyright 2013 Facebook
This software may be used and distributed according to the terms of
the GNU General Public License, incorporated herein by reference.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "util.h"
/*
* This is a multiset of directory names, built from the files that
* appear in a dirstate or manifest.
*
* A few implementation notes:
*
* We modify Python integers for refcounting, but those integers are
* never visible to Python code.
*
* We mutate strings in-place, but leave them immutable once they can
* be seen by Python code.
*/
typedef struct {
PyObject_HEAD
PyObject *dict;
} dirsObject;
static inline Py_ssize_t _finddir(PyObject *path, Py_ssize_t pos)
{
const char *s = PyString_AS_STRING(path);
while (pos != -1) {
if (s[pos] == '/')
break;
pos -= 1;
}
return pos;
}
static int _addpath(PyObject *dirs, PyObject *path)
{
const char *cpath = PyString_AS_STRING(path);
Py_ssize_t pos = PyString_GET_SIZE(path);
PyObject *key = NULL;
int ret = -1;
while ((pos = _finddir(path, pos - 1)) != -1) {
PyObject *val;
/* It's likely that every prefix already has an entry
in our dict. Try to avoid allocating and
deallocating a string for each prefix we check. */
if (key != NULL)
((PyStringObject *)key)->ob_shash = -1;
else {
/* Force Python to not reuse a small shared string. */
key = PyString_FromStringAndSize(cpath,
pos < 2 ? 2 : pos);
if (key == NULL)
goto bail;
}
PyString_GET_SIZE(key) = pos;
PyString_AS_STRING(key)[pos] = '\0';
val = PyDict_GetItem(dirs, key);
if (val != NULL) {
PyInt_AS_LONG(val) += 1;
continue;
}
/* Force Python to not reuse a small shared int. */
val = PyInt_FromLong(0x1eadbeef);
if (val == NULL)
goto bail;
PyInt_AS_LONG(val) = 1;
ret = PyDict_SetItem(dirs, key, val);
Py_DECREF(val);
if (ret == -1)
goto bail;
Py_CLEAR(key);
}
ret = 0;
bail:
Py_XDECREF(key);
return ret;
}
static int _delpath(PyObject *dirs, PyObject *path)
{
Py_ssize_t pos = PyString_GET_SIZE(path);
PyObject *key = NULL;
int ret = -1;
while ((pos = _finddir(path, pos - 1)) != -1) {
PyObject *val;
key = PyString_FromStringAndSize(PyString_AS_STRING(path), pos);
if (key == NULL)
goto bail;
val = PyDict_GetItem(dirs, key);
if (val == NULL) {
PyErr_SetString(PyExc_ValueError,
"expected a value, found none");
goto bail;
}
if (--PyInt_AS_LONG(val) <= 0 &&
PyDict_DelItem(dirs, key) == -1)
goto bail;
Py_CLEAR(key);
}
ret = 0;
bail:
Py_XDECREF(key);
return ret;
}
static int dirs_fromdict(PyObject *dirs, PyObject *source, char skipchar)
{
PyObject *key, *value;
Py_ssize_t pos = 0;
while (PyDict_Next(source, &pos, &key, &value)) {
if (!PyString_Check(key)) {
PyErr_SetString(PyExc_TypeError, "expected string key");
return -1;
}
if (skipchar) {
if (!dirstate_tuple_check(value)) {
PyErr_SetString(PyExc_TypeError,
"expected a dirstate tuple");
return -1;
}
if (((dirstateTupleObject *)value)->state == skipchar)
continue;
}
if (_addpath(dirs, key) == -1)
return -1;
}
return 0;
}
static int dirs_fromiter(PyObject *dirs, PyObject *source)
{
PyObject *iter, *item = NULL;
int ret;
iter = PyObject_GetIter(source);
if (iter == NULL)
return -1;
while ((item = PyIter_Next(iter)) != NULL) {
if (!PyString_Check(item)) {
PyErr_SetString(PyExc_TypeError, "expected string");
break;
}
if (_addpath(dirs, item) == -1)
break;
Py_CLEAR(item);
}
ret = PyErr_Occurred() ? -1 : 0;
Py_DECREF(iter);
Py_XDECREF(item);
return ret;
}
/*
* Calculate a refcounted set of directory names for the files in a
* dirstate.
*/
static int dirs_init(dirsObject *self, PyObject *args)
{
PyObject *dirs = NULL, *source = NULL;
char skipchar = 0;
int ret = -1;
self->dict = NULL;
if (!PyArg_ParseTuple(args, "|Oc:__init__", &source, &skipchar))
return -1;
dirs = PyDict_New();
if (dirs == NULL)
return -1;
if (source == NULL)
ret = 0;
else if (PyDict_Check(source))
ret = dirs_fromdict(dirs, source, skipchar);
else if (skipchar)
PyErr_SetString(PyExc_ValueError,
"skip character is only supported "
"with a dict source");
else
ret = dirs_fromiter(dirs, source);
if (ret == -1)
Py_XDECREF(dirs);
else
self->dict = dirs;
return ret;
}
PyObject *dirs_addpath(dirsObject *self, PyObject *args)
{
PyObject *path;
if (!PyArg_ParseTuple(args, "O!:addpath", &PyString_Type, &path))
return NULL;
if (_addpath(self->dict, path) == -1)
return NULL;
Py_RETURN_NONE;
}
static PyObject *dirs_delpath(dirsObject *self, PyObject *args)
{
PyObject *path;
if (!PyArg_ParseTuple(args, "O!:delpath", &PyString_Type, &path))
return NULL;
if (_delpath(self->dict, path) == -1)
return NULL;
Py_RETURN_NONE;
}
static int dirs_contains(dirsObject *self, PyObject *value)
{
return PyString_Check(value) ? PyDict_Contains(self->dict, value) : 0;
}
static void dirs_dealloc(dirsObject *self)
{
Py_XDECREF(self->dict);
PyObject_Del(self);
}
static PyObject *dirs_iter(dirsObject *self)
{
return PyObject_GetIter(self->dict);
}
static PySequenceMethods dirs_sequence_methods;
static PyMethodDef dirs_methods[] = {
{"addpath", (PyCFunction)dirs_addpath, METH_VARARGS, "add a path"},
{"delpath", (PyCFunction)dirs_delpath, METH_VARARGS, "remove a path"},
{NULL} /* Sentinel */
};
static PyTypeObject dirsType = { PyObject_HEAD_INIT(NULL) };
void dirs_module_init(PyObject *mod)
{
dirs_sequence_methods.sq_contains = (objobjproc)dirs_contains;
dirsType.tp_name = "parsers.dirs";
dirsType.tp_new = PyType_GenericNew;
dirsType.tp_basicsize = sizeof(dirsObject);
dirsType.tp_dealloc = (destructor)dirs_dealloc;
dirsType.tp_as_sequence = &dirs_sequence_methods;
dirsType.tp_flags = Py_TPFLAGS_DEFAULT;
dirsType.tp_doc = "dirs";
dirsType.tp_iter = (getiterfunc)dirs_iter;
dirsType.tp_methods = dirs_methods;
dirsType.tp_init = (initproc)dirs_init;
if (PyType_Ready(&dirsType) < 0)
return;
Py_INCREF(&dirsType);
PyModule_AddObject(mod, "dirs", (PyObject *)&dirsType);
}