[PATCH 2 of 2] Issue919: add a standard extension to recreate hardlinks between repositories

Benoit Boissinot benoit.boissinot at ens-lyon.org
Thu Nov 5 16:22:46 CST 2009


On Thu, Nov 05, 2009 at 09:48:22PM -0000, Jesse Glick wrote:
> # HG changeset patch
> # User Jesse Glick <jesse.glick at sun.com>
> # Date 1257457250 18000
> # Node ID 7327f8bdbf136b4a85767f260a63c3154e68d48e
> # Parent  1308731835ae484a708ec42bae6ebc692970d0f0
> Issue919: add a standard extension to recreate hardlinks between repositories.
> Having to run a standalone Python script from the contrib dir is a nuisance.
> Also makes various improvements to locking, file discovery, etc.
> Should also update: http://www.selenic.com/mercurial/wiki/index.cgi/RecreateHardlinksBetweenRepositories
> 
> diff --git a/contrib/hg-relink b/hgext/relink.py
> rename from contrib/hg-relink
> rename to hgext/relink.py
> --- a/contrib/hg-relink
> +++ b/hgext/relink.py
> @@ -1,52 +1,72 @@
> -#!/usr/bin/env python
> +# Mercurial extension to provide 'hg relink' command
>  #
>  # Copyright (C) 2007 Brendan Cully <brendan at kublai.com>
>  #
>  # This software may be used and distributed according to the terms of the
>  # GNU General Public License version 2, incorporated herein by reference.
>  
> -import os, sys
> +"""recreates hardlinks between repository clones"""
>  
> -class ConfigError(Exception): pass
> +from mercurial import cmdutil, hg, util
> +from mercurial.i18n import _
> +import os, stat
>  
> -def usage():
> -    print """relink <source> <destination>
> -    Recreate hard links between source and destination repositories"""
> +def relink(ui, repo, origin=None, **opts):
> +    """recreate hardlinks between two repositories
>  
> -class Config:
> -    def __init__(self, args):
> -        if len(args) != 3:
> -            raise ConfigError("wrong number of arguments")
> -        self.src = os.path.abspath(args[1])
> -        self.dst = os.path.abspath(args[2])
> -        for d in (self.src, self.dst):
> -            if not os.path.exists(os.path.join(d, '.hg')):
> -                raise ConfigError("%s: not a mercurial repository" % d)
> +    When repositories are cloned locally, their data files will be hardlinked
> +    so that they only use the space of a single repository.
>  
> -def collect(src):

(unrelated)
this diff is ugly, GNU diff is much better :/
it shouldn't resync here ideally

[snip]
> +    lock1 = repo.lock()
> +    try:
> +        src = hg.repository(
> +            cmdutil.remoteui(repo, opts),
> +            ui.expandpath(origin or 'default-relink', origin or 'default'))
> +        if not src.local():
> +            raise util.Abort('must specify local origin repository')
> +        ui.status(_('Relinking %s to %s\n') % (src.store.path, repo.store.path))

lowercase for status messages, please.

> +        lock2 = src.lock()
> +        try:
> +            candidates = collect(src.store.path, ui)
> +            targets = prune(candidates, repo.store.path, ui)
> +            do_relink(src.store.path, repo.store.path, targets, ui)
> +        finally:
> +            lock2.release()
> +    finally:
> +        lock1.release()

You could take both locks at the same time, no need to take repo.lock()
so early.

And then if you from mercurial import lock, just do

lock.release(locallock, remotelock)

(and remotelock is a better name for lock2)

> +
> +def collect(src, ui):
>      seplen = len(os.path.sep)
>      candidates = []
>      for dirpath, dirnames, filenames in os.walk(src):
>          relpath = dirpath[len(src) + seplen:]
>          for filename in filenames:
> -            if not filename.endswith('.i'):
> -                continue
> -            st = os.stat(os.path.join(dirpath, filename))
> -            candidates.append((os.path.join(relpath, filename), st))
> +            if filename[-2:] in ('.d', '.i'):
> +                st = os.stat(os.path.join(dirpath, filename))
> +                if stat.S_ISREG(st.st_mode):
> +                    candidates.append((os.path.join(relpath, filename), st))

here using the store stuff, you would be able to relink repo with a
different store encoding (don't know if it matters, you can leave it out
for the time being if you prefer).
>  
> +    ui.status(_('Collected %d candidate storage files\n') % len(candidates))

lowercase

> @@ -57,9 +77,9 @@
>              return False
>          if st.st_dev != ts.st_dev:
>              # No point in continuing
> -            raise Exception('Source and destination are on different devices')
> +            raise util.Abort(
> +                _('Source and destination are on different devices'))

lowercase

>  
> +    ui.status(_('Pruned down to %d probably relinkable files\n') % len(targets))

ditto

> -    print 'Relinked %d files (%d bytes reclaimed)' % (relinked, savedbytes)
> +    ui.status(_('Relinked %d files (%d bytes reclaimed)\n') %
> +              (relinked, savedbytes))
>  
> +cmdtable = {
> +    'relink': (
> +        relink,
> +        [],
> +        _('[ORIGIN]')
> +    )
> +}

better help text if possible.

> diff --git a/mercurial/hg.py b/mercurial/hg.py
> --- a/mercurial/hg.py
> +++ b/mercurial/hg.py
> @@ -225,6 +225,9 @@
>          if src_repo.cancopy() and islocal(dest):
>              abspath = os.path.abspath(util.drop_scheme('file', origsource))
>              copy = not pull and not rev
> +            if rev and not pull:
> +                ui.warn(_("--rev implies --pull; "
> +                          "use relink extension to reclaim hardlinks\n"))

I'd leave that out, I'm unsure about advertizing extensions in core hg
like that and a warning is a bit too verbose in my opinion.

Thanks!

Benoit

-- 
:wq


More information about the Mercurial-devel mailing list