##// END OF EJS Templates
largefiles: update _subdirlfs() comment...
Matt Harbison -
r24007:240343e1 default
parent child Browse files
Show More
@@ -1,371 +1,371 b''
1 1 # Copyright 2009-2010 Gregory P. Ward
2 2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 3 # Copyright 2010-2011 Fog Creek Software
4 4 # Copyright 2010-2011 Unity Technologies
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 '''setup for largefiles repositories: reposetup'''
10 10 import copy
11 11 import os
12 12
13 13 from mercurial import error, manifest, match as match_, util
14 14 from mercurial.i18n import _
15 15 from mercurial import scmutil, localrepo
16 16
17 17 import lfcommands
18 18 import lfutil
19 19
20 20 def reposetup(ui, repo):
21 21 # wire repositories should be given new wireproto functions
22 22 # by "proto.wirereposetup()" via "hg.wirepeersetupfuncs"
23 23 if not repo.local():
24 24 return
25 25
26 26 class lfilesrepo(repo.__class__):
27 27 lfstatus = False
28 28 def status_nolfiles(self, *args, **kwargs):
29 29 return super(lfilesrepo, self).status(*args, **kwargs)
30 30
31 31 # When lfstatus is set, return a context that gives the names
32 32 # of largefiles instead of their corresponding standins and
33 33 # identifies the largefiles as always binary, regardless of
34 34 # their actual contents.
35 35 def __getitem__(self, changeid):
36 36 ctx = super(lfilesrepo, self).__getitem__(changeid)
37 37 if self.lfstatus:
38 38 class lfilesmanifestdict(manifest.manifestdict):
39 39 def __contains__(self, filename):
40 40 orig = super(lfilesmanifestdict, self).__contains__
41 41 return orig(filename) or orig(lfutil.standin(filename))
42 42 class lfilesctx(ctx.__class__):
43 43 def files(self):
44 44 filenames = super(lfilesctx, self).files()
45 45 return [lfutil.splitstandin(f) or f for f in filenames]
46 46 def manifest(self):
47 47 man1 = super(lfilesctx, self).manifest()
48 48 man1.__class__ = lfilesmanifestdict
49 49 return man1
50 50 def filectx(self, path, fileid=None, filelog=None):
51 51 orig = super(lfilesctx, self).filectx
52 52 try:
53 53 if filelog is not None:
54 54 result = orig(path, fileid, filelog)
55 55 else:
56 56 result = orig(path, fileid)
57 57 except error.LookupError:
58 58 # Adding a null character will cause Mercurial to
59 59 # identify this as a binary file.
60 60 if filelog is not None:
61 61 result = orig(lfutil.standin(path), fileid,
62 62 filelog)
63 63 else:
64 64 result = orig(lfutil.standin(path), fileid)
65 65 olddata = result.data
66 66 result.data = lambda: olddata() + '\0'
67 67 return result
68 68 ctx.__class__ = lfilesctx
69 69 return ctx
70 70
71 71 # Figure out the status of big files and insert them into the
72 72 # appropriate list in the result. Also removes standin files
73 73 # from the listing. Revert to the original status if
74 74 # self.lfstatus is False.
75 75 # XXX large file status is buggy when used on repo proxy.
76 76 # XXX this needs to be investigated.
77 77 @localrepo.unfilteredmethod
78 78 def status(self, node1='.', node2=None, match=None, ignored=False,
79 79 clean=False, unknown=False, listsubrepos=False):
80 80 listignored, listclean, listunknown = ignored, clean, unknown
81 81 orig = super(lfilesrepo, self).status
82 82 if not self.lfstatus:
83 83 return orig(node1, node2, match, listignored, listclean,
84 84 listunknown, listsubrepos)
85 85
86 86 # some calls in this function rely on the old version of status
87 87 self.lfstatus = False
88 88 ctx1 = self[node1]
89 89 ctx2 = self[node2]
90 90 working = ctx2.rev() is None
91 91 parentworking = working and ctx1 == self['.']
92 92
93 93 if match is None:
94 94 match = match_.always(self.root, self.getcwd())
95 95
96 96 wlock = None
97 97 try:
98 98 try:
99 99 # updating the dirstate is optional
100 100 # so we don't wait on the lock
101 101 wlock = self.wlock(False)
102 102 except error.LockError:
103 103 pass
104 104
105 105 # First check if paths or patterns were specified on the
106 106 # command line. If there were, and they don't match any
107 107 # largefiles, we should just bail here and let super
108 108 # handle it -- thus gaining a big performance boost.
109 109 lfdirstate = lfutil.openlfdirstate(ui, self)
110 110 if not match.always():
111 111 for f in lfdirstate:
112 112 if match(f):
113 113 break
114 114 else:
115 115 return orig(node1, node2, match, listignored, listclean,
116 116 listunknown, listsubrepos)
117 117
118 118 # Create a copy of match that matches standins instead
119 119 # of largefiles.
120 120 def tostandins(files):
121 121 if not working:
122 122 return files
123 123 newfiles = []
124 124 dirstate = self.dirstate
125 125 for f in files:
126 126 sf = lfutil.standin(f)
127 127 if sf in dirstate:
128 128 newfiles.append(sf)
129 129 elif sf in dirstate.dirs():
130 130 # Directory entries could be regular or
131 131 # standin, check both
132 132 newfiles.extend((f, sf))
133 133 else:
134 134 newfiles.append(f)
135 135 return newfiles
136 136
137 137 m = copy.copy(match)
138 138 m._files = tostandins(m._files)
139 139
140 140 result = orig(node1, node2, m, ignored, clean, unknown,
141 141 listsubrepos)
142 142 if working:
143 143
144 144 def sfindirstate(f):
145 145 sf = lfutil.standin(f)
146 146 dirstate = self.dirstate
147 147 return sf in dirstate or sf in dirstate.dirs()
148 148
149 149 match._files = [f for f in match._files
150 150 if sfindirstate(f)]
151 151 # Don't waste time getting the ignored and unknown
152 152 # files from lfdirstate
153 153 unsure, s = lfdirstate.status(match, [], False, listclean,
154 154 False)
155 155 (modified, added, removed, clean) = (s.modified, s.added,
156 156 s.removed, s.clean)
157 157 if parentworking:
158 158 for lfile in unsure:
159 159 standin = lfutil.standin(lfile)
160 160 if standin not in ctx1:
161 161 # from second parent
162 162 modified.append(lfile)
163 163 elif ctx1[standin].data().strip() \
164 164 != lfutil.hashfile(self.wjoin(lfile)):
165 165 modified.append(lfile)
166 166 else:
167 167 if listclean:
168 168 clean.append(lfile)
169 169 lfdirstate.normal(lfile)
170 170 else:
171 171 tocheck = unsure + modified + added + clean
172 172 modified, added, clean = [], [], []
173 173 checkexec = self.dirstate._checkexec
174 174
175 175 for lfile in tocheck:
176 176 standin = lfutil.standin(lfile)
177 177 if standin in ctx1:
178 178 abslfile = self.wjoin(lfile)
179 179 if ((ctx1[standin].data().strip() !=
180 180 lfutil.hashfile(abslfile)) or
181 181 (checkexec and
182 182 ('x' in ctx1.flags(standin)) !=
183 183 bool(lfutil.getexecutable(abslfile)))):
184 184 modified.append(lfile)
185 185 elif listclean:
186 186 clean.append(lfile)
187 187 else:
188 188 added.append(lfile)
189 189
190 190 # at this point, 'removed' contains largefiles
191 191 # marked as 'R' in the working context.
192 192 # then, largefiles not managed also in the target
193 193 # context should be excluded from 'removed'.
194 194 removed = [lfile for lfile in removed
195 195 if lfutil.standin(lfile) in ctx1]
196 196
197 197 # Standins no longer found in lfdirstate has been
198 198 # removed
199 199 for standin in ctx1.walk(lfutil.getstandinmatcher(self)):
200 200 lfile = lfutil.splitstandin(standin)
201 201 if not match(lfile):
202 202 continue
203 203 if lfile not in lfdirstate:
204 204 removed.append(lfile)
205 205
206 206 # Filter result lists
207 207 result = list(result)
208 208
209 209 # Largefiles are not really removed when they're
210 210 # still in the normal dirstate. Likewise, normal
211 211 # files are not really removed if they are still in
212 212 # lfdirstate. This happens in merges where files
213 213 # change type.
214 214 removed = [f for f in removed
215 215 if f not in self.dirstate]
216 216 result[2] = [f for f in result[2]
217 217 if f not in lfdirstate]
218 218
219 219 lfiles = set(lfdirstate._map)
220 220 # Unknown files
221 221 result[4] = set(result[4]).difference(lfiles)
222 222 # Ignored files
223 223 result[5] = set(result[5]).difference(lfiles)
224 224 # combine normal files and largefiles
225 225 normals = [[fn for fn in filelist
226 226 if not lfutil.isstandin(fn)]
227 227 for filelist in result]
228 228 lfstatus = (modified, added, removed, s.deleted, [], [],
229 229 clean)
230 230 result = [sorted(list1 + list2)
231 231 for (list1, list2) in zip(normals, lfstatus)]
232 232 else: # not against working directory
233 233 result = [[lfutil.splitstandin(f) or f for f in items]
234 234 for items in result]
235 235
236 236 if wlock:
237 237 lfdirstate.write()
238 238
239 239 finally:
240 240 if wlock:
241 241 wlock.release()
242 242
243 243 self.lfstatus = True
244 244 return scmutil.status(*result)
245 245
246 246 def commitctx(self, ctx, *args, **kwargs):
247 247 node = super(lfilesrepo, self).commitctx(ctx, *args, **kwargs)
248 248 class lfilesctx(ctx.__class__):
249 249 def markcommitted(self, node):
250 250 orig = super(lfilesctx, self).markcommitted
251 251 return lfutil.markcommitted(orig, self, node)
252 252 ctx.__class__ = lfilesctx
253 253 return node
254 254
255 255 # Before commit, largefile standins have not had their
256 256 # contents updated to reflect the hash of their largefile.
257 257 # Do that here.
258 258 def commit(self, text="", user=None, date=None, match=None,
259 259 force=False, editor=False, extra={}):
260 260 orig = super(lfilesrepo, self).commit
261 261
262 262 wlock = self.wlock()
263 263 try:
264 264 lfcommithook = self._lfcommithooks[-1]
265 265 match = lfcommithook(self, match)
266 266 result = orig(text=text, user=user, date=date, match=match,
267 267 force=force, editor=editor, extra=extra)
268 268 return result
269 269 finally:
270 270 wlock.release()
271 271
272 272 def push(self, remote, force=False, revs=None, newbranch=False):
273 273 if remote.local():
274 274 missing = set(self.requirements) - remote.local().supported
275 275 if missing:
276 276 msg = _("required features are not"
277 277 " supported in the destination:"
278 278 " %s") % (', '.join(sorted(missing)))
279 279 raise util.Abort(msg)
280 280 return super(lfilesrepo, self).push(remote, force=force, revs=revs,
281 281 newbranch=newbranch)
282 282
283 283 # TODO: _subdirlfs should be moved into "lfutil.py", because
284 284 # it is referred only from "lfutil.updatestandinsbymatch"
285 285 def _subdirlfs(self, files, lfiles):
286 286 '''
287 287 Adjust matched file list
288 288 If we pass a directory to commit whose only committable files
289 289 are largefiles, the core commit code aborts before finding
290 290 the largefiles.
291 291 So we do the following:
292 292 For directories that only have largefiles as matches,
293 293 we explicitly add the largefiles to the match list and remove
294 294 the directory.
295 295 In other cases, we leave the match list unmodified.
296 296 '''
297 297 actualfiles = []
298 298 dirs = []
299 299 regulars = []
300 300
301 301 for f in files:
302 302 if lfutil.isstandin(f + '/'):
303 303 raise util.Abort(
304 304 _('file "%s" is a largefile standin') % f,
305 305 hint=('commit the largefile itself instead'))
306 306 # Scan directories
307 307 if os.path.isdir(self.wjoin(f)):
308 308 dirs.append(f)
309 309 else:
310 310 regulars.append(f)
311 311
312 312 for f in dirs:
313 313 matcheddir = False
314 314 d = self.dirstate.normalize(f) + '/'
315 315 # Check for matched normal files
316 316 for mf in regulars:
317 317 if self.dirstate.normalize(mf).startswith(d):
318 318 actualfiles.append(f)
319 319 matcheddir = True
320 320 break
321 321 if not matcheddir:
322 322 # If no normal match, manually append
323 323 # any matching largefiles
324 324 for lf in lfiles:
325 325 if self.dirstate.normalize(lf).startswith(d):
326 326 actualfiles.append(lf)
327 327 if not matcheddir:
328 328 # There may still be normal files in the dir, so
329 # make sure a directory is in the list, which
330 # forces status to walk and call the match
331 # function on the matcher. Windows does NOT
332 # require this.
329 # make sure _a_ directory is in the list, which
330 # forces status/dirstate to walk all files and
331 # call the match function on the matcher, even
332 # on case sensitive filesytems.
333 333 actualfiles.append('.')
334 334 matcheddir = True
335 335 # Nothing in dir, so readd it
336 336 # and let commit reject it
337 337 if not matcheddir:
338 338 actualfiles.append(f)
339 339
340 340 # Always add normal files
341 341 actualfiles += regulars
342 342 return actualfiles
343 343
344 344 repo.__class__ = lfilesrepo
345 345
346 346 # stack of hooks being executed before committing.
347 347 # only last element ("_lfcommithooks[-1]") is used for each committing.
348 348 repo._lfcommithooks = [lfutil.updatestandinsbymatch]
349 349
350 350 # Stack of status writer functions taking "*msg, **opts" arguments
351 351 # like "ui.status()". Only last element ("_lfstatuswriters[-1]")
352 352 # is used to write status out.
353 353 repo._lfstatuswriters = [ui.status]
354 354
355 355 def prepushoutgoinghook(local, remote, outgoing):
356 356 if outgoing.missing:
357 357 toupload = set()
358 358 addfunc = lambda fn, lfhash: toupload.add(lfhash)
359 359 lfutil.getlfilestoupload(local, outgoing.missing, addfunc)
360 360 lfcommands.uploadlfiles(ui, local, remote, toupload)
361 361 repo.prepushoutgoinghooks.add("largefiles", prepushoutgoinghook)
362 362
363 363 def checkrequireslfiles(ui, repo, **kwargs):
364 364 if 'largefiles' not in repo.requirements and util.any(
365 365 lfutil.shortname+'/' in f[0] for f in repo.store.datafiles()):
366 366 repo.requirements.add('largefiles')
367 367 repo._writerequirements()
368 368
369 369 ui.setconfig('hooks', 'changegroup.lfiles', checkrequireslfiles,
370 370 'largefiles')
371 371 ui.setconfig('hooks', 'commit.lfiles', checkrequireslfiles, 'largefiles')
General Comments 0
You need to be logged in to leave comments. Login now