treestate: implement gc

Summary:
Implement gc for treestate. Dirstate writes are protected by wlock. So it's
fine to do a gc inside the write method.

Reviewed By: markbt

Differential Revision: D8394736

fbshipit-source-id: 672087a2c14b855d1ca0449eb5b7d92d65bba418
This commit is contained in:
Jun Wu 2018-06-18 10:27:49 -07:00 committed by Facebook Github Bot
parent afc292c75c
commit fd0ca23d39
3 changed files with 34 additions and 2 deletions

View File

@ -733,6 +733,9 @@ def cleanup(ui, repo, debug=None):
% (treeid, ", ".join(treesinuse[treeid]))
)
if "treestate" in repo.requirements:
repo.dirstate._map._gc()
def wrapdirstate(orig, self):
ds = orig(self)

View File

@ -73,6 +73,9 @@ class treestatemap(object):
and an offset.
"""
# Filenames (uuid) that are currently in use. Useful for gc.
fileinuse = set()
def __init__(self, ui, vfs, root, importdirstate=None):
self._filename = None
self._ui = ui
@ -251,8 +254,8 @@ class treestatemap(object):
def setparents(self, p1, p2):
self._parents = (p1, p2)
def _read(self):
"""Read every metadata automatically"""
def _parsedirstate(self, filename):
"""Parse given dirstate metadata file"""
dirstate = self._vfs.tryread("dirstate")
f = util.stringio(dirstate)
p1 = f.read(20) or node.nullid
@ -276,6 +279,12 @@ class treestatemap(object):
rootid = 0
threshold = 0
return p1, p2, filename, rootid, threshold
def _read(self):
"""Read every metadata automatically"""
p1, p2, filename, rootid, threshold = self._parsedirstate("dirstate")
self._parents = (p1, p2)
self._threshold = threshold
self._rootid = rootid
@ -308,10 +317,27 @@ class treestatemap(object):
if filename is None:
filename = "%s" % uuid.uuid4()
assert self._filename != filename
self.fileinuse.add(filename)
self._filename = filename
path = self._vfs.join("treestate", self._filename)
return path
def _gc(self):
"""Remove unreferenced treestate files"""
for name in ["dirstate", "undo.dirstate", "undo.backup.dirstate"]:
try:
_p1, _p2, filename = self._parsedirstate(name)[:3]
self.fileinuse.add(filename)
except Exception:
# dirstate file does not exist, or is in an incompatible
# format.
pass
for name in self._vfs.listdir("treestate"):
if name in self.fileinuse:
continue
self._ui.debug("removing unreferenced treestate/%s\n" % name)
self._vfs.tryunlink("treestate/%s" % name)
def write(self, st, now):
# write .hg/treestate/<uuid>
metadata = {}
@ -340,6 +366,7 @@ class treestatemap(object):
# recalculate threshold
self._threshold = 0
rootid = self._tree.saveas(path)
self._gc()
else:
rootid = self._tree.flush()

View File

@ -56,9 +56,11 @@ Auto repack happens when treestate exceeds size threshold
.
.
creating treestate/00000000-0000-0000-0000-000000000002
removing unreferenced treestate/00000000-0000-0000-0000-000000000000
$ hg debugtreestate
dirstate v2 (using treestate/00000000-0000-0000-0000-000000000002, offset 88, 5 files tracked)
Cleanup removes the leftover files
$ hg debugtreestate cleanup --debug
removing unreferenced treestate/00000000-0000-0000-0000-000000000001