##// END OF EJS Templates
Add support for relinking on Windows....
Siddharth Agarwal -
r10218:750b7a4f stable
parent child Browse files
Show More
@@ -34,6 +34,8 b' def relink(ui, repo, origin=None, **opts'
34 34 Do not attempt any read operations on this repository while the command is
35 35 running. (Both repositories will be locked against writes.)
36 36 """
37 if not hasattr(util, 'samefile') or not hasattr(util, 'samedevice'):
38 raise util.Abort(_('hardlinks are not supported on this system'))
37 39 src = hg.repository(
38 40 cmdutil.remoteui(repo, opts),
39 41 ui.expandpath(origin or 'default-relink', origin or 'default'))
@@ -45,7 +47,7 b' def relink(ui, repo, origin=None, **opts'
45 47 remotelock = src.lock()
46 48 try:
47 49 candidates = collect(src.store.path, ui)
48 targets = prune(candidates, repo.store.path, ui)
50 targets = prune(candidates, src.store.path, repo.store.path, ui)
49 51 do_relink(src.store.path, repo.store.path, targets, ui)
50 52 finally:
51 53 remotelock.release()
@@ -68,16 +70,16 b' def collect(src, ui):'
68 70 ui.status(_('collected %d candidate storage files\n') % len(candidates))
69 71 return candidates
70 72
71 def prune(candidates, dst, ui):
72 def linkfilter(dst, st):
73 def prune(candidates, src, dst, ui):
74 def linkfilter(src, dst, st):
73 75 try:
74 76 ts = os.stat(dst)
75 77 except OSError:
76 78 # Destination doesn't have this file?
77 79 return False
78 if st.st_ino == ts.st_ino:
80 if util.samefile(src, dst):
79 81 return False
80 if st.st_dev != ts.st_dev:
82 if not util.samedevice(src, dst):
81 83 # No point in continuing
82 84 raise util.Abort(
83 85 _('source and destination are on different devices'))
@@ -87,8 +89,9 b' def prune(candidates, dst, ui):'
87 89
88 90 targets = []
89 91 for fn, st in candidates:
92 srcpath = os.path.join(src, fn)
90 93 tgt = os.path.join(dst, fn)
91 ts = linkfilter(tgt, st)
94 ts = linkfilter(srcpath, tgt, st)
92 95 if not ts:
93 96 ui.debug(_('not linkable: %s\n') % fn)
94 97 continue
@@ -102,7 +105,7 b' def do_relink(src, dst, files, ui):'
102 105 bak = dst + '.bak'
103 106 os.rename(dst, bak)
104 107 try:
105 os.link(src, dst)
108 util.os_link(src, dst)
106 109 except OSError:
107 110 os.rename(bak, dst)
108 111 raise
@@ -118,14 +121,17 b' def do_relink(src, dst, files, ui):'
118 121 pos += 1
119 122 source = os.path.join(src, f)
120 123 tgt = os.path.join(dst, f)
121 sfp = file(source)
122 dfp = file(tgt)
124 # Binary mode, so that read() works correctly, especially on Windows
125 sfp = file(source, 'rb')
126 dfp = file(tgt, 'rb')
123 127 sin = sfp.read(CHUNKLEN)
124 128 while sin:
125 129 din = dfp.read(CHUNKLEN)
126 130 if sin != din:
127 131 break
128 132 sin = sfp.read(CHUNKLEN)
133 sfp.close()
134 dfp.close()
129 135 if sin:
130 136 ui.debug(_('not linkable: %s\n') % f)
131 137 continue
@@ -105,6 +105,18 b' def pconvert(path):'
105 105 def localpath(path):
106 106 return path
107 107
108 def samefile(fpath1, fpath2):
109 """Returns whether path1 and path2 refer to the same file. This is only
110 guaranteed to work for files, not directories."""
111 return os.path.samefile(fpath1, fpath2)
112
113 def samedevice(fpath1, fpath2):
114 """Returns whether fpath1 and fpath2 are on the same device. This is only
115 guaranteed to work for files, not directories."""
116 st1 = os.lstat(fpath1)
117 st2 = os.lstat(fpath2)
118 return st1.st_dev == st2.st_dev
119
108 120 if sys.platform == 'darwin':
109 121 def realpath(path):
110 122 '''
@@ -37,7 +37,7 b' def os_link(src, dst):'
37 37 except NotImplementedError: # Another fake error win Win98
38 38 raise OSError(errno.EINVAL, 'Hardlinking not supported')
39 39
40 def nlinks(pathname):
40 def _getfileinfo(pathname):
41 41 """Return number of hardlinks for the given file."""
42 42 try:
43 43 fh = win32file.CreateFile(pathname,
@@ -45,10 +45,39 b' def nlinks(pathname):'
45 45 None, win32file.OPEN_EXISTING, 0, None)
46 46 res = win32file.GetFileInformationByHandle(fh)
47 47 fh.Close()
48 return res[7]
48 return res
49 49 except pywintypes.error:
50 return None
51
52 def nlinks(pathname):
53 """Return number of hardlinks for the given file."""
54 res = _getfileinfo(pathname)
55 if res is not None:
56 return res[7]
57 else:
50 58 return os.lstat(pathname).st_nlink
51 59
60 def samefile(fpath1, fpath2):
61 """Returns whether fpath1 and fpath2 refer to the same file. This is only
62 guaranteed to work for files, not directories."""
63 res1 = _getfileinfo(fpath1)
64 res2 = _getfileinfo(fpath2)
65 if res1 is not None and res2 is not None:
66 # Index 4 is the volume serial number, and 8 and 9 contain the file ID
67 return res1[4] == res2[4] and res1[8] == res2[8] and res1[9] == res2[9]
68 else:
69 return False
70
71 def samedevice(fpath1, fpath2):
72 """Returns whether fpath1 and fpath2 are on the same device. This is only
73 guaranteed to work for files, not directories."""
74 res1 = _getfileinfo(fpath1)
75 res2 = _getfileinfo(fpath2)
76 if res1 is not None and res2 is not None:
77 return res1[4] == res2[4]
78 else:
79 return False
80
52 81 def testpid(pid):
53 82 '''return True if pid is still running or unable to
54 83 determine, False otherwise'''
@@ -12,12 +12,13 b' 2 files updated, 0 files merged, 0 files'
12 12 created new head
13 13 % relink
14 14 relinking .hg/store
15 collected 4 candidate storage files
15 collected 5 candidate storage files
16 16 not linkable: 00changelog.i
17 17 not linkable: 00manifest.i
18 18 not linkable: data/b.i
19 pruned down to 1 probably relinkable files
20 relink: data/a.i 1/1 files (1e+02%)
19 pruned down to 2 probably relinkable files
20 relink: data/a.i 1/2 files ( 50%)
21 not linkable: data/dummy.i
21 22 relinked 1 files (136 bytes reclaimed)
22 23 % check hardlinks
23 24 repo/.hg/store/data/a.i == clone/.hg/store/data/a.i
General Comments 0
You need to be logged in to leave comments. Login now