##// END OF EJS Templates
relink: use absolute_import
timeless -
r28380:ad266834 default
parent child Browse files
Show More
@@ -1,187 +1,195
1 1 # Mercurial extension to provide 'hg relink' command
2 2 #
3 3 # Copyright (C) 2007 Brendan Cully <brendan@kublai.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 """recreates hardlinks between repository clones"""
9 from __future__ import absolute_import
9 10
10 from mercurial import cmdutil, hg, util, error
11 import os
12 import stat
13
14 from mercurial import (
15 cmdutil,
16 error,
17 hg,
18 util,
19 )
11 20 from mercurial.i18n import _
12 import os, stat
13 21
14 22 cmdtable = {}
15 23 command = cmdutil.command(cmdtable)
16 24 # Note for extension authors: ONLY specify testedwith = 'internal' for
17 25 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
18 26 # be specifying the version(s) of Mercurial they are tested with, or
19 27 # leave the attribute unspecified.
20 28 testedwith = 'internal'
21 29
22 30 @command('relink', [], _('[ORIGIN]'))
23 31 def relink(ui, repo, origin=None, **opts):
24 32 """recreate hardlinks between two repositories
25 33
26 34 When repositories are cloned locally, their data files will be
27 35 hardlinked so that they only use the space of a single repository.
28 36
29 37 Unfortunately, subsequent pulls into either repository will break
30 38 hardlinks for any files touched by the new changesets, even if
31 39 both repositories end up pulling the same changes.
32 40
33 41 Similarly, passing --rev to "hg clone" will fail to use any
34 42 hardlinks, falling back to a complete copy of the source
35 43 repository.
36 44
37 45 This command lets you recreate those hardlinks and reclaim that
38 46 wasted space.
39 47
40 48 This repository will be relinked to share space with ORIGIN, which
41 49 must be on the same local disk. If ORIGIN is omitted, looks for
42 50 "default-relink", then "default", in [paths].
43 51
44 52 Do not attempt any read operations on this repository while the
45 53 command is running. (Both repositories will be locked against
46 54 writes.)
47 55 """
48 56 if (not util.safehasattr(util, 'samefile') or
49 57 not util.safehasattr(util, 'samedevice')):
50 58 raise error.Abort(_('hardlinks are not supported on this system'))
51 59 src = hg.repository(repo.baseui, ui.expandpath(origin or 'default-relink',
52 60 origin or 'default'))
53 61 ui.status(_('relinking %s to %s\n') % (src.store.path, repo.store.path))
54 62 if repo.root == src.root:
55 63 ui.status(_('there is nothing to relink\n'))
56 64 return
57 65
58 66 if not util.samedevice(src.store.path, repo.store.path):
59 67 # No point in continuing
60 68 raise error.Abort(_('source and destination are on different devices'))
61 69
62 70 locallock = repo.lock()
63 71 try:
64 72 remotelock = src.lock()
65 73 try:
66 74 candidates = sorted(collect(src, ui))
67 75 targets = prune(candidates, src.store.path, repo.store.path, ui)
68 76 do_relink(src.store.path, repo.store.path, targets, ui)
69 77 finally:
70 78 remotelock.release()
71 79 finally:
72 80 locallock.release()
73 81
74 82 def collect(src, ui):
75 83 seplen = len(os.path.sep)
76 84 candidates = []
77 85 live = len(src['tip'].manifest())
78 86 # Your average repository has some files which were deleted before
79 87 # the tip revision. We account for that by assuming that there are
80 88 # 3 tracked files for every 2 live files as of the tip version of
81 89 # the repository.
82 90 #
83 91 # mozilla-central as of 2010-06-10 had a ratio of just over 7:5.
84 92 total = live * 3 // 2
85 93 src = src.store.path
86 94 pos = 0
87 95 ui.status(_("tip has %d files, estimated total number of files: %d\n")
88 96 % (live, total))
89 97 for dirpath, dirnames, filenames in os.walk(src):
90 98 dirnames.sort()
91 99 relpath = dirpath[len(src) + seplen:]
92 100 for filename in sorted(filenames):
93 101 if filename[-2:] not in ('.d', '.i'):
94 102 continue
95 103 st = os.stat(os.path.join(dirpath, filename))
96 104 if not stat.S_ISREG(st.st_mode):
97 105 continue
98 106 pos += 1
99 107 candidates.append((os.path.join(relpath, filename), st))
100 108 ui.progress(_('collecting'), pos, filename, _('files'), total)
101 109
102 110 ui.progress(_('collecting'), None)
103 111 ui.status(_('collected %d candidate storage files\n') % len(candidates))
104 112 return candidates
105 113
106 114 def prune(candidates, src, dst, ui):
107 115 def linkfilter(src, dst, st):
108 116 try:
109 117 ts = os.stat(dst)
110 118 except OSError:
111 119 # Destination doesn't have this file?
112 120 return False
113 121 if util.samefile(src, dst):
114 122 return False
115 123 if not util.samedevice(src, dst):
116 124 # No point in continuing
117 125 raise error.Abort(
118 126 _('source and destination are on different devices'))
119 127 if st.st_size != ts.st_size:
120 128 return False
121 129 return st
122 130
123 131 targets = []
124 132 total = len(candidates)
125 133 pos = 0
126 134 for fn, st in candidates:
127 135 pos += 1
128 136 srcpath = os.path.join(src, fn)
129 137 tgt = os.path.join(dst, fn)
130 138 ts = linkfilter(srcpath, tgt, st)
131 139 if not ts:
132 140 ui.debug('not linkable: %s\n' % fn)
133 141 continue
134 142 targets.append((fn, ts.st_size))
135 143 ui.progress(_('pruning'), pos, fn, _('files'), total)
136 144
137 145 ui.progress(_('pruning'), None)
138 146 ui.status(_('pruned down to %d probably relinkable files\n') % len(targets))
139 147 return targets
140 148
141 149 def do_relink(src, dst, files, ui):
142 150 def relinkfile(src, dst):
143 151 bak = dst + '.bak'
144 152 os.rename(dst, bak)
145 153 try:
146 154 util.oslink(src, dst)
147 155 except OSError:
148 156 os.rename(bak, dst)
149 157 raise
150 158 os.remove(bak)
151 159
152 160 CHUNKLEN = 65536
153 161 relinked = 0
154 162 savedbytes = 0
155 163
156 164 pos = 0
157 165 total = len(files)
158 166 for f, sz in files:
159 167 pos += 1
160 168 source = os.path.join(src, f)
161 169 tgt = os.path.join(dst, f)
162 170 # Binary mode, so that read() works correctly, especially on Windows
163 171 sfp = file(source, 'rb')
164 172 dfp = file(tgt, 'rb')
165 173 sin = sfp.read(CHUNKLEN)
166 174 while sin:
167 175 din = dfp.read(CHUNKLEN)
168 176 if sin != din:
169 177 break
170 178 sin = sfp.read(CHUNKLEN)
171 179 sfp.close()
172 180 dfp.close()
173 181 if sin:
174 182 ui.debug('not linkable: %s\n' % f)
175 183 continue
176 184 try:
177 185 relinkfile(source, tgt)
178 186 ui.progress(_('relinking'), pos, f, _('files'), total)
179 187 relinked += 1
180 188 savedbytes += sz
181 189 except OSError as inst:
182 190 ui.warn('%s: %s\n' % (tgt, str(inst)))
183 191
184 192 ui.progress(_('relinking'), None)
185 193
186 194 ui.status(_('relinked %d files (%s reclaimed)\n') %
187 195 (relinked, util.bytecount(savedbytes)))
@@ -1,147 +1,146
1 1 #require test-repo
2 2
3 3 $ cd "$TESTDIR"/..
4 4
5 5 $ hg files 'set:(**.py)' | sed 's|\\|/|g' | xargs python contrib/check-py3-compat.py
6 6 contrib/check-code.py not using absolute_import
7 7 contrib/check-code.py requires print_function
8 8 contrib/debugshell.py not using absolute_import
9 9 contrib/hgfixes/fix_bytes.py not using absolute_import
10 10 contrib/hgfixes/fix_bytesmod.py not using absolute_import
11 11 contrib/hgfixes/fix_leftover_imports.py not using absolute_import
12 12 contrib/import-checker.py not using absolute_import
13 13 contrib/import-checker.py requires print_function
14 14 contrib/memory.py not using absolute_import
15 15 contrib/perf.py not using absolute_import
16 16 contrib/python-hook-examples.py not using absolute_import
17 17 contrib/revsetbenchmarks.py not using absolute_import
18 18 contrib/revsetbenchmarks.py requires print_function
19 19 contrib/showstack.py not using absolute_import
20 20 contrib/synthrepo.py not using absolute_import
21 21 contrib/win32/hgwebdir_wsgi.py not using absolute_import
22 22 doc/check-seclevel.py not using absolute_import
23 23 doc/gendoc.py not using absolute_import
24 24 doc/hgmanpage.py not using absolute_import
25 25 hgext/__init__.py not using absolute_import
26 26 hgext/color.py not using absolute_import
27 27 hgext/convert/__init__.py not using absolute_import
28 28 hgext/convert/bzr.py not using absolute_import
29 29 hgext/convert/common.py not using absolute_import
30 30 hgext/convert/convcmd.py not using absolute_import
31 31 hgext/convert/cvs.py not using absolute_import
32 32 hgext/convert/subversion.py not using absolute_import
33 33 hgext/convert/transport.py not using absolute_import
34 34 hgext/eol.py not using absolute_import
35 35 hgext/extdiff.py not using absolute_import
36 36 hgext/factotum.py not using absolute_import
37 37 hgext/fetch.py not using absolute_import
38 38 hgext/gpg.py not using absolute_import
39 39 hgext/graphlog.py not using absolute_import
40 40 hgext/hgcia.py not using absolute_import
41 41 hgext/hgk.py not using absolute_import
42 42 hgext/highlight/__init__.py not using absolute_import
43 43 hgext/highlight/highlight.py not using absolute_import
44 44 hgext/histedit.py not using absolute_import
45 45 hgext/largefiles/__init__.py not using absolute_import
46 46 hgext/largefiles/basestore.py not using absolute_import
47 47 hgext/largefiles/lfcommands.py not using absolute_import
48 48 hgext/largefiles/lfutil.py not using absolute_import
49 49 hgext/largefiles/localstore.py not using absolute_import
50 50 hgext/largefiles/overrides.py not using absolute_import
51 51 hgext/largefiles/proto.py not using absolute_import
52 52 hgext/largefiles/remotestore.py not using absolute_import
53 53 hgext/largefiles/reposetup.py not using absolute_import
54 54 hgext/largefiles/uisetup.py not using absolute_import
55 55 hgext/largefiles/wirestore.py not using absolute_import
56 56 hgext/mq.py not using absolute_import
57 57 hgext/notify.py not using absolute_import
58 58 hgext/patchbomb.py not using absolute_import
59 59 hgext/purge.py not using absolute_import
60 60 hgext/rebase.py not using absolute_import
61 61 hgext/record.py not using absolute_import
62 hgext/relink.py not using absolute_import
63 62 hgext/share.py not using absolute_import
64 63 hgext/transplant.py not using absolute_import
65 64 hgext/win32mbcs.py not using absolute_import
66 65 hgext/win32text.py not using absolute_import
67 66 i18n/check-translation.py not using absolute_import
68 67 i18n/polib.py not using absolute_import
69 68 setup.py not using absolute_import
70 69 tests/filterpyflakes.py requires print_function
71 70 tests/generate-working-copy-states.py requires print_function
72 71 tests/get-with-headers.py requires print_function
73 72 tests/heredoctest.py requires print_function
74 73 tests/hypothesishelpers.py not using absolute_import
75 74 tests/hypothesishelpers.py requires print_function
76 75 tests/killdaemons.py not using absolute_import
77 76 tests/md5sum.py not using absolute_import
78 77 tests/mockblackbox.py not using absolute_import
79 78 tests/printenv.py not using absolute_import
80 79 tests/readlink.py not using absolute_import
81 80 tests/readlink.py requires print_function
82 81 tests/revlog-formatv0.py not using absolute_import
83 82 tests/run-tests.py not using absolute_import
84 83 tests/seq.py not using absolute_import
85 84 tests/seq.py requires print_function
86 85 tests/silenttestrunner.py not using absolute_import
87 86 tests/silenttestrunner.py requires print_function
88 87 tests/sitecustomize.py not using absolute_import
89 88 tests/svn-safe-append.py not using absolute_import
90 89 tests/svnxml.py not using absolute_import
91 90 tests/test-ancestor.py requires print_function
92 91 tests/test-atomictempfile.py not using absolute_import
93 92 tests/test-batching.py not using absolute_import
94 93 tests/test-batching.py requires print_function
95 94 tests/test-bdiff.py not using absolute_import
96 95 tests/test-bdiff.py requires print_function
97 96 tests/test-context.py not using absolute_import
98 97 tests/test-context.py requires print_function
99 98 tests/test-demandimport.py not using absolute_import
100 99 tests/test-demandimport.py requires print_function
101 100 tests/test-dispatch.py not using absolute_import
102 101 tests/test-dispatch.py requires print_function
103 102 tests/test-doctest.py not using absolute_import
104 103 tests/test-duplicateoptions.py not using absolute_import
105 104 tests/test-duplicateoptions.py requires print_function
106 105 tests/test-filecache.py not using absolute_import
107 106 tests/test-filecache.py requires print_function
108 107 tests/test-filelog.py not using absolute_import
109 108 tests/test-filelog.py requires print_function
110 109 tests/test-hg-parseurl.py not using absolute_import
111 110 tests/test-hg-parseurl.py requires print_function
112 111 tests/test-hgweb-auth.py not using absolute_import
113 112 tests/test-hgweb-auth.py requires print_function
114 113 tests/test-hgwebdir-paths.py not using absolute_import
115 114 tests/test-hybridencode.py not using absolute_import
116 115 tests/test-hybridencode.py requires print_function
117 116 tests/test-lrucachedict.py not using absolute_import
118 117 tests/test-lrucachedict.py requires print_function
119 118 tests/test-manifest.py not using absolute_import
120 119 tests/test-minirst.py not using absolute_import
121 120 tests/test-minirst.py requires print_function
122 121 tests/test-parseindex2.py not using absolute_import
123 122 tests/test-parseindex2.py requires print_function
124 123 tests/test-pathencode.py not using absolute_import
125 124 tests/test-pathencode.py requires print_function
126 125 tests/test-propertycache.py not using absolute_import
127 126 tests/test-propertycache.py requires print_function
128 127 tests/test-revlog-ancestry.py not using absolute_import
129 128 tests/test-revlog-ancestry.py requires print_function
130 129 tests/test-run-tests.py not using absolute_import
131 130 tests/test-simplemerge.py not using absolute_import
132 131 tests/test-status-inprocess.py not using absolute_import
133 132 tests/test-status-inprocess.py requires print_function
134 133 tests/test-symlink-os-yes-fs-no.py not using absolute_import
135 134 tests/test-trusted.py not using absolute_import
136 135 tests/test-trusted.py requires print_function
137 136 tests/test-ui-color.py not using absolute_import
138 137 tests/test-ui-color.py requires print_function
139 138 tests/test-ui-config.py not using absolute_import
140 139 tests/test-ui-config.py requires print_function
141 140 tests/test-ui-verbosity.py not using absolute_import
142 141 tests/test-ui-verbosity.py requires print_function
143 142 tests/test-url.py not using absolute_import
144 143 tests/test-url.py requires print_function
145 144 tests/test-walkrepo.py requires print_function
146 145 tests/test-wireproto.py requires print_function
147 146 tests/tinyproxy.py requires print_function
General Comments 0
You need to be logged in to leave comments. Login now