diff --git a/mercurial/config.py b/mercurial/config.py --- a/mercurial/config.py +++ b/mercurial/config.py @@ -20,13 +20,14 @@ class config(object): def __init__(self, data=None, includepaths=None): self._data = {} - self._source = {} self._unset = [] self._includepaths = includepaths or [] if data: for k in data._data: self._data[k] = data[k].copy() self._source = data._source.copy() + else: + self._source = util.cowdict() def copy(self): return config(self) def __contains__(self, section): @@ -39,13 +40,19 @@ for d in self.sections(): yield d def update(self, src): + self._source = self._source.preparewrite() for s, n in src._unset: - if s in self and n in self._data[s]: + ds = self._data.get(s, None) + if ds is not None and n in ds: + self._data[s] = ds.preparewrite() del self._data[s][n] del self._source[(s, n)] for s in src: - if s not in self: - self._data[s] = util.sortdict() + ds = self._data.get(s, None) + if ds: + self._data[s] = ds.preparewrite() + else: + self._data[s] = util.cowsortdict() self._data[s].update(src._data[s]) self._source.update(src._source) def get(self, section, item, default=None): @@ -74,16 +81,21 @@ assert not isinstance(value, str), ( 'config values may not be unicode strings on Python 3') if section not in self: - self._data[section] = util.sortdict() + self._data[section] = util.cowsortdict() + else: + self._data[section] = self._data[section].preparewrite() self._data[section][item] = value if source: + self._source = self._source.preparewrite() self._source[(section, item)] = source def restore(self, data): """restore data returned by self.backup""" + self._source = self._source.preparewrite() if len(data) == 4: # restore old data section, item, value, source = data + self._data[section] = self._data[section].preparewrite() self._data[section][item] = value self._source[(section, item)] = source else: @@ -149,7 +161,7 @@ if remap: section = remap.get(section, section) if section not in self: - self._data[section] = util.sortdict() + self._data[section] = util.cowsortdict() continue m = itemre.match(l) if m: diff --git a/mercurial/util.py b/mercurial/util.py --- a/mercurial/util.py +++ b/mercurial/util.py @@ -588,6 +588,21 @@ return f +class cow(object): + """helper class to make copy-on-write easier""" + + def preparewrite(self): + """call this before writes, return a new object""" + if getattr(self, '_copied', 0): + self._copied -= 1 + return self.__class__(self) + return self + + def copy(self): + """always do a cheap copy""" + self._copied = getattr(self, '_copied', 0) + 1 + return self + class sortdict(collections.OrderedDict): '''a simple sorted dictionary @@ -613,6 +628,40 @@ for k, v in src: self[k] = v +class cowdict(cow, dict): + """copy-on-write dict + + Be sure to call d = d.preparewrite() before writing to d. + + >>> a = cowdict() + >>> a is a.preparewrite() + True + >>> b = a.copy() + >>> b is a + True + >>> c = b.copy() + >>> c is a + True + >>> a = a.preparewrite() + >>> b is a + False + >>> a is a.preparewrite() + True + >>> c = c.preparewrite() + >>> b is c + False + >>> b is b.preparewrite() + True + """ + pass + +class cowsortdict(cow, sortdict): + """copy-on-write sortdict + + Be sure to call d = d.preparewrite() before writing to d. + """ + pass + class transactional(object): """Base class for making a transactional type into a context manager.""" __metaclass__ = abc.ABCMeta