comparison contrib/hg-relink @ 4270:29eb88bd5c8d

hg-relink: do not compare .d files
author Brendan Cully <brendan@kublai.com>
date Fri, 23 Mar 2007 14:11:31 -0700
parents 7663780b55a7
children 46293a0c7e9f
comparison
equal deleted inserted replaced
4264:bda63383d529 4270:29eb88bd5c8d
21 self.dst = os.path.abspath(args[2]) 21 self.dst = os.path.abspath(args[2])
22 for d in (self.src, self.dst): 22 for d in (self.src, self.dst):
23 if not os.path.exists(os.path.join(d, '.hg')): 23 if not os.path.exists(os.path.join(d, '.hg')):
24 raise ConfigError("%s: not a mercurial repository" % d) 24 raise ConfigError("%s: not a mercurial repository" % d)
25 25
26 try:
27 cfg = Config(sys.argv)
28 except ConfigError, inst:
29 print str(inst)
30 usage()
31 sys.exit(1)
32
33 def collect(src): 26 def collect(src):
34 seplen = len(os.path.sep) 27 seplen = len(os.path.sep)
35 candidates = [] 28 candidates = []
36 for dirpath, dirnames, filenames in os.walk(src): 29 for dirpath, dirnames, filenames in os.walk(src):
37 relpath = dirpath[len(src) + seplen:] 30 relpath = dirpath[len(src) + seplen:]
38 for filename in filenames: 31 for filename in filenames:
39 if not (filename.endswith('.i') or filename.endswith('.d')): 32 if not filename.endswith('.i'):
40 continue 33 continue
41 st = os.stat(os.path.join(dirpath, filename)) 34 st = os.stat(os.path.join(dirpath, filename))
42 candidates.append((os.path.join(relpath, filename), st)) 35 candidates.append((os.path.join(relpath, filename), st))
43 36
44 return candidates 37 return candidates
45 38
46 def prune(candidates, dst): 39 def prune(candidates, dst):
40 def getdatafile(path):
41 if not path.endswith('.i'):
42 return None, None
43 df = path[:-1] + 'd'
44 try:
45 st = os.stat(df)
46 except OSError:
47 return None, None
48 return df, st
49
50 def linkfilter(dst, st):
51 try:
52 ts = os.stat(dst)
53 except OSError:
54 # Destination doesn't have this file?
55 return False
56 if st.st_ino == ts.st_ino:
57 return False
58 if st.st_dev != ts.st_dev:
59 # No point in continuing
60 raise Exception('Source and destination are on different devices')
61 if st.st_size != ts.st_size:
62 # TODO: compare revlog heads
63 return False
64 return st
65
47 targets = [] 66 targets = []
48 for fn, st in candidates: 67 for fn, st in candidates:
49 tgt = os.path.join(dst, fn) 68 tgt = os.path.join(dst, fn)
50 try: 69 ts = linkfilter(tgt, st)
51 ts = os.stat(tgt) 70 if not ts:
52 except OSError:
53 # Destination doesn't have this file?
54 continue
55 if st.st_ino == ts.st_ino:
56 continue
57 if st.st_dev != ts.st_dev:
58 raise Exception('Source and destination are on different devices')
59 if st.st_size != ts.st_size:
60 continue 71 continue
61 targets.append((fn, ts.st_size)) 72 targets.append((fn, ts.st_size))
73 df, ts = getdatafile(tgt)
74 if df:
75 targets.append((fn[:-1] + 'd', ts.st_size))
62 76
63 return targets 77 return targets
64 78
65 def relink(src, dst, files): 79 def relink(src, dst, files):
80 def relinkfile(src, dst):
81 bak = dst + '.bak'
82 os.rename(dst, bak)
83 try:
84 os.link(src, dst)
85 except OSError:
86 os.rename(bak, dst)
87 raise
88 os.remove(bak)
89
66 CHUNKLEN = 65536 90 CHUNKLEN = 65536
67 relinked = 0 91 relinked = 0
68 savedbytes = 0 92 savedbytes = 0
69 93
70 for f, sz in files: 94 for f, sz in files:
79 break 103 break
80 sin = sfp.read(CHUNKLEN) 104 sin = sfp.read(CHUNKLEN)
81 if sin: 105 if sin:
82 continue 106 continue
83 try: 107 try:
84 os.rename(tgt, tgt + '.bak') 108 relinkfile(source, tgt)
85 try:
86 os.link(source, tgt)
87 except OSError:
88 os.rename(tgt + '.bak', tgt)
89 raise
90 print 'Relinked %s' % f 109 print 'Relinked %s' % f
91 relinked += 1 110 relinked += 1
92 savedbytes += sz 111 savedbytes += sz
93 os.remove(tgt + '.bak')
94 except OSError, inst: 112 except OSError, inst:
95 print '%s: %s' % (tgt, str(inst)) 113 print '%s: %s' % (tgt, str(inst))
96 114
97 print 'Relinked %d files (%d bytes reclaimed)' % (relinked, savedbytes) 115 print 'Relinked %d files (%d bytes reclaimed)' % (relinked, savedbytes)
116
117 try:
118 cfg = Config(sys.argv)
119 except ConfigError, inst:
120 print str(inst)
121 usage()
122 sys.exit(1)
98 123
99 src = os.path.join(cfg.src, '.hg') 124 src = os.path.join(cfg.src, '.hg')
100 dst = os.path.join(cfg.dst, '.hg') 125 dst = os.path.join(cfg.dst, '.hg')
101 candidates = collect(src) 126 candidates = collect(src)
102 targets = prune(candidates, dst) 127 targets = prune(candidates, dst)