contrib/hg-relink
author Dirkjan Ochtman <dirkjan@ochtman.nl>
Mon, 20 Oct 2008 10:15:26 +0200
changeset 7180 a42d27bc809d
parent 4270 29eb88bd5c8d
child 8225 46293a0c7e9f
permissions -rwxr-xr-x
hgweb: be sure to drain request data even in early error conditions Thanks to Mads Kiilerich with noticing this. The hg client can only read data after all the sent data has been read, so we have to read all the request data even if we're not going to do anything with it (in error conditions). This is not easy to fix in the client, because we're using Python's httplib, which is strictly stateful. Abstracted the draining into a separate method.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
4249
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
     1
#!/usr/bin/env python
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
     2
#
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
     3
# Copyright (C) 2007 Brendan Cully <brendan@kublai.com>
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
     4
#
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
     5
# This software may be used and distributed according to the terms
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
     6
# of the GNU General Public License, incorporated herein by reference.
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
     7
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
     8
import os, sys
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
     9
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    10
class ConfigError(Exception): pass
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    11
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    12
def usage():
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    13
    print """relink <source> <destination>
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    14
    Recreate hard links between source and destination repositories"""
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    15
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    16
class Config:
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    17
    def __init__(self, args):
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    18
        if len(args) != 3:
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    19
            raise ConfigError("wrong number of arguments")
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    20
        self.src = os.path.abspath(args[1])
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    21
        self.dst = os.path.abspath(args[2])
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    22
        for d in (self.src, self.dst):
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    23
            if not os.path.exists(os.path.join(d, '.hg')):
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    24
                raise ConfigError("%s: not a mercurial repository" % d)
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    25
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    26
def collect(src):
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    27
    seplen = len(os.path.sep)
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    28
    candidates = []
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    29
    for dirpath, dirnames, filenames in os.walk(src):
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    30
        relpath = dirpath[len(src) + seplen:]
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    31
        for filename in filenames:
4270
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    32
            if not filename.endswith('.i'):
4249
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    33
                continue
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    34
            st = os.stat(os.path.join(dirpath, filename))
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    35
            candidates.append((os.path.join(relpath, filename), st))
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    36
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    37
    return candidates
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    38
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    39
def prune(candidates, dst):
4270
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    40
    def getdatafile(path):
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    41
        if not path.endswith('.i'):
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    42
            return None, None
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    43
        df = path[:-1] + 'd'
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    44
        try:
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    45
            st = os.stat(df)
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    46
        except OSError:
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    47
            return None, None
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    48
        return df, st
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    49
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    50
    def linkfilter(dst, st):
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    51
        try:
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    52
            ts = os.stat(dst)
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    53
        except OSError:
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    54
            # Destination doesn't have this file?
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    55
            return False
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    56
        if st.st_ino == ts.st_ino:
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    57
            return False
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    58
        if st.st_dev != ts.st_dev:
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    59
            # No point in continuing
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    60
            raise Exception('Source and destination are on different devices')
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    61
        if st.st_size != ts.st_size:
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    62
            # TODO: compare revlog heads
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    63
            return False
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    64
        return st
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    65
4249
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    66
    targets = []
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    67
    for fn, st in candidates:
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    68
        tgt = os.path.join(dst, fn)
4270
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    69
        ts = linkfilter(tgt, st)
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    70
        if not ts:
4249
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    71
            continue
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    72
        targets.append((fn, ts.st_size))
4270
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    73
        df, ts = getdatafile(tgt)
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    74
        if df:
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    75
            targets.append((fn[:-1] + 'd', ts.st_size))
4249
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    76
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    77
    return targets
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    78
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    79
def relink(src, dst, files):
4270
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    80
    def relinkfile(src, dst):
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    81
        bak = dst + '.bak'
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    82
        os.rename(dst, bak)
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    83
        try:
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    84
            os.link(src, dst)
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    85
        except OSError:
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    86
            os.rename(bak, dst)
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    87
            raise
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    88
        os.remove(bak)
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
    89
4249
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    90
    CHUNKLEN = 65536
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    91
    relinked = 0
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    92
    savedbytes = 0
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    93
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    94
    for f, sz in files:
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    95
        source = os.path.join(src, f)
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    96
        tgt = os.path.join(dst, f)
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    97
        sfp = file(source)
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    98
        dfp = file(tgt)
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
    99
        sin = sfp.read(CHUNKLEN)
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
   100
        while sin:
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
   101
            din = dfp.read(CHUNKLEN)
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
   102
            if sin != din:
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
   103
                break
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
   104
            sin = sfp.read(CHUNKLEN)
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
   105
        if sin:
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
   106
            continue
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
   107
        try:
4270
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
   108
            relinkfile(source, tgt)
4249
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
   109
            print 'Relinked %s' % f
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
   110
            relinked += 1
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
   111
            savedbytes += sz
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
   112
        except OSError, inst:
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
   113
            print '%s: %s' % (tgt, str(inst))
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
   114
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
   115
    print 'Relinked %d files (%d bytes reclaimed)' % (relinked, savedbytes)
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
   116
4270
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
   117
try:
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
   118
    cfg = Config(sys.argv)
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
   119
except ConfigError, inst:
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
   120
    print str(inst)
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
   121
    usage()
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
   122
    sys.exit(1)
29eb88bd5c8d hg-relink: do not compare .d files
Brendan Cully <brendan@kublai.com>
parents: 4249
diff changeset
   123
4249
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
   124
src = os.path.join(cfg.src, '.hg')
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
   125
dst = os.path.join(cfg.dst, '.hg')
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
   126
candidates = collect(src)
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
   127
targets = prune(candidates, dst)
7663780b55a7 Add hg-relink script to contrib
Brendan Cully <brendan@kublai.com>
parents:
diff changeset
   128
relink(src, dst, targets)