##// END OF EJS Templates
largefiles: optimize update speed by only updating changed largefiles...
largefiles: optimize update speed by only updating changed largefiles Historically, during 'hg update', every largefile in the working copy was hashed (which is a very expensive operation on big files) and any largefiles that did not have a hash that matched their standin were updated. This patch optimizes 'hg update' by keeping track of what standins have changed between the old and new revisions, and only updating the largefiles that have changed. This saves a lot of time by avoiding the unecessary calculation of a list of sha1 hashes for big files. With this patch, the time 'hg update' takes to complete is a function of how many largefiles need to be updated and what their size is. Performance tests on a repository with about 80 largefiles ranging from a few MB to about 97 MB are shown below. The tests show how long it takes to run 'hg update' with no changes actually being updated. Mercurial 2.1 release: $ time hg update 0 files updated, 0 files merged, 0 files removed, 0 files unresolved getting changed largefiles 0 largefiles updated, 0 removed real 0m10.045s user 0m9.367s sys 0m0.674s With this patch: $ time hg update 0 files updated, 0 files merged, 0 files removed, 0 files unresolved real 0m0.965s user 0m0.845s sys 0m0.115s The same repsoitory, without the largefiles extension enabled: $ time hg update 0 files updated, 0 files merged, 0 files removed, 0 files unresolved real 0m0.799s user 0m0.684s sys 0m0.111s So before the patch, 'hg update' with no changes was approximately 9.25s slower with largefiles enabled. With this patch, it is approximately 0.165s slower.

File last commit:

r14104:23fc62e0 stable
r16120:47ee41fc default
Show More
__init__.py
185 lines | 6.1 KiB | text/x-python | PythonLexer
# zeroconf.py - zeroconf support for Mercurial
#
# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
'''discover and advertise repositories on the local network
Zeroconf-enabled repositories will be announced in a network without
the need to configure a server or a service. They can be discovered
without knowing their actual IP address.
To allow other people to discover your repository using run
:hg:`serve` in your repository::
$ cd test
$ hg serve
You can discover Zeroconf-enabled repositories by running
:hg:`paths`::
$ hg paths
zc-test = http://example.com:8000/test
'''
import socket, time, os
import Zeroconf
from mercurial import ui, hg, encoding, util, dispatch
from mercurial import extensions
from mercurial.hgweb import hgweb_mod
from mercurial.hgweb import hgwebdir_mod
# publish
server = None
localip = None
def getip():
# finds external-facing interface without sending any packets (Linux)
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('1.0.0.1', 0))
ip = s.getsockname()[0]
return ip
except:
pass
# Generic method, sometimes gives useless results
try:
dumbip = socket.gethostbyaddr(socket.gethostname())[2][0]
if not dumbip.startswith('127.') and ':' not in dumbip:
return dumbip
except (socket.gaierror, socket.herror):
dumbip = '127.0.0.1'
# works elsewhere, but actually sends a packet
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('1.0.0.1', 1))
ip = s.getsockname()[0]
return ip
except:
pass
return dumbip
def publish(name, desc, path, port):
global server, localip
if not server:
ip = getip()
if ip.startswith('127.'):
# if we have no internet connection, this can happen.
return
localip = socket.inet_aton(ip)
server = Zeroconf.Zeroconf(ip)
hostname = socket.gethostname().split('.')[0]
host = hostname + ".local"
name = "%s-%s" % (hostname, name)
# advertise to browsers
svc = Zeroconf.ServiceInfo('_http._tcp.local.',
name + '._http._tcp.local.',
server = host,
port = port,
properties = {'description': desc,
'path': "/" + path},
address = localip, weight = 0, priority = 0)
server.registerService(svc)
# advertise to Mercurial clients
svc = Zeroconf.ServiceInfo('_hg._tcp.local.',
name + '._hg._tcp.local.',
server = host,
port = port,
properties = {'description': desc,
'path': "/" + path},
address = localip, weight = 0, priority = 0)
server.registerService(svc)
class hgwebzc(hgweb_mod.hgweb):
def __init__(self, repo, name=None, baseui=None):
super(hgwebzc, self).__init__(repo, name=name, baseui=baseui)
name = self.reponame or os.path.basename(self.repo.root)
path = self.repo.ui.config("web", "prefix", "").strip('/')
desc = self.repo.ui.config("web", "description", name)
publish(name, desc, path,
util.getport(self.repo.ui.config("web", "port", 8000)))
class hgwebdirzc(hgwebdir_mod.hgwebdir):
def __init__(self, conf, baseui=None):
super(hgwebdirzc, self).__init__(conf, baseui=baseui)
prefix = self.ui.config("web", "prefix", "").strip('/') + '/'
for repo, path in self.repos:
u = self.ui.copy()
u.readconfig(os.path.join(path, '.hg', 'hgrc'))
name = os.path.basename(repo)
path = (prefix + repo).strip('/')
desc = u.config('web', 'description', name)
publish(name, desc, path, util.getport(u.config("web", "port", 8000)))
# listen
class listener(object):
def __init__(self):
self.found = {}
def removeService(self, server, type, name):
if repr(name) in self.found:
del self.found[repr(name)]
def addService(self, server, type, name):
self.found[repr(name)] = server.getServiceInfo(type, name)
def getzcpaths():
ip = getip()
if ip.startswith('127.'):
return
server = Zeroconf.Zeroconf(ip)
l = listener()
Zeroconf.ServiceBrowser(server, "_hg._tcp.local.", l)
time.sleep(1)
server.close()
for value in l.found.values():
name = value.name[:value.name.index('.')]
url = "http://%s:%s%s" % (socket.inet_ntoa(value.address), value.port,
value.properties.get("path", "/"))
yield "zc-" + name, url
def config(orig, self, section, key, default=None, untrusted=False):
if section == "paths" and key.startswith("zc-"):
for name, path in getzcpaths():
if name == key:
return path
return orig(self, section, key, default, untrusted)
def configitems(orig, self, section, untrusted=False):
repos = orig(self, section, untrusted)
if section == "paths":
repos += getzcpaths()
return repos
def defaultdest(orig, source):
for name, path in getzcpaths():
if path == source:
return name.encode(encoding.encoding)
return orig(source)
def cleanupafterdispatch(orig, ui, options, cmd, cmdfunc):
try:
return orig(ui, options, cmd, cmdfunc)
finally:
# we need to call close() on the server to notify() the various
# threading Conditions and allow the background threads to exit
global server
if server:
server.close()
extensions.wrapfunction(dispatch, '_runcommand', cleanupafterdispatch)
extensions.wrapfunction(ui.ui, 'config', config)
extensions.wrapfunction(ui.ui, 'configitems', configitems)
extensions.wrapfunction(hg, 'defaultdest', defaultdest)
hgweb_mod.hgweb = hgwebzc
hgwebdir_mod.hgwebdir = hgwebdirzc