[PATCH 1 of 6 OBSOLETE-MARKERS] obsolete: introduction obsolete markers
Pierre-Yves David
pierre-yves.david at ens-lyon.org
Sat May 12 12:08:49 CDT 2012
# HG changeset patch
# User Pierre-Yves.David at ens-lyon.org
# Date 1336836092 -7200
# Node ID b2cecf48226020c40d448617fc6d421c4e7f6cfe
# Parent ddd4996740c785cc187766249977ea3ece8c17ab
obsolete: introduction obsolete markers
This changeset add a obsstore attribute on localrepository. We are able to Save
to disk and load to it. Format is describe in the module docstring.
There is still a lot a work to do. In particular using standard transaction to
manage the write and aborting.
diff -r ddd4996740c7 -r b2cecf482260 mercurial/localrepo.py
--- a/mercurial/localrepo.py Tue May 08 12:05:45 2012 -0500
+++ b/mercurial/localrepo.py Sat May 12 17:21:32 2012 +0200
@@ -7,7 +7,7 @@
from node import bin, hex, nullid, nullrev, short
from i18n import _
-import repo, changegroup, subrepo, discovery, pushkey
+import repo, changegroup, subrepo, discovery, pushkey, obsolete
import changelog, dirstate, filelog, manifest, context, bookmarks, phases
import lock, transaction, store, encoding
import scmutil, util, extensions, hook, error, revset
@@ -200,6 +200,14 @@
cache[rev] = phase
return cache
+ @storecache('obsmarkers')
+ def obsstore(self):
+ return obsolete.readmarkers(self)
+
+ @property
+ def _dirtyobsstore(self):
+ return 'obsstore' in vars(self) and self.obsstore._new
+
@storecache('00changelog.i')
def changelog(self):
c = changelog.changelog(self.sopener)
@@ -935,6 +943,8 @@
if self._dirtyphases:
phases.writeroots(self)
self._dirtyphases = False
+ if self._dirtyobsstore:
+ obsolete.writemarkers(self)
for k, ce in self._filecache.items():
if k == 'dirstate':
continue
diff -r ddd4996740c7 -r b2cecf482260 mercurial/obsolete.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/obsolete.py Sat May 12 17:21:32 2012 +0200
@@ -0,0 +1,163 @@
+"""Code related to Obsolete marker
+
+
+Obsolete marker content
+------------------------------
+
+A marker hold 4 data:
+* obsoleted changeset (1 mandatory node id)
+* replacements changeset (0..n node id)
+* binary flag field (for various use)
+* various metadata (date, user, maybe more)
+
+Obsolete marker storage format
+------------------------------
+
+The file starts with a version header
+* 'I' size of version String
+* '-s' Version string
+
+Remaining content are about markers. Each marker consist in:
+
+ A fixed width header:
+ * 'B' number of remplacements: "nb-R" (in number of nodes)
+ * 'I' metadata size (in Bytes)
+ * 'B' multipurpose Flag Field
+ * '20s' Obsoleted nodeid
+
+ Then Replacements nodeid:
+ * (20s){nb-R}
+
+ Then comes metadata:
+ * (key\0value\0)+
+
+ Final result looks like::
+
+ ">B>I>B20s(20s)*(-s\0-s\0)*
+"""
+import struct
+_pack = struct.pack
+_unpack = struct.unpack
+
+from mercurial import util
+from i18n import _
+
+
+# data used for parsing and writing
+fmversion = 'Alpha'
+fmfixed = '>BIB20s'
+fmnode = '20s'
+fmfsize = struct.calcsize(fmfixed)
+fnodesize = struct.calcsize(fmnode)
+
+
+def readmarkers(repo):
+ """Read obsolete markers from disk
+
+ (This need to be splitted to allow parsing content from the network)"""
+ store = obsstore()
+ try:
+ f = repo.sopener('obsmarkers', 'rb')
+ try:
+ data = f.read()
+ finally:
+ f.close()
+ ### read format version
+ # XXX need some security around here if the file is really short.
+ # version size
+ off = 0
+ s = struct.calcsize('>Q')
+ cur = data[off:off + s]
+ off += s
+ s, = _unpack('>Q', cur)
+ # version String itself
+ diskversion = data[off:off + s]
+ off += s
+ if diskversion != fmversion:
+ raise util.Abort(_('parsing obsolete marker: unknown version %r')
+ % diskversion)
+ l = len(data)
+ while off + fmfsize <= l:
+ ### read marker
+ # read fixed part
+ cur = data[off:off + fmfsize]
+ off += fmfsize
+ nbrepl, mdsize, flags, obs = _unpack(fmfixed, cur)
+ # read replacement
+ repls = tuple()
+ if nbrepl:
+ s = (fnodesize * nbrepl)
+ cur = data[off:off + s]
+ repls = _unpack(fmnode * nbrepl, cur)
+ off += s
+ # read metadata
+ # (metadata will be decoded on demand)
+ metadata = data[off:off + mdsize]
+ off += mdsize
+ marker = (obs, repls, flags, metadata)
+ store._load(marker)
+ except IOError:
+ pass
+ return store
+
+
+def writemarkers(repo):
+ """Write obsolete markers to disk
+
+ Transaction logic should be used here. But for now rewritting the whole
+ file is good enough.
+
+ (This need to be splitted to allow parsing content from the network)"""
+ f = repo.sopener('obsmarkers', 'wb', atomictemp=True)
+ store = repo.obsstore
+ try:
+ ### write version header
+ vlen = len(fmversion)
+ f.write(_pack('>Q', vlen))
+ f.write(_pack('%is' % vlen, fmversion))
+ for marker in store._all:
+ ### marker
+ # fixed header
+ obs, repls, flags, metadata = marker
+ nbrepl = len(repls)
+ format = fmfixed + (fmnode * nbrepl)
+ data = [nbrepl, len(metadata), flags, obs]
+ data.extend(repls)
+ f.write(_pack(format, *data))
+ f.write(metadata)
+ store._new[:] = []
+ finally:
+ f.close()
+
+
+class obsstore(object):
+ """store all obsolete marker
+
+ marker are accessible through two mappingi
+
+ :store.obsoleted: obsolete -> marker
+ :store.replacements: replacement -> marker
+ """
+
+ def __init__(self):
+ self.obsoleted = {}
+ self.replacements = {}
+ self._all = []
+ self._new = []
+
+ def add(self, marker):
+ """Add a new marker to the store
+
+ This marker still need to be written to disk"""
+ self._new.append(marker)
+ self._load(marker)
+
+ def _load(self, marker):
+ """load a marker in the store
+
+ The marker do need to be written to disk"""
+ self._all.append(marker)
+ obs, rpls = marker[:2]
+ self.obsoleted.setdefault(obs, set()).add(marker)
+ for rp in rpls:
+ self.replacements.setdefault(rp, set()).add(marker)
More information about the Mercurial-devel
mailing list