diff --git a/kallithea/lib/paster_commands/cleanup.py b/kallithea/lib/paster_commands/cleanup.py
new file mode 100644
--- /dev/null
+++ b/kallithea/lib/paster_commands/cleanup.py
@@ -0,0 +1,155 @@
+# -*- coding: utf-8 -*-
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+"""
+kallithea.lib.paster_commands.cleanup
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+cleanup-repos paster command for Kallithea
+
+
+This file was forked by the Kallithea project in July 2014.
+Original author and date, and relevant copyright and licensing information is below:
+:created_on: Jul 14, 2012
+:author: marcink
+:copyright: (c) 2013 RhodeCode GmbH.
+:license: GPLv3, see LICENSE.md for more details.
+"""
+
+from __future__ import with_statement
+
+import os
+import sys
+import re
+import shutil
+import logging
+import datetime
+
+from kallithea.lib.utils import BasePasterCommand, ask_ok, REMOVED_REPO_PAT
+from kallithea.lib.utils2 import safe_str
+from kallithea.model.db import Ui
+
+# Add location of top level folder to sys.path
+from os.path import dirname as dn
+rc_path = dn(dn(dn(os.path.realpath(__file__))))
+sys.path.append(rc_path)
+
+log = logging.getLogger(__name__)
+
+
+class Command(BasePasterCommand):
+
+ max_args = 1
+ min_args = 1
+
+ usage = "CONFIG_FILE"
+ group_name = "Kallithea"
+ takes_config_file = -1
+ parser = BasePasterCommand.standard_parser(verbose=True)
+ summary = "Cleanup deleted repos"
+
+ def _parse_older_than(self, val):
+ regex = re.compile(r'((?P\d+?)d)?((?P\d+?)h)?((?P\d+?)m)?((?P\d+?)s)?')
+ parts = regex.match(val)
+ if not parts:
+ return
+ parts = parts.groupdict()
+ time_params = {}
+ for (name, param) in parts.iteritems():
+ if param:
+ time_params[name] = int(param)
+ return datetime.timedelta(**time_params)
+
+ def _extract_date(self, name):
+ """
+ Extract the date part from rm__ pattern of removed repos,
+ and convert it to datetime object
+
+ :param name:
+ """
+ date_part = name[4:19] # 4:19 since we don't parse milisecods
+ return datetime.datetime.strptime(date_part, '%Y%m%d_%H%M%S')
+
+ def command(self):
+ #get SqlAlchemy session
+ self._init_session()
+
+ repos_location = Ui.get_repos_location()
+ to_remove = []
+ for dn, dirs, f in os.walk(safe_str(repos_location)):
+ alldirs = list(dirs)
+ del dirs[:]
+ if ('.hg' in alldirs or
+ 'objects' in alldirs and ('refs' in alldirs or 'packed-refs' in f)):
+ continue
+ for loc in alldirs:
+ if REMOVED_REPO_PAT.match(loc):
+ to_remove.append([os.path.join(dn, loc),
+ self._extract_date(loc)])
+ else:
+ dirs.append(loc)
+
+ #filter older than (if present)!
+ now = datetime.datetime.now()
+ older_than = self.options.older_than
+ if older_than:
+ to_remove_filtered = []
+ older_than_date = self._parse_older_than(older_than)
+ for name, date_ in to_remove:
+ repo_age = now - date_
+ if repo_age > older_than_date:
+ to_remove_filtered.append([name, date_])
+
+ to_remove = to_remove_filtered
+ print >> sys.stdout, 'removing %s deleted repos older than %s (%s)' \
+ % (len(to_remove), older_than, older_than_date)
+ else:
+ print >> sys.stdout, 'removing all [%s] deleted repos' \
+ % len(to_remove)
+ if self.options.dont_ask or not to_remove:
+ # don't ask just remove !
+ remove = True
+ else:
+ remove = ask_ok('the following repositories will be deleted completely:\n%s\n'
+ 'are you sure you want to remove them [y/n]?'
+ % ', \n'.join(['%s removed on %s'
+ % (safe_str(x[0]), safe_str(x[1])) for x in to_remove]))
+
+ if remove:
+ for path, date_ in to_remove:
+ print >> sys.stdout, 'removing repository %s' % path
+ shutil.rmtree(path)
+ else:
+ print 'nothing done exiting...'
+ sys.exit(0)
+
+ def update_parser(self):
+ self.parser.add_option(
+ '--older-than',
+ action='store',
+ dest='older_than',
+ help=("only remove repos that have been removed "
+ "at least given time ago. "
+ "The default is to remove all removed repositories. "
+ "Possible suffixes: "
+ "d (days), h (hours), m (minutes), s (seconds). "
+ "For example --older-than=30d deletes repositories "
+ "removed more than 30 days ago.")
+ )
+
+ self.parser.add_option(
+ '--dont-ask',
+ action="store_true",
+ dest="dont_ask",
+ help="remove repositories without asking for confirmation."
+ )
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -172,6 +172,7 @@ setup(
[paste.global_paster_command]
setup-db=kallithea.lib.paster_commands.setup_db:Command
+ cleanup-repos=kallithea.lib.paster_commands.cleanup:Command
update-repoinfo=kallithea.lib.paster_commands.update_repoinfo:Command
make-rcext=kallithea.lib.paster_commands.make_rcextensions:Command
repo-scan=kallithea.lib.paster_commands.repo_scan:Command