# HG changeset patch # User Brendan Cully # Date 1174684291 25200 # Node ID 29eb88bd5c8d06e0b3d45dbcbb9e9fd6566ba671 # Parent bda63383d5291fcfd3560501320318d7970bef3b hg-relink: do not compare .d files diff --git a/contrib/hg-relink b/contrib/hg-relink --- a/contrib/hg-relink +++ b/contrib/hg-relink @@ -23,20 +23,13 @@ class Config: if not os.path.exists(os.path.join(d, '.hg')): raise ConfigError("%s: not a mercurial repository" % d) -try: - cfg = Config(sys.argv) -except ConfigError, inst: - print str(inst) - usage() - sys.exit(1) - def collect(src): 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') or filename.endswith('.d')): + if not filename.endswith('.i'): continue st = os.stat(os.path.join(dirpath, filename)) candidates.append((os.path.join(relpath, filename), st)) @@ -44,25 +37,56 @@ def collect(src): return candidates def prune(candidates, dst): + def getdatafile(path): + if not path.endswith('.i'): + return None, None + df = path[:-1] + 'd' + try: + st = os.stat(df) + except OSError: + return None, None + return df, st + + def linkfilter(dst, st): + try: + ts = os.stat(dst) + except OSError: + # Destination doesn't have this file? + return False + if st.st_ino == ts.st_ino: + return False + if st.st_dev != ts.st_dev: + # No point in continuing + raise Exception('Source and destination are on different devices') + if st.st_size != ts.st_size: + # TODO: compare revlog heads + return False + return st + targets = [] for fn, st in candidates: tgt = os.path.join(dst, fn) - try: - ts = os.stat(tgt) - except OSError: - # Destination doesn't have this file? - continue - if st.st_ino == ts.st_ino: - continue - if st.st_dev != ts.st_dev: - raise Exception('Source and destination are on different devices') - if st.st_size != ts.st_size: + ts = linkfilter(tgt, st) + if not ts: continue targets.append((fn, ts.st_size)) + df, ts = getdatafile(tgt) + if df: + targets.append((fn[:-1] + 'd', ts.st_size)) return targets def relink(src, dst, files): + def relinkfile(src, dst): + bak = dst + '.bak' + os.rename(dst, bak) + try: + os.link(src, dst) + except OSError: + os.rename(bak, dst) + raise + os.remove(bak) + CHUNKLEN = 65536 relinked = 0 savedbytes = 0 @@ -81,21 +105,22 @@ def relink(src, dst, files): if sin: continue try: - os.rename(tgt, tgt + '.bak') - try: - os.link(source, tgt) - except OSError: - os.rename(tgt + '.bak', tgt) - raise + relinkfile(source, tgt) print 'Relinked %s' % f relinked += 1 savedbytes += sz - os.remove(tgt + '.bak') except OSError, inst: print '%s: %s' % (tgt, str(inst)) print 'Relinked %d files (%d bytes reclaimed)' % (relinked, savedbytes) +try: + cfg = Config(sys.argv) +except ConfigError, inst: + print str(inst) + usage() + sys.exit(1) + src = os.path.join(cfg.src, '.hg') dst = os.path.join(cfg.dst, '.hg') candidates = collect(src)